package diff import ( "testing" "time" "god-eye/internal/store" ) func TestCompute_NewHost(t *testing.T) { oldHosts := []*store.Host{} newHosts := []*store.Host{{Subdomain: "api.example.com"}} r := Compute("example.com", oldHosts, newHosts, time.Now(), time.Now()) if len(r.Changes) != 1 || r.Changes[0].Kind != "new_host" { t.Errorf("expected 1 new_host change, got %+v", r.Changes) } if !r.HasMeaningful() { t.Error("new_host should be meaningful") } } func TestCompute_RemovedHost(t *testing.T) { oldHosts := []*store.Host{{Subdomain: "old.example.com"}} newHosts := []*store.Host{} r := Compute("example.com", oldHosts, newHosts, time.Now(), time.Now()) if len(r.Changes) != 1 || r.Changes[0].Kind != "removed_host" { t.Errorf("expected removed_host, got %+v", r.Changes) } if !r.HasMeaningful() { t.Error("removed_host should be meaningful") } } func TestCompute_StatusChange(t *testing.T) { oldH := &store.Host{Subdomain: "a.example.com", StatusCode: 200} newH := &store.Host{Subdomain: "a.example.com", StatusCode: 401} r := Compute("example.com", []*store.Host{oldH}, []*store.Host{newH}, time.Now(), time.Now()) if len(r.Changes) != 1 || r.Changes[0].Kind != "status_change" { t.Errorf("expected status_change, got %+v", r.Changes) } if r.Changes[0].Before != "200" || r.Changes[0].After != "401" { t.Errorf("wrong before/after: %+v", r.Changes[0]) } } func TestCompute_IPDelta(t *testing.T) { oldH := &store.Host{Subdomain: "a.example.com", IPs: []string{"1.1.1.1"}} newH := &store.Host{Subdomain: "a.example.com", IPs: []string{"1.1.1.1", "2.2.2.2"}} r := Compute("example.com", []*store.Host{oldH}, []*store.Host{newH}, time.Now(), time.Now()) found := false for _, c := range r.Changes { if c.Kind == "new_ip" && c.After == "2.2.2.2" { found = true } } if !found { t.Errorf("expected new_ip change, got %+v", r.Changes) } } func TestCompute_NewVuln(t *testing.T) { oldH := &store.Host{Subdomain: "a.example.com"} newH := &store.Host{ Subdomain: "a.example.com", Vulnerabilities: []store.Vulnerability{ {ID: "xss", Title: "Reflected XSS", Severity: "high"}, }, } r := Compute("example.com", []*store.Host{oldH}, []*store.Host{newH}, time.Now(), time.Now()) found := false for _, c := range r.Changes { if c.Kind == "new_vuln" && c.After == "Reflected XSS" { found = true } } if !found { t.Errorf("expected new_vuln change, got %+v", r.Changes) } if !r.HasMeaningful() { t.Error("new_vuln must be meaningful") } } func TestCompute_ClearedVuln(t *testing.T) { oldH := &store.Host{ Subdomain: "a.example.com", Vulnerabilities: []store.Vulnerability{ {ID: "git-exposed", Title: "Git Exposed", Severity: "critical"}, }, } newH := &store.Host{Subdomain: "a.example.com"} r := Compute("example.com", []*store.Host{oldH}, []*store.Host{newH}, time.Now(), time.Now()) found := false for _, c := range r.Changes { if c.Kind == "cleared_vuln" { found = true } } if !found { t.Errorf("expected cleared_vuln, got %+v", r.Changes) } } func TestCompute_NewTakeover(t *testing.T) { oldH := &store.Host{Subdomain: "a.example.com"} newH := &store.Host{ Subdomain: "a.example.com", Takeover: &store.Takeover{Service: "GitHub Pages"}, } r := Compute("example.com", []*store.Host{oldH}, []*store.Host{newH}, time.Now(), time.Now()) found := false for _, c := range r.Changes { if c.Kind == "new_takeover" && c.After == "GitHub Pages" { found = true } } if !found { t.Errorf("expected new_takeover, got %+v", r.Changes) } if !r.HasMeaningful() { t.Error("new_takeover must be meaningful") } } func TestCompute_NoChange(t *testing.T) { h := &store.Host{ Subdomain: "a.example.com", IPs: []string{"1.1.1.1"}, StatusCode: 200, Technologies: []string{"nginx"}, } r := Compute("example.com", []*store.Host{h}, []*store.Host{h}, time.Now(), time.Now()) if len(r.Changes) != 0 { t.Errorf("expected no changes, got %+v", r.Changes) } if r.HasMeaningful() { t.Error("empty report should not be meaningful") } } func TestCompute_TechChange(t *testing.T) { oldH := &store.Host{Subdomain: "a.example.com", Technologies: []string{"nginx"}} newH := &store.Host{Subdomain: "a.example.com", Technologies: []string{"nginx", "Apache"}} r := Compute("example.com", []*store.Host{oldH}, []*store.Host{newH}, time.Now(), time.Now()) found := false for _, c := range r.Changes { if c.Kind == "tech_change" { found = true } } if !found { t.Errorf("expected tech_change, got %+v", r.Changes) } }