package store import ( "bytes" "encoding/json" "os" "path/filepath" "testing" "git.ipao.vip/rogee/go-sip/internal/contract" ) func TestSIPSourceSurvivesRestartAndCorrelatesMalformedHTTP(t *testing.T) { path := filepath.Join(t.TempDir(), "state.db") s, err := Open(path) if err != nil { t.Fatal(err) } if err := s.NoteSIPChange(currentDispatcherID, 1); err != nil { t.Fatal(err) } raw := []byte(`{"event_id":"sip-original","event_type":"sip.config","dispatcher_id":"` + currentDispatcherID + `","payload":{"trunk_id":"1","change_type":"enabled","revision":1}}`) if err := s.RememberSIPSource(currentDispatcherID, raw); err != nil { t.Fatal(err) } if err := s.Close(); err != nil { t.Fatal(err) } s, err = Open(path) if err != nil { t.Fatal(err) } defer s.Close() sources, err := s.PendingSIPSources() if err != nil || len(sources) != 1 || !bytes.Equal(sources[0].Original, raw) { t.Fatalf("notification context lost: %+v %v", sources, err) } invalid := []byte(`{"resource":"sip_config","private":"PRIVATE-SIP-HTTP"}`) cause := contract.ValidateCurrent("config-read", invalid) if cause == nil { t.Fatal("invalid HTTP fixture accepted") } if err := s.RejectData(currentDispatcherID, raw, cause); err != nil { t.Fatal(err) } if err := s.CompleteSIPSource(sources[0]); err != nil { t.Fatal(err) } events, err := s.ListPendingOutbox(currentDispatcherID) if err != nil || len(events) != 1 || !bytes.Contains(events[0].Body, []byte(`"event_type":"sip.config"`)) { t.Fatalf("notification rejection missing: %+v %v", events, err) } files, err := filepath.Glob(filepath.Join(filepath.Dir(path), "rejected-data", "*.json")) if err != nil || len(files) != 1 { t.Fatalf("private original missing: %v %v", files, err) } saved, err := os.ReadFile(files[0]) if err != nil { t.Fatal(err) } var evidence struct{ Original, InvalidSource []byte } if err := json.Unmarshal(saved, &evidence); err != nil || !bytes.Equal(evidence.Original, raw) || !bytes.Equal(evidence.InvalidSource, invalid) { t.Fatal("source bytes changed or not saved") } }