Files
yovision/Sense/server/app/sense/media/service_test.go
T

129 lines
4.5 KiB
Go

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")
}
}