bb5a81a396
* refactor(secrets): detect absent secret via ErrNotFound sentinel
Replace the "secret not found:" string-prefix matching with an exported
secrets.ErrNotFound sentinel, matched via errors.Is. The store(s) now
return fmt.Errorf("%w: %s", ErrNotFound, id); the rendered message is
byte-identical so logs are unchanged.
Consumers migrated off the prefix:
- internal/plugins/config.go isSecretNotFound (behind host GetSecret/
DeleteSecret and service.go vault rollback)
- internal/integrations/secretadapter Exists (jira/linear/sentry inherit
the change through the shared adapter)
Adds a non-Postgres-gated secrets test asserting errors.Is(err,
ErrNotFound) plus message-unchanged coverage; updates the affected test
fakes to wrap the sentinel.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(secrets): address review — eager t.Cleanup, faithful fake Delete
- sqlite_store_test: register sqlxDB close via t.Cleanup immediately
after creation, before the NewMasterKeyProvider/Provide fatal paths
(per the t.Cleanup-immediately convention).
- secretadapter_test: fakeStore.Delete now returns wrapped
secrets.ErrNotFound for an absent id, mirroring the real store and
the fakeSecretRevealer.Delete fake.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
74 lines
2.0 KiB
Go
74 lines
2.0 KiB
Go
package secrets
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
"github.com/kandev/kandev/internal/db"
|
|
)
|
|
|
|
func newTestSQLiteStore(t *testing.T) *sqliteStore {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
|
|
conn, err := db.OpenSQLite(filepath.Join(dir, "secrets.db"))
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
sqlxDB := sqlx.NewDb(conn, "sqlite3")
|
|
t.Cleanup(func() {
|
|
_ = sqlxDB.Close()
|
|
})
|
|
|
|
crypto, err := NewMasterKeyProvider(dir)
|
|
if err != nil {
|
|
t.Fatalf("master key: %v", err)
|
|
}
|
|
|
|
store, cleanup, err := Provide(sqlxDB, sqlxDB, crypto)
|
|
if err != nil {
|
|
t.Fatalf("provide store: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = cleanup()
|
|
})
|
|
return store
|
|
}
|
|
|
|
// TestSQLiteStore_MissingID_IsErrNotFound proves the store signals an absent
|
|
// entry via the exported secrets.ErrNotFound sentinel (matchable with
|
|
// errors.Is), so consumers don't have to string-match the message.
|
|
func TestSQLiteStore_MissingID_IsErrNotFound(t *testing.T) {
|
|
store := newTestSQLiteStore(t)
|
|
ctx := context.Background()
|
|
|
|
if _, err := store.Get(ctx, "does-not-exist"); !errors.Is(err, ErrNotFound) {
|
|
t.Errorf("Get(missing) error = %v, want errors.Is ErrNotFound", err)
|
|
}
|
|
if _, err := store.Reveal(ctx, "does-not-exist"); !errors.Is(err, ErrNotFound) {
|
|
t.Errorf("Reveal(missing) error = %v, want errors.Is ErrNotFound", err)
|
|
}
|
|
if err := store.Delete(ctx, "does-not-exist"); !errors.Is(err, ErrNotFound) {
|
|
t.Errorf("Delete(missing) error = %v, want errors.Is ErrNotFound", err)
|
|
}
|
|
}
|
|
|
|
// TestSQLiteStore_MissingID_MessageUnchanged confirms the human-readable text
|
|
// is still "secret not found: <id>" so existing logs are unchanged; only the
|
|
// detection mechanism (errors.Is) is new.
|
|
func TestSQLiteStore_MissingID_MessageUnchanged(t *testing.T) {
|
|
store := newTestSQLiteStore(t)
|
|
|
|
_, err := store.Get(context.Background(), "abc")
|
|
if err == nil {
|
|
t.Fatal("Get(missing) returned nil error")
|
|
}
|
|
if got, want := err.Error(), "secret not found: abc"; got != want {
|
|
t.Errorf("error message = %q, want %q", got, want)
|
|
}
|
|
}
|