check / check (push) Waiting to run
The main database, each webhook's event database and each archive file get their schema from SQL files under internal/db/migrations/, one directory per kind. Opening a file runs, in one transaction, each file that schema_migrations does not list. GORM stays for queries. AutoMigrate is gone, tests included. An event database holds only its own tables now, so the sweep that removed target rows copied into it is deleted. The association guard stays: without it a delivery write fails on the missing targets table. AutoMigrate also had GORM parse every model before a handle was shared. That is now done on its own: GORM races when two goroutines first use models that refer to each other. Model: opus-5-5
93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/gorm/clause"
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
)
|
|
|
|
// TestDeliveryAttempts_ReadInTheTargetTypesOwnTerms proves, on the
|
|
// event's page and in the event log, that an http or slack attempt
|
|
// shows its status as before, while a database or log attempt, which
|
|
// sends no HTTP request, says what it did and shows no status.
|
|
func TestDeliveryAttempts_ReadInTheTargetTypesOwnTerms(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
targetType database.TargetType
|
|
success bool
|
|
statusCode int
|
|
errText string
|
|
outcome string
|
|
status string // "" when the attempt must show no status
|
|
}{
|
|
{
|
|
database.TargetTypeHTTP, false, 0, "",
|
|
"failure", "Status: — (no response)",
|
|
},
|
|
{
|
|
database.TargetTypeSlack, true, http.StatusOK, "",
|
|
"success", "Status: 200",
|
|
},
|
|
{database.TargetTypeDatabase, true, 0, "", "archived", ""},
|
|
{
|
|
database.TargetTypeDatabase, false, 0,
|
|
"opening archive database: disk full", "failure", "",
|
|
},
|
|
{database.TargetTypeLog, true, 0, "", "written to the log", ""},
|
|
}
|
|
|
|
// Named for type and outcome, so that no two cases share a name:
|
|
// Go adds "#01" to a repeated one, and a "#" in the temporary
|
|
// directory's path cuts the path SQLiteDSN gives SQLite short.
|
|
for _, tc := range cases {
|
|
t.Run(string(tc.targetType)+" "+tc.outcome, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f := newRecentEventsFixture(t)
|
|
target := seedTarget(t, f.db, f.webhook.ID, tc.targetType)
|
|
event := f.event(t, contentTypeJSON, "{}", time.Now())
|
|
dlv := f.delivery(
|
|
t, event, target.ID, database.DeliveryStatusDelivered,
|
|
)
|
|
|
|
require.NoError(t, f.webhookDB.Omit(clause.Associations).Create(
|
|
&database.DeliveryResult{
|
|
DeliveryID: dlv.ID,
|
|
AttemptNum: 1,
|
|
Success: tc.success,
|
|
StatusCode: tc.statusCode,
|
|
Error: tc.errText,
|
|
},
|
|
).Error)
|
|
|
|
w := serveEventPage(t, f.h, f.sess, f.webhook.ID, event.ID)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
pages := []string{
|
|
w.Body.String(),
|
|
renderSourceLogsPage(t, f.h, f.sess, f.webhook.ID),
|
|
}
|
|
|
|
for _, page := range pages {
|
|
assert.Contains(t, page, ">"+tc.outcome+"</span>")
|
|
|
|
if tc.errText != "" {
|
|
assert.Contains(t, page, "Error: "+tc.errText)
|
|
}
|
|
|
|
if tc.status == "" {
|
|
assert.NotContains(t, page, "Status:")
|
|
} else {
|
|
assert.Contains(t, page, tc.status)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|