package sources import ( "reflect" "testing" "github.com/salvacybersec/keyhunter/pkg/providers" ) func testRegistry() *providers.Registry { return providers.NewRegistryFromProviders([]providers.Provider{ { Name: "openai", Keywords: []string{"sk-proj-", "sk-"}, }, { Name: "anthropic", Keywords: []string{"sk-ant-", "sk-"}, // shared "sk-" for dedup test }, { Name: "empty-kw", Keywords: []string{}, }, }) } func TestBuildQueries_GitHub(t *testing.T) { got := BuildQueries(testRegistry(), "github") want := []string{ `"sk-" in:file`, `"sk-ant-" in:file`, `"sk-proj-" in:file`, } if !reflect.DeepEqual(got, want) { t.Fatalf("github queries mismatch:\n got=%v\nwant=%v", got, want) } } func TestBuildQueries_Gist(t *testing.T) { got := BuildQueries(testRegistry(), "gist") if len(got) != 3 { t.Fatalf("expected 3 queries, got %d: %v", len(got), got) } if got[0] != `"sk-" in:file` { t.Fatalf("gist should use in:file syntax, got %q", got[0]) } } func TestBuildQueries_GitLab(t *testing.T) { got := BuildQueries(testRegistry(), "gitlab") want := []string{"sk-", "sk-ant-", "sk-proj-"} if !reflect.DeepEqual(got, want) { t.Fatalf("gitlab queries mismatch:\n got=%v\nwant=%v", got, want) } } func TestBuildQueries_HuggingFace(t *testing.T) { got := BuildQueries(testRegistry(), "huggingface") want := []string{"sk-", "sk-ant-", "sk-proj-"} if !reflect.DeepEqual(got, want) { t.Fatalf("huggingface queries mismatch:\n got=%v\nwant=%v", got, want) } } func TestBuildQueries_UnknownSourceDefault(t *testing.T) { got := BuildQueries(testRegistry(), "mystery-source") want := []string{"sk-", "sk-ant-", "sk-proj-"} if !reflect.DeepEqual(got, want) { t.Fatalf("default queries mismatch:\n got=%v\nwant=%v", got, want) } } func TestBuildQueries_NilRegistry(t *testing.T) { if got := BuildQueries(nil, "github"); got != nil { t.Fatalf("expected nil for nil registry, got %v", got) } } func TestBuildQueries_DedupAcrossProviders(t *testing.T) { // "sk-" is shared by openai and anthropic — should appear exactly once. got := BuildQueries(testRegistry(), "gitlab") count := 0 for _, q := range got { if q == "sk-" { count++ } } if count != 1 { t.Fatalf("expected dedup of 'sk-' to 1 occurrence, got %d", count) } } func TestBuildQueries_EmptyKeywordsSkipped(t *testing.T) { reg := providers.NewRegistryFromProviders([]providers.Provider{ {Name: "only-empty", Keywords: []string{"", ""}}, {Name: "real", Keywords: []string{"xyz"}}, }) got := BuildQueries(reg, "gitlab") want := []string{"xyz"} if !reflect.DeepEqual(got, want) { t.Fatalf("empty keywords should be skipped:\n got=%v\nwant=%v", got, want) } } func TestRegisterAll_NilEngineNoPanic(t *testing.T) { defer func() { if r := recover(); r != nil { t.Fatalf("RegisterAll(nil, ...) panicked: %v", r) } }() RegisterAll(nil, SourcesConfig{}) } func TestRegisterAll_EmptyCfgNoPanic(t *testing.T) { defer func() { if r := recover(); r != nil { t.Fatalf("RegisterAll panicked with empty cfg: %v", r) } }() // Use a non-nil engine so the nil-guard early-return doesn't mask issues. // Import cycle prevention: pkg/recon is imported by register.go. eng := newTestEngine() RegisterAll(eng, SourcesConfig{}) }