129 lines
4.6 KiB
Go
129 lines
4.6 KiB
Go
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}
|
|
}
|