package cmd import ( "bytes" "os" "path/filepath" "testing" "github.com/salvacybersec/keyhunter/pkg/engine" "github.com/salvacybersec/keyhunter/pkg/importer" "github.com/salvacybersec/keyhunter/pkg/storage" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSelectImporter(t *testing.T) { cases := []struct { format string want string wantErr bool }{ {"trufflehog", "trufflehog", false}, {"gitleaks", "gitleaks", false}, {"gitleaks-csv", "gitleaks-csv", false}, {"bogus", "", true}, {"", "", true}, } for _, tc := range cases { t.Run(tc.format, func(t *testing.T) { imp, err := selectImporter(tc.format) if tc.wantErr { assert.Error(t, err) return } require.NoError(t, err) assert.Equal(t, tc.want, imp.Name()) // Check concrete type matches expectation via interface dispatch. switch tc.format { case "trufflehog": _, ok := imp.(importer.TruffleHogImporter) assert.True(t, ok, "expected TruffleHogImporter") case "gitleaks": _, ok := imp.(importer.GitleaksImporter) assert.True(t, ok, "expected GitleaksImporter") case "gitleaks-csv": _, ok := imp.(importer.GitleaksCSVImporter) assert.True(t, ok, "expected GitleaksCSVImporter") } }) } } func TestEngineToStorage(t *testing.T) { ef := engine.Finding{ ProviderName: "openai", KeyValue: "sk-abcdefghijklmnop", KeyMasked: "sk-abcde...mnop", Confidence: "high", Source: "a.yml", SourceType: "import:trufflehog", LineNumber: 5, Verified: true, VerifyStatus: "live", VerifyHTTPCode: 200, VerifyMetadata: map[string]string{"org": "acme"}, } sf := engineToStorage(ef) assert.Equal(t, "openai", sf.ProviderName) assert.Equal(t, "sk-abcdefghijklmnop", sf.KeyValue) assert.Equal(t, "sk-abcde...mnop", sf.KeyMasked) assert.Equal(t, "high", sf.Confidence) assert.Equal(t, "a.yml", sf.SourcePath, "engine.Source -> storage.SourcePath") assert.Equal(t, "import:trufflehog", sf.SourceType) assert.Equal(t, 5, sf.LineNumber) assert.True(t, sf.Verified) assert.Equal(t, "live", sf.VerifyStatus) assert.Equal(t, 200, sf.VerifyHTTPCode) assert.Equal(t, map[string]string{"org": "acme"}, sf.VerifyMetadata) } // seedImportDB spins up a temp SQLite database wired through viper + // KEYHUNTER_PASSPHRASE so runImport -> openDBWithKey resolves to it. func seedImportDB(t *testing.T) string { t.Helper() dir := t.TempDir() dbPath := filepath.Join(dir, "import.db") viper.Reset() viper.Set("database.path", dbPath) t.Setenv("KEYHUNTER_PASSPHRASE", "test-pass") t.Cleanup(func() { viper.Reset() importFormat = "" }) return dbPath } func TestRunImport_TruffleHogEndToEnd(t *testing.T) { dbPath := seedImportDB(t) // Use the canonical testdata shipped by pkg/importer. importFormat = "trufflehog" samplePath, err := filepath.Abs(filepath.Join("..", "pkg", "importer", "testdata", "trufflehog-sample.json")) require.NoError(t, err) _, err = os.Stat(samplePath) require.NoError(t, err, "testdata trufflehog-sample.json must exist") var out bytes.Buffer importCmd.SetOut(&out) importCmd.SetErr(&out) // First import: all findings are new. err = runImport(importCmd, []string{samplePath}) require.NoError(t, err) first := out.String() assert.Contains(t, first, "Imported 3 findings") assert.Contains(t, first, "3 new") assert.Contains(t, first, "0 duplicates") // Confirm findings landed in the database. db, err := storage.Open(dbPath) require.NoError(t, err) encKey, err := loadOrCreateEncKey(db, "test-pass") require.NoError(t, err) stored, err := db.ListFindings(encKey) require.NoError(t, err) assert.GreaterOrEqual(t, len(stored), 3) require.NoError(t, db.Close()) // Second import of the same file: everything should now be a duplicate. out.Reset() err = runImport(importCmd, []string{samplePath}) require.NoError(t, err) second := out.String() assert.Contains(t, second, "Imported 3 findings") assert.Contains(t, second, "0 new") assert.Contains(t, second, "3 duplicates") } func TestRunImport_UnknownFormat(t *testing.T) { _ = seedImportDB(t) importFormat = "bogus" err := runImport(importCmd, []string{"/nonexistent"}) require.Error(t, err) assert.Contains(t, err.Error(), "unknown format") } func TestRunImport_MissingFile(t *testing.T) { _ = seedImportDB(t) importFormat = "trufflehog" err := runImport(importCmd, []string{filepath.Join(t.TempDir(), "does-not-exist.json")}) require.Error(t, err) assert.Contains(t, err.Error(), "opening") }