Align docs with archive semantics; fail loud on non-positive expiry (#43)
All checks were successful
check / check (push) Successful in 2m39s
All checks were successful
check / check (push) Successful in 2m39s
- README: rewrite the database-target documentation (target-types
bullet and the per-webhook databases section) to describe the
shipped archiving semantics -- separate archive-{webhookID}.db,
debounced close/reopen for offline archiving, auto-recreate,
creation-validated optional expiry with prune-on-open, and
fail-loud delivery on archive write errors -- replacing the
stale always-successful stub description.
- parseArchiveExpiry now returns an error for set-but-non-positive
durations ("0s", "-5h") instead of silently defaulting to
keep-forever, matching ValidateArchiveExpiry at creation time;
the delivery then fails loudly like any other archive error.
TestParseArchiveExpiry extended with zero and negative cases.
- databaseTarget type comment: "fire-and-forget" -> "no-retry",
matching the fail-loud behaviour.
This commit is contained in:
@@ -10,7 +10,7 @@ import (
|
||||
"sneak.berlin/go/webhooker/internal/database"
|
||||
)
|
||||
|
||||
// databaseTarget is a fire-and-forget target that archives the
|
||||
// databaseTarget is a no-retry target that archives the
|
||||
// full inbound event into a per-webhook archive SQLite file,
|
||||
// separate from the per-webhook event database. The event is
|
||||
// already persisted in the per-webhook event DB by the time
|
||||
|
||||
@@ -77,7 +77,10 @@ type archivedEvent struct {
|
||||
// parseArchiveExpiry reads the optional expiry from a database
|
||||
// target's config JSON. An empty config, an empty expiry, or
|
||||
// the literal "never" all mean keep forever, returned as a zero
|
||||
// duration. Any other value is parsed as a Go duration.
|
||||
// duration. Any other value must parse as a positive Go
|
||||
// duration; a set-but-invalid value (unparseable, zero, or
|
||||
// negative) is an error rather than a silent default, matching
|
||||
// ValidateArchiveExpiry at target creation.
|
||||
func parseArchiveExpiry(
|
||||
configJSON string,
|
||||
) (time.Duration, error) {
|
||||
@@ -106,7 +109,9 @@ func parseArchiveExpiry(
|
||||
}
|
||||
|
||||
if dur <= 0 {
|
||||
return 0, nil
|
||||
return 0, fmt.Errorf(
|
||||
"%w: %q", errArchiveExpiryNotPositive, cfg.Expiry,
|
||||
)
|
||||
}
|
||||
|
||||
return dur, nil
|
||||
|
||||
@@ -250,15 +250,18 @@ func TestParseArchiveExpiry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
in string
|
||||
want time.Duration
|
||||
name string
|
||||
in string
|
||||
want time.Duration
|
||||
wantErr bool
|
||||
}{
|
||||
{"empty config", "", 0},
|
||||
{"explicit never", `{"expiry":"never"}`, 0},
|
||||
{"empty expiry", `{"expiry":""}`, 0},
|
||||
{"duration", `{"expiry":"1h"}`, time.Hour},
|
||||
{"zero duration", `{"expiry":"0s"}`, 0},
|
||||
{"empty config", "", 0, false},
|
||||
{"explicit never", `{"expiry":"never"}`, 0, false},
|
||||
{"empty expiry", `{"expiry":""}`, 0, false},
|
||||
{"duration", `{"expiry":"1h"}`, time.Hour, false},
|
||||
{"unparseable", `{"expiry":"nonsense"}`, 0, true},
|
||||
{"zero duration", `{"expiry":"0s"}`, 0, true},
|
||||
{"negative duration", `{"expiry":"-5h"}`, 0, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -266,15 +269,16 @@ func TestParseArchiveExpiry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := delivery.ExportParseArchiveExpiry(tc.in)
|
||||
if tc.wantErr {
|
||||
require.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
|
||||
_, err := delivery.ExportParseArchiveExpiry(
|
||||
`{"expiry":"nonsense"}`,
|
||||
)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// seedDatabaseTargetDelivery seeds a pending delivery for a
|
||||
|
||||
Reference in New Issue
Block a user