package media import ( "context" "crypto/rand" "encoding/base64" "errors" "os" "testing" "time" "gorm.io/driver/sqlite" "gorm.io/gorm" "git.ilapage.cn/ila/yovision/Sense/server/app/sense/credential" ) type fakeController struct { healthErr, applyErr, statusErr error applies int status PathStatus } func (f *fakeController) Health(context.Context) error { return f.healthErr } func (f *fakeController) Apply(context.Context, Source) error { f.applies++; return f.applyErr } func (f *fakeController) Delete(context.Context, string) error { return nil } func (f *fakeController) Status(context.Context, string) (PathStatus, error) { return f.status, f.statusErr } func mediaTestService(t *testing.T, controller Controller) (*Service, *gorm.DB) { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatal(err) } if err = db.AutoMigrate(&Route{}, &admissionProfile{}, &credential.DeviceCredential{}); err != nil { t.Fatal(err) } key := make([]byte, 32) if _, err = rand.Read(key); err != nil { t.Fatal(err) } t.Setenv(credential.EnvironmentKey, base64.StdEncoding.EncodeToString(key)) vault, _ := credential.NewVault(key) ciphertext, err := vault.Encrypt("device-1", credential.PurposeRTSP, "synthetic-user", "synthetic-password") if err != nil { t.Fatal(err) } if err = db.Create(&credential.DeviceCredential{DeviceID: "device-1", Purpose: credential.PurposeRTSP, Ciphertext: ciphertext, KeyVersion: credential.Version()}).Error; err != nil { t.Fatal(err) } if err = db.Create(&admissionProfile{DeviceID: "device-1", Token: "main", StreamURI: "rtsp://192.0.2.1/live", VerificationStatus: "ready"}).Error; err != nil { t.Fatal(err) } service := NewService(db, controller, nil, RuntimeConfig{}) service.now = func() time.Time { return time.Date(2026, 8, 14, 0, 0, 0, 0, time.UTC) } return service, db } func TestEnsureAndReconcileAreIdempotent(t *testing.T) { controller := &fakeController{status: PathStatus{Exists: true, Ready: true, Readers: 2}} service, db := mediaTestService(t, controller) if err := service.EnsureDevice(context.Background(), "device-1"); err != nil { t.Fatal(err) } if err := service.EnsureDevice(context.Background(), "device-1"); err != nil { t.Fatal(err) } var count int64 if err := db.Model(&Route{}).Count(&count).Error; err != nil || count != 1 { t.Fatalf("count=%d err=%v", count, err) } item, err := service.Reconcile(context.Background(), "device-1:main") if err != nil { t.Fatal(err) } if item.Actual != "ready" || item.Readers != 2 || controller.applies != 1 { t.Fatalf("item=%#v applies=%d", item, controller.applies) } if err = service.ReconcileDue(context.Background()); err != nil { t.Fatal(err) } items, err := service.List(context.Background()) if err != nil || controller.applies != 1 || items[0].Version != item.Version { t.Fatalf("steady route was rewritten: items=%#v applies=%d err=%v", items, controller.applies, err) } } func TestColdStartDoesNotReactivateStoppedRoute(t *testing.T) { controller := &fakeController{status: PathStatus{Exists: true}} service, _ := mediaTestService(t, controller) if err := service.EnsureDevice(context.Background(), "device-1"); err != nil { t.Fatal(err) } if _, err := service.StopRoute(context.Background(), "device-1:main"); err != nil { t.Fatal(err) } if err := service.EnsureAllVerified(context.Background()); err != nil { t.Fatal(err) } items, err := service.List(context.Background()) if err != nil || len(items) != 1 || items[0].Desired != DesiredStopped { t.Fatalf("stopped route was reactivated: %#v err=%v", items, err) } } func TestFailurePersistsBackoffWithoutChangingProfile(t *testing.T) { controller := &fakeController{applyErr: errors.New("synthetic apply failure")} service, db := mediaTestService(t, controller) if err := service.EnsureDevice(context.Background(), "device-1"); err != nil { t.Fatal(err) } item, err := service.Reconcile(context.Background(), "device-1:main") if err == nil || item.Actual != "apply_failed" || item.FailureCount != 1 || item.NextRetryAt == nil { t.Fatalf("item=%#v err=%v", item, err) } var profile admissionProfile if err = db.First(&profile, "device_id = ? AND token = ?", "device-1", "main").Error; err != nil { t.Fatal(err) } if profile.VerificationStatus != "ready" { t.Fatalf("profile changed: %#v", profile) } if value := os.Getenv(credential.EnvironmentKey); value == "" { t.Fatal("test key unexpectedly missing") } }