package local_event import ( "encoding/json" "strings" "testing" "time" coreService "github.com/go-admin-team/go-admin-core/sdk/service" "github.com/google/uuid" "gorm.io/driver/sqlite" "gorm.io/gorm" adminModels "git.ilapage.cn/ila/yovision/Sense/server/app/admin/models" commonDTO "git.ilapage.cn/ila/yovision/Sense/server/common/dto" ) func localEventService(t *testing.T, now time.Time) *Service { t.Helper() db, err := gorm.Open(sqlite.Open("file:"+uuid.NewString()+"?mode=memory&cache=shared"), &gorm.Config{}) if err != nil { t.Fatal(err) } sqlDB, err := db.DB() if err != nil { t.Fatal(err) } sqlDB.SetMaxOpenConns(1) if err = db.AutoMigrate(&EventCandidate{}, &adminModels.SysOperaLog{}); err != nil { t.Fatal(err) } return &Service{Service: coreService.Service{Orm: db}, Now: func() time.Time { return now }} } func TestSyntheticFixtureSupportsIndependentListFilterAndDetail(t *testing.T) { now := time.Date(2026, 8, 28, 2, 0, 0, 0, time.UTC) service := localEventService(t, now) if err := SeedSyntheticFixture(service.Orm, now); err != nil { t.Fatal(err) } // Seeding is explicit and idempotent; no Bell or Brain service is involved. if err := SeedSyntheticFixture(service.Orm, now); err != nil { t.Fatal(err) } items, count, err := service.List(&PageRequest{CandidateState: CandidateStateCandidate, EvidenceState: EvidenceStateFailed, Keyword: "SEN-CAM-11"}) if err != nil { t.Fatal(err) } if count != 1 || len(items) != 1 || items[0].RetentionState != "expiring" || items[0].RemainingDays != 2 { t.Fatalf("unexpected filtered fixture: count=%d items=%#v", count, items) } detail, err := service.Get(items[0].ID) if err != nil { t.Fatal(err) } if detail.EvidenceState != EvidenceStateFailed || detail.CandidateState != CandidateStateCandidate || detail.EvidenceDetail == "" { t.Fatalf("unexpected detail: %#v", detail) } encoded, _ := json.Marshal(detail) if strings.Contains(strings.ToLower(string(encoded)), "bell") || strings.Contains(strings.ToLower(string(encoded)), "brain") { t.Fatalf("local response leaked a cross-project schema field: %s", encoded) } } func TestListPaginatesAndOrdersNewestFirst(t *testing.T) { now := time.Date(2026, 8, 28, 2, 0, 0, 0, time.UTC) service := localEventService(t, now) if err := SeedSyntheticFixture(service.Orm, now); err != nil { t.Fatal(err) } first, count, err := service.List(&PageRequest{Pagination: pagination(1, 2)}) if err != nil { t.Fatal(err) } second, _, err := service.List(&PageRequest{Pagination: pagination(2, 2)}) if err != nil { t.Fatal(err) } if count != 3 || len(first) != 2 || len(second) != 1 || !first[0].OccurredAt.After(first[1].OccurredAt) || !first[1].OccurredAt.After(second[0].OccurredAt) { t.Fatalf("unexpected pagination: first=%#v second=%#v count=%d", first, second, count) } } func TestListRejectsInvalidStatesTimesAndOversizedPages(t *testing.T) { now := time.Date(2026, 8, 28, 2, 0, 0, 0, time.UTC) service := localEventService(t, now) cases := []*PageRequest{ {CandidateState: "delivered"}, {EvidenceState: "unknown"}, {OccurredAfter: "not-a-time"}, {OccurredAfter: now.Format(time.RFC3339), OccurredBefore: now.Add(-time.Hour).Format(time.RFC3339)}, {Pagination: pagination(1, 101)}, } for _, request := range cases { if _, _, err := service.List(request); err != ErrInvalidFilter { t.Fatalf("request %#v: expected ErrInvalidFilter, got %v", request, err) } } } func TestGetReportsMissingCandidate(t *testing.T) { service := localEventService(t, time.Now().UTC()) if _, err := service.Get("00000000-0000-4000-8000-000000000099"); err != ErrNotFound { t.Fatalf("expected ErrNotFound, got %v", err) } } func TestAccessAuditIsSynchronousAndExcludesEventData(t *testing.T) { now := time.Date(2026, 8, 28, 2, 0, 0, 0, time.UTC) service := localEventService(t, now) if err := WriteAccessAudit(service.Orm, AccessAudit{Action: "Get", Status: auditSuccess, Username: "operator", UserID: 7, ClientIP: "127.0.0.1", Route: "/api/v1/local-events/:id", Remark: "读取本地事件详情 fixture-1", At: now}); err != nil { t.Fatal(err) } var stored adminModels.SysOperaLog if err := service.Orm.First(&stored).Error; err != nil { t.Fatal(err) } if stored.Title != "本地事件" || stored.Status != auditSuccess || stored.CreateBy != 7 || stored.OperUrl != "/api/v1/local-events/:id" { t.Fatalf("unexpected access audit: %#v", stored) } if stored.OperParam != "" || stored.JsonResult != "" { t.Fatalf("audit persisted query or response data: %#v", stored) } } func pagination(index, size int) commonDTO.Pagination { return commonDTO.Pagination{PageIndex: index, PageSize: size} }