package storage_test import ( "database/sql" "errors" "testing" "github.com/salvacybersec/keyhunter/pkg/storage" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // seedQueryFindings inserts three findings across two providers with mixed // verified flags. Returns the DB, encryption key, and the saved IDs in // insertion order: [openai-live, openai-dead, anthropic-live]. func seedQueryFindings(t *testing.T) (*storage.DB, []byte, []int64) { t.Helper() db, err := storage.Open(":memory:") require.NoError(t, err) t.Cleanup(func() { db.Close() }) encKey := makeTestKey() seeds := []storage.Finding{ { ProviderName: "openai", KeyValue: "sk-proj-openai-live-1234567890", Confidence: "high", SourcePath: "/tmp/a.env", SourceType: "file", LineNumber: 1, Verified: true, VerifyStatus: "live", VerifyHTTPCode: 200, VerifyMetadata: map[string]string{"org": "acme"}, }, { ProviderName: "openai", KeyValue: "sk-proj-openai-dead-abcdefghij", Confidence: "medium", SourcePath: "/tmp/b.env", SourceType: "file", LineNumber: 2, Verified: false, }, { ProviderName: "anthropic", KeyValue: "sk-ant-api03-livekey-abcdefghij", Confidence: "high", SourcePath: "/tmp/c.yaml", SourceType: "file", LineNumber: 3, Verified: true, VerifyStatus: "live", VerifyHTTPCode: 200, }, } ids := make([]int64, 0, len(seeds)) for _, f := range seeds { id, err := db.SaveFinding(f, encKey) require.NoError(t, err) ids = append(ids, id) } return db, encKey, ids } func TestListFindingsFiltered_ByProvider(t *testing.T) { db, encKey, _ := seedQueryFindings(t) out, err := db.ListFindingsFiltered(encKey, storage.Filters{Provider: "openai"}) require.NoError(t, err) require.Len(t, out, 2) for _, f := range out { assert.Equal(t, "openai", f.ProviderName) } } func TestListFindingsFiltered_Verified(t *testing.T) { db, encKey, _ := seedQueryFindings(t) verifiedTrue := true out, err := db.ListFindingsFiltered(encKey, storage.Filters{Verified: &verifiedTrue}) require.NoError(t, err) require.Len(t, out, 2) for _, f := range out { assert.True(t, f.Verified, "expected only verified findings") } verifiedFalse := false out, err = db.ListFindingsFiltered(encKey, storage.Filters{Verified: &verifiedFalse}) require.NoError(t, err) require.Len(t, out, 1) assert.False(t, out[0].Verified) } func TestListFindingsFiltered_Pagination(t *testing.T) { db, encKey, _ := seedQueryFindings(t) // Unpaginated baseline — should return all 3. all, err := db.ListFindingsFiltered(encKey, storage.Filters{}) require.NoError(t, err) require.Len(t, all, 3) // Limit=1 Offset=1 returns the second row from the baseline order. page, err := db.ListFindingsFiltered(encKey, storage.Filters{Limit: 1, Offset: 1}) require.NoError(t, err) require.Len(t, page, 1) assert.Equal(t, all[1].ID, page[0].ID) } func TestGetFinding_Hit(t *testing.T) { db, encKey, ids := seedQueryFindings(t) f, err := db.GetFinding(ids[0], encKey) require.NoError(t, err) require.NotNil(t, f) assert.Equal(t, "openai", f.ProviderName) assert.Equal(t, "sk-proj-openai-live-1234567890", f.KeyValue) assert.True(t, f.Verified) } func TestGetFinding_Miss(t *testing.T) { db, encKey, _ := seedQueryFindings(t) f, err := db.GetFinding(9999, encKey) assert.Nil(t, f) assert.True(t, errors.Is(err, sql.ErrNoRows), "expected sql.ErrNoRows, got %v", err) } func TestDeleteFinding_Hit(t *testing.T) { db, encKey, ids := seedQueryFindings(t) n, err := db.DeleteFinding(ids[1]) require.NoError(t, err) assert.Equal(t, int64(1), n) f, err := db.GetFinding(ids[1], encKey) assert.Nil(t, f) assert.True(t, errors.Is(err, sql.ErrNoRows)) } func TestDeleteFinding_Miss(t *testing.T) { db, _, _ := seedQueryFindings(t) n, err := db.DeleteFinding(9999) require.NoError(t, err) assert.Equal(t, int64(0), n) } func TestFindingExistsByKey(t *testing.T) { db, encKey, _ := seedQueryFindings(t) // Insert a finding with a deterministic masked key we can query against. masked := "sk-exact...1234" _, err := db.SaveFinding(storage.Finding{ ProviderName: "openai", KeyValue: "sk-exact-key-value-1234", KeyMasked: masked, Confidence: "high", SourcePath: "/tmp/exact.env", SourceType: "import:trufflehog", LineNumber: 42, }, encKey) require.NoError(t, err) // Exact tuple hits. exists, err := db.FindingExistsByKey("openai", masked, "/tmp/exact.env", 42) require.NoError(t, err) assert.True(t, exists, "exact tuple should be found") // Any differing field misses. miss1, err := db.FindingExistsByKey("anthropic", masked, "/tmp/exact.env", 42) require.NoError(t, err) assert.False(t, miss1) miss2, err := db.FindingExistsByKey("openai", "sk-other...9999", "/tmp/exact.env", 42) require.NoError(t, err) assert.False(t, miss2) miss3, err := db.FindingExistsByKey("openai", masked, "/tmp/other.env", 42) require.NoError(t, err) assert.False(t, miss3) miss4, err := db.FindingExistsByKey("openai", masked, "/tmp/exact.env", 7) require.NoError(t, err) assert.False(t, miss4) }