package scan import ( "context" "database/sql" "testing" _ "modernc.org/sqlite" ) func TestDetectAdapter_Greenhouse(t *testing.T) { c := Company{ Name: "Dragos", CareersURL: "https://job-boards.greenhouse.io/dragos", API: "https://boards-api.greenhouse.io/v1/boards/dragos/jobs", } info, ok := DetectAdapter(c) if !ok || info.Type != AdapterGreenhouse { t.Fatalf("expected greenhouse, got %v ok=%v", info.Type, ok) } } func TestDetectAdapter_Ashby(t *testing.T) { c := Company{Name: "Acme", CareersURL: "https://jobs.ashbyhq.com/acme"} info, ok := DetectAdapter(c) if !ok || info.Type != AdapterAshby { t.Fatalf("expected ashby, got %v ok=%v", info.Type, ok) } } func TestDetectAdapter_Lever(t *testing.T) { c := Company{Name: "Acme", CareersURL: "https://jobs.lever.co/acme"} info, ok := DetectAdapter(c) if !ok || info.Type != AdapterLever { t.Fatalf("expected lever, got %v ok=%v", info.Type, ok) } } func TestDetectAdapter_Unknown(t *testing.T) { c := Company{Name: "Acme", CareersURL: "https://acme.com/careers"} _, ok := DetectAdapter(c) if ok { t.Fatalf("expected unsupported for arbitrary careers page") } } func TestTitleMatcher_PositiveOnly(t *testing.T) { m := NewTitleMatcher(TitleFilter{Positive: []string{"Red Team", "Penetration Test"}}) if !m("Senior Red Team Operator") { t.Fatal("expected match") } if m("Senior Software Engineer") { t.Fatal("expected no match") } } func TestTitleMatcher_NegativeBlocks(t *testing.T) { m := NewTitleMatcher(TitleFilter{ Positive: []string{"Security"}, Negative: []string{"Intern", "Junior"}, }) if m("Security Intern") { t.Fatal("negative should block") } if !m("Security Engineer") { t.Fatal("positive should match") } } func TestRecordJobs_BatchDedup(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") if err != nil { t.Fatal(err) } defer db.Close() if _, err := db.Exec(` CREATE TABLE jobs (url TEXT UNIQUE NOT NULL, company TEXT, title TEXT, source TEXT); CREATE TABLE scanned_urls (url TEXT UNIQUE NOT NULL, company TEXT, title TEXT, source TEXT, status TEXT, first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP); `); err != nil { t.Fatal(err) } r := &Runner{DB: db} ctx := context.Background() batch := []Job{ {Title: "Senior Red Team", URL: "https://example.com/jobs/1", Company: "Acme", Source: "test"}, {Title: "Staff Pentester", URL: "https://example.com/jobs/2", Company: "Acme", Source: "test"}, } newJobs, err := r.recordJobs(ctx, batch) if err != nil { t.Fatalf("first batch: %v", err) } if len(newJobs) != 2 { t.Fatalf("expected 2 new, got %d", len(newJobs)) } // Re-run the same batch plus one fresh URL — only the new one should land. batch = append(batch, Job{Title: "Principal OT", URL: "https://example.com/jobs/3", Company: "Acme", Source: "test"}) newJobs, err = r.recordJobs(ctx, batch) if err != nil { t.Fatalf("second batch: %v", err) } if len(newJobs) != 1 || newJobs[0].URL != "https://example.com/jobs/3" { t.Fatalf("expected only jobs/3 new, got %+v", newJobs) } } func TestLoadConfig(t *testing.T) { cfg, err := LoadConfig("adapters.yaml") if err != nil { t.Fatalf("LoadConfig: %v", err) } if len(cfg.TrackedCompanies) == 0 { t.Fatal("expected seeded companies") } if len(cfg.TitleFilter.Positive) == 0 { t.Fatal("expected positive keywords") } }