package brain_control_test import ( "crypto/ed25519" "crypto/rand" "encoding/json" "strings" "sync" "testing" "time" bc "git.ilapage.cn/ila/yovision/Sense/server/app/sense/integration/brain_control" mi "git.ilapage.cn/ila/yovision/Sense/server/app/sense/integration/machine_identity" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func testDB(t *testing.T, name string) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open("file:"+name+"?mode=memory&cache=shared"), &gorm.Config{}) if err != nil { t.Fatal(err) } if err = db.AutoMigrate(&bc.ReplayToken{}, &bc.RuntimeProjection{}, &bc.SourceRevision{}); err != nil { t.Fatal(err) } return db } func TestMapperProducesCredentialFreeFrozenContract(t *testing.T) { now := time.Date(2026, 8, 31, 0, 0, 0, 0, time.UTC) c, err := bc.MapSourceConfig(bc.SourceFacts{ConfigID: "gate-primary", SiteID: "site-east", LogicalDeviceID: "camera-1", MediaPath: "site-east/camera-1/main", Revision: 1, PublishedAt: now, EffectiveAt: now, Profile: bc.Profile{ID: "main", Width: 1920, Height: 1080, Encoding: "h264", FrameRate: 25}, RuleSetVersion: "rules-1", Areas: []bc.AreaRule{{ID: "danger", Version: 1, Kind: "danger_area", Enabled: true, Points: []bc.Point{{X: .1, Y: .1}, {X: .8, Y: .1}, {X: .5, Y: .8}}}}}) if err != nil { t.Fatal(err) } if err = bc.ValidateSourceConfig(c); err != nil { t.Fatal(err) } raw, _ := json.Marshal(c) text := strings.ToLower(string(raw)) for _, secret := range []string{"password", "username", "rtsp://", "stream_uri", "credential"} { if strings.Contains(text, secret) { t.Fatalf("leaked %q", secret) } } c2, err := bc.MapSourceConfig(bc.SourceFacts{ConfigID: "gate-primary", SiteID: "site-east", LogicalDeviceID: "camera-1", MediaPath: "site-east/camera-1/main", Revision: 2, PublishedAt: now, EffectiveAt: now, Profile: bc.Profile{ID: "main-v2", Width: 1280, Height: 720, Encoding: "H265", FrameRate: 20}, RuleSetVersion: "rules-2", NeedsRecalibration: true, Areas: []bc.AreaRule{{ID: "danger", Version: 2, Kind: "danger_area", Enabled: true, Points: []bc.Point{{X: .1, Y: .1}, {X: .8, Y: .1}, {X: .5, Y: .8}}}}}) if err != nil { t.Fatal(err) } if c2.RuleSet.State != "recalibration_required" || c2.RuleSet.Areas[0].Enabled { t.Fatal("recalibration must disable rules") } } func TestDurableReplayIsAtomicAndSurvivesVerifierRestart(t *testing.T) { db := testDB(t, "sense-replay") pub, priv, _ := ed25519.GenerateKey(rand.Reader) now := time.Date(2026, 8, 31, 0, 0, 0, 0, time.UTC) registry, _ := mi.NewRegistry(mi.KeyRecord{Principal: "yv:brain:east", KeyID: "brain-key-01", PublicKey: pub, Audience: "yovision-sense", Scopes: []string{"runtime-status:write"}, Enabled: true}) signer := mi.Signer{Principal: "yv:brain:east", KeyID: "brain-key-01", PrivateKey: priv, Now: func() time.Time { return now }} body := []byte(`{"ok":true}`) token, _ := signer.Mint("yovision-sense", []string{"runtime-status:write"}, "POST", "/machine/v1/runtime-status", body) results := make(chan bool, 8) var wg sync.WaitGroup for i := 0; i < 8; i++ { wg.Add(1) go func() { defer wg.Done() v := mi.Verifier{Registry: registry, Replay: bc.GORMReplayStore{DB: db}, Now: func() time.Time { return now }} _, err := v.Verify(token, "yovision-sense", "runtime-status:write", "POST", "/machine/v1/runtime-status", body) results <- err == nil }() } wg.Wait() close(results) accepted := 0 for ok := range results { if ok { accepted++ } } if accepted != 1 { t.Fatalf("accepted=%d", accepted) } v2 := mi.Verifier{Registry: registry, Replay: bc.GORMReplayStore{DB: db}, Now: func() time.Time { return now }} if _, err := v2.Verify(token, "yovision-sense", "runtime-status:write", "POST", "/machine/v1/runtime-status", body); err == nil { t.Fatal("replay accepted after verifier restart") } } func TestRevisionStoreConcurrentAndRestart(t *testing.T) { db := testDB(t, "sense-revisions") store := bc.RevisionStore{DB: db} for want := int64(1); want <= 3; want++ { got, err := store.Next("gate-primary") if err != nil || got != want { t.Fatalf("got %d err %v", got, err) } } restarted := bc.RevisionStore{DB: db} got, err := restarted.Next("gate-primary") if err != nil || got != 4 { t.Fatalf("restart got %d err %v", got, err) } } func TestRuntimeProjectionStaleRecoveryMismatchAndOrdering(t *testing.T) { db := testDB(t, "sense-projection") now := time.Date(2026, 8, 31, 0, 0, 0, 0, time.UTC) store := bc.ProjectionStore{DB: db, Clock: func() time.Time { return now }, StaleAfter: 90 * time.Second} raw := statusJSON("018f4d6a-8d1b-4a25-8b37-9085f9c0d101", 41, now, "running", 20) view, err := store.Ingest(raw, map[string]int64{"gate-primary": 21}) if err != nil { t.Fatal(err) } if !view.RevisionMismatch || view.Stale { t.Fatalf("bad initial view %+v", view) } now = now.Add(91 * time.Second) view, err = store.View("brain-east-01") if err != nil || !view.Offline || !view.Stale { t.Fatalf("offline %+v %v", view, err) } raw = statusJSON("018f4d6a-8d1b-4a25-8b37-9085f9c0d102", 42, now, "running", 21) view, err = store.Ingest(raw, map[string]int64{"gate-primary": 21}) if err != nil || !view.Recovered || view.RevisionMismatch { t.Fatalf("recovery %+v %v", view, err) } if _, err = store.Ingest(statusJSON("018f4d6a-8d1b-4a25-8b37-9085f9c0d103", 41, now, "running", 21), nil); err == nil || err.Error() != "OUT_OF_ORDER_STATUS" { t.Fatalf("expected ordering rejection: %v", err) } } func statusJSON(id string, seq int64, observed time.Time, state string, revision int64) []byte { v := map[string]any{"schema_version": bc.RuntimeStatusVersion, "status_id": id, "brain_instance_ref": "brain-east-01", "sequence": seq, "observed_at": observed.Format(time.RFC3339), "runtime": map[string]any{"state": state, "version": "1.0.0", "started_at": observed.Add(-time.Minute).Format(time.RFC3339)}, "model": map[string]any{"model_ref": "people-detection", "version": "2026.08.1"}, "configurations": []any{map[string]any{"config_id": "gate-primary", "apply_state": "applied", "applied_revision": revision, "error_code": nil}}, "health": map[string]any{"overall": "healthy", "error_codes": []any{}, "metrics": map[string]any{"load_percent": 1.0, "queue_depth": 0, "latency_ms": 2.0}}, "inputs": []any{}} raw, _ := json.Marshal(v) return raw }