package store import ( "context" "database/sql" "errors" "fmt" "os" "strings" "sync" "testing" "time" "yovision/sense/internal/device" ) const ( postgresTestDSNEnv = "YOVISION_TEST_POSTGRES_DSN" postgresTestAdminDSNEnv = "YOVISION_TEST_POSTGRES_ADMIN_DSN" ) func TestPostgresInvalidDSNDoesNotLeakInput(t *testing.T) { secret := "do-not-echo-this-value" _, err := OpenPostgres(context.Background(), "postgres://sense:"+secret+"@%zz") if err == nil { t.Fatal("invalid PostgreSQL DSN was accepted") } if strings.Contains(err.Error(), secret) { t.Fatal("PostgreSQL configuration error leaked DSN input") } } func TestOpenRepositoryRejectsUnknownDriver(t *testing.T) { _, err := OpenRepository(context.Background(), "mysql", "unused") if err == nil { t.Fatal("unknown repository driver was accepted") } } func TestPostgresDefaultAndMaximumQuota(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant-default", "site-default", 0) for index := 1; index <= device.DefaultVideoChannels; index++ { if err := store.CreateDevice(ctx, videoDevice(index, "tenant-default", "site-default")); err != nil { t.Fatalf("create default channel %d: %v", index, err) } } radar := device.Device{ ID: "radar-default", TenantID: "tenant-default", SiteID: "site-default", AreaID: "area-default", SerialNumber: "radar-default", Name: "Radar", Modality: device.ModalityRadar, Capabilities: []device.Capability{device.CapabilityTelemetry}, DesiredState: device.DesiredEnabled, ActualState: device.ActualPending, } if err := store.CreateDevice(ctx, radar); err != nil { t.Fatalf("non-video device must not consume video quota: %v", err) } err := store.CreateDevice(ctx, videoDevice(17, "tenant-default", "site-default")) var quotaError *device.QuotaExceededError if !errors.As(err, "aError) || quotaError.Limit != 16 { t.Fatalf("expected default quota error, got %v", err) } insertBellSite(t, admin, "tenant-max", "site-max", 128) for index := 1; index <= device.MaximumVideoChannels; index++ { if err := store.CreateDevice(ctx, videoDevice(index+1000, "tenant-max", "site-max")); err != nil { t.Fatalf("create maximum channel %d: %v", index, err) } } err = store.CreateDevice(ctx, videoDevice(1129, "tenant-max", "site-max")) if !errors.As(err, "aError) || quotaError.Limit != 128 { t.Fatalf("expected maximum quota error, got %v", err) } } func TestPostgresConcurrentAdmissionCannotExceedQuota(t *testing.T) { store, admin := openPostgresTestStore(t) insertBellSite(t, admin, "tenant", "site", 1) ctx := context.Background() start := make(chan struct{}) errorsFound := make(chan error, 2) var wait sync.WaitGroup for index := 1; index <= 2; index++ { wait.Add(1) go func(index int) { defer wait.Done() <-start errorsFound <- store.CreateDevice(ctx, videoDevice(index, "tenant", "site")) }(index) } close(start) wait.Wait() close(errorsFound) var succeeded, rejected int for err := range errorsFound { if err == nil { succeeded++ continue } var quotaError *device.QuotaExceededError if errors.As(err, "aError) { rejected++ continue } t.Fatalf("unexpected concurrent admission error: %v", err) } if succeeded != 1 || rejected != 1 { t.Fatalf("expected one success and one quota rejection, got success=%d rejected=%d", succeeded, rejected) } } func TestPostgresProjectionFailureAndRollbackFailClosed(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() err := store.CreateDevice(ctx, videoDevice(1, "missing-tenant", "missing-site")) if !errors.Is(err, ErrAreaPolicyUnavailable) { t.Fatalf("missing Area projection must fail closed first, got %v", err) } insertBellSite(t, admin, "tenant", "site", 2) if _, err := store.db.ExecContext(ctx, `INSERT INTO sense.site_quota_projection_state( tenant_id, site_id, source_version, synced_at ) VALUES ('tenant', 'site', 99, clock_timestamp())`); err != nil { t.Fatal(err) } err = store.CreateDevice(ctx, videoDevice(2, "tenant", "site")) if !errors.Is(err, ErrQuotaProjectionInvalid) { t.Fatalf("projection version rollback must fail closed, got %v", err) } var devices int if err := store.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM sense.devices`).Scan(&devices); err != nil { t.Fatal(err) } if devices != 0 { t.Fatalf("failed admissions must not persist devices, got %d", devices) } } func TestPostgresLowerQuotaDoesNotDisableAndEnableStillChecks(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) for index := 1; index <= 2; index++ { if err := store.CreateDevice(ctx, videoDevice(index, "tenant", "site")); err != nil { t.Fatal(err) } } disabled := videoDevice(3, "tenant", "site") disabled.DesiredState = device.DesiredDisabled if err := store.CreateDevice(ctx, disabled); err != nil { t.Fatal(err) } if _, err := admin.ExecContext(ctx, `UPDATE bell.sites SET max_video_channels = 1 WHERE tenant_id = 'tenant' AND id = 'site'`); err != nil { t.Fatal(err) } for index := 1; index <= 2; index++ { value, err := store.GetDevice(ctx, fmt.Sprintf("camera-%03d", index)) if err != nil { t.Fatal(err) } if value.DesiredState != device.DesiredEnabled { t.Fatalf("quota decrease disabled existing device %d", index) } } err := store.SetDesiredState(ctx, "camera-003", device.DesiredEnabled) var quotaError *device.QuotaExceededError if !errors.As(err, "aError) || quotaError.Limit != 1 { t.Fatalf("new enable after quota decrease must be rejected, got %v", err) } } func TestPostgresTenantIsolationAndConvergenceParity(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant-a", "site", 1) insertBellSite(t, admin, "tenant-b", "site", 1) left := videoDevice(1, "tenant-a", "site") right := videoDevice(2, "tenant-b", "site") right.SerialNumber = left.SerialNumber if err := store.CreateDevice(ctx, left); err != nil { t.Fatal(err) } if err := store.CreateDevice(ctx, right); err != nil { t.Fatal(err) } now := time.Date(2026, 8, 7, 0, 0, 0, 0, time.UTC) if err := store.MarkReconciled(ctx, left.ID, 1, now); err != nil { t.Fatal(err) } if err := store.UpdateActualState(ctx, left.ID, device.ActualOnline, now); err != nil { t.Fatal(err) } snapshot, err := store.ConvergenceSnapshot(ctx) if err != nil { t.Fatal(err) } if snapshot.Total != 2 || snapshot.Unconverged != 1 { t.Fatalf("unexpected convergence snapshot: %+v", snapshot) } if err := store.RequestReconcile(ctx, left.ID, now.Add(time.Second)); err != nil { t.Fatal(err) } snapshot, err = store.ConvergenceSnapshot(ctx) if err != nil { t.Fatal(err) } if snapshot.Unconverged != 2 { t.Fatalf("runtime reconcile request was not persisted: %+v", snapshot) } } func TestPostgresRecordsQuotaSourceVersion(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) if err := store.CreateDevice(ctx, videoDevice(1, "tenant", "site")); err != nil { t.Fatal(err) } var firstVersion int64 if err := store.db.QueryRowContext(ctx, `SELECT quota_source_version FROM sense.devices WHERE id = 'camera-001'`).Scan(&firstVersion); err != nil { t.Fatal(err) } if _, err := admin.ExecContext(ctx, `UPDATE bell.sites SET name = name WHERE tenant_id = 'tenant' AND id = 'site'`); err != nil { t.Fatal(err) } second := videoDevice(2, "tenant", "site") if err := store.CreateDevice(ctx, second); err != nil { t.Fatal(err) } var secondVersion int64 if err := store.db.QueryRowContext(ctx, `SELECT quota_source_version FROM sense.devices WHERE id = 'camera-002'`).Scan(&secondVersion); err != nil { t.Fatal(err) } if firstVersion != 1 || secondVersion != 2 { t.Fatalf("expected recorded projection versions 1 and 2, got %d and %d", firstVersion, secondVersion) } } func TestPostgresReconcilePortParity(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 1) if err := store.CreateDevice(ctx, videoDevice(1, "tenant", "site")); err != nil { t.Fatal(err) } now := time.Now().UTC() due, err := store.ListDueReconcile(ctx, now.Add(time.Second), 10) if err != nil || len(due) != 1 || len(due[0].Device.Capabilities) != 2 { t.Fatalf("unexpected due reconcile list: values=%+v error=%v", due, err) } enabled, err := store.ListEnabledVideoDevices(ctx, 10) if err != nil || len(enabled) != 1 || enabled[0].ID != "camera-001" { t.Fatalf("unexpected enabled video list: values=%+v error=%v", enabled, err) } if err := store.SetDesiredState(ctx, "camera-001", device.DesiredEnabled); err != nil { t.Fatal(err) } value, err := store.GetDevice(ctx, "camera-001") if err != nil || value.Generation != 1 { t.Fatalf("idempotent desired state changed generation: value=%+v error=%v", value, err) } nextAttempt := now.Add(time.Minute) if err := store.MarkReconcileFailure(ctx, "camera-001", 1, nextAttempt, "test_failure", now); err != nil { t.Fatal(err) } due, err = store.ListDueReconcile(ctx, now.Add(30*time.Second), 10) if err != nil || len(due) != 0 { t.Fatalf("backoff device became due early: values=%+v error=%v", due, err) } due, err = store.ListDueReconcile(ctx, now.Add(2*time.Minute), 10) if err != nil || len(due) != 1 || due[0].FailureCount != 1 { t.Fatalf("backoff device did not become due: values=%+v error=%v", due, err) } value, err = store.GetDevice(ctx, "camera-001") if err != nil || value.ActualState != device.ActualFailed { t.Fatalf("reconcile failure did not persist actual state: value=%+v error=%v", value, err) } } func TestPostgresOpenRejectsOverprivilegedRuntimeRole(t *testing.T) { _, admin := openPostgresTestStore(t) ctx := context.Background() if _, err := admin.ExecContext(ctx, `GRANT UPDATE ON bell.site_quota_v1 TO yovision_t010_sense`); err != nil { t.Fatal(err) } defer func() { _, _ = admin.ExecContext(context.Background(), `REVOKE UPDATE ON bell.site_quota_v1 FROM yovision_t010_sense`) }() value, err := OpenPostgres(ctx, os.Getenv(postgresTestDSNEnv)) if value != nil { _ = value.Close() t.Fatal("overprivileged runtime role was accepted") } if err == nil || !strings.Contains(err.Error(), "privilege boundary") { t.Fatalf("expected privilege-boundary error, got %v", err) } } func TestPostgresAreaPolicyAllowsNonImagingAndDeniesImagingCreate(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) if _, err := admin.ExecContext(ctx, `UPDATE bell.areas SET capture_policy = 'non_imaging_only' WHERE tenant_id = 'tenant' AND id = 'area-default'`); err != nil { t.Fatal(err) } blocked := videoDevice(1, "tenant", "site") blocked.DesiredState = device.DesiredDisabled err := store.CreateDevice(ctx, blocked) if !errors.Is(err, ErrAreaPolicyDenied) { t.Fatalf("disabled imaging create must still be denied, got %v", err) } radar := device.Device{ ID: "radar-001", TenantID: "tenant", SiteID: "site", AreaID: "area-default", SerialNumber: "radar-001", Name: "Radar", Modality: device.ModalityRadar, Capabilities: []device.Capability{device.CapabilityTelemetry}, DesiredState: device.DesiredEnabled, ActualState: device.ActualPending, } if err := store.CreateDevice(ctx, radar); err != nil { t.Fatalf("non-imaging device must be allowed: %v", err) } stored, err := store.GetDevice(ctx, radar.ID) if err != nil { t.Fatal(err) } if stored.AreaID != "area-default" || stored.AreaPolicySourceVersion != 2 { t.Fatalf("Area projection evidence was not stored: %+v", stored) } var devices, audits int if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.devices`).Scan(&devices); err != nil { t.Fatal(err) } if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.device_operation_outbox`).Scan(&audits); err != nil { t.Fatal(err) } if devices != 1 || audits != 1 { t.Fatalf("denied create left partial state: devices=%d audits=%d", devices, audits) } } func TestPostgresAreaProjectionMissingAndRollbackFailClosed(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) missing := videoDevice(1, "tenant", "site") missing.AreaID = "missing-area" if err := store.CreateDevice(ctx, missing); !errors.Is(err, ErrAreaPolicyUnavailable) { t.Fatalf("missing Area projection must fail closed, got %v", err) } if _, err := store.db.ExecContext(ctx, `INSERT INTO sense.area_policy_projection_state( tenant_id, site_id, area_id, source_version, synced_at ) VALUES ('tenant', 'site', 'area-default', 99, clock_timestamp())`); err != nil { t.Fatal(err) } if err := store.CreateDevice(ctx, videoDevice(2, "tenant", "site")); !errors.Is(err, ErrAreaPolicyInvalid) { t.Fatalf("Area source-version rollback must fail closed, got %v", err) } var devices, audits int if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.devices`).Scan(&devices); err != nil { t.Fatal(err) } if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.device_operation_outbox`).Scan(&audits); err != nil { t.Fatal(err) } if devices != 0 || audits != 0 { t.Fatalf("failed Area admissions persisted state: devices=%d audits=%d", devices, audits) } } func TestPostgresConcurrentAreaObservationIsMonotonic(t *testing.T) { store, admin := openPostgresTestStore(t) insertBellSite(t, admin, "tenant", "site", 2) ctx := context.Background() start := make(chan struct{}) errorsFound := make(chan error, 2) var wait sync.WaitGroup for index := 1; index <= 2; index++ { wait.Add(1) go func(index int) { defer wait.Done() value := videoDevice(index, "tenant", "site") value.DesiredState = device.DesiredDisabled <-start errorsFound <- store.CreateDevice(ctx, value) }(index) } close(start) wait.Wait() close(errorsFound) for err := range errorsFound { if err != nil { t.Fatalf("concurrent Area observation failed: %v", err) } } var sourceVersion int64 if err := admin.QueryRowContext(ctx, `SELECT source_version FROM sense.area_policy_projection_state WHERE tenant_id = 'tenant' AND site_id = 'site' AND area_id = 'area-default'`). Scan(&sourceVersion); err != nil { t.Fatal(err) } if sourceVersion != 1 { t.Fatalf("concurrent observation recorded version %d", sourceVersion) } } func TestPostgresAreaProjectionCannotCrossTenantBoundary(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant-a", "site", 2) insertBellSite(t, admin, "tenant-b", "site", 2) insertBellArea(t, admin, "tenant-b", "site", "private-area", "video_allowed") value := videoDevice(1, "tenant-a", "site") value.AreaID = "private-area" if err := store.CreateDevice(ctx, value); !errors.Is(err, ErrAreaPolicyUnavailable) { t.Fatalf("cross-tenant Area was not hidden as unavailable: %v", err) } } func TestPostgresEnableRechecksAreaWithoutStoppingExistingDevice(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) disabled := videoDevice(1, "tenant", "site") disabled.DesiredState = device.DesiredDisabled if err := store.CreateDevice(ctx, disabled); err != nil { t.Fatal(err) } if err := store.CreateDevice(ctx, videoDevice(2, "tenant", "site")); err != nil { t.Fatal(err) } if _, err := admin.ExecContext(ctx, `UPDATE bell.areas SET capture_policy = 'non_imaging_only' WHERE tenant_id = 'tenant' AND id = 'area-default'`); err != nil { t.Fatal(err) } if err := store.SetDesiredState(ctx, disabled.ID, device.DesiredEnabled); !errors.Is(err, ErrAreaPolicyDenied) { t.Fatalf("enable under non-imaging policy must be denied, got %v", err) } stillDisabled, err := store.GetDevice(ctx, disabled.ID) if err != nil { t.Fatal(err) } stillEnabled, err := store.GetDevice(ctx, "camera-002") if err != nil { t.Fatal(err) } if stillDisabled.DesiredState != device.DesiredDisabled || stillEnabled.DesiredState != device.DesiredEnabled { t.Fatalf("policy change altered existing state: disabled=%s enabled=%s", stillDisabled.DesiredState, stillEnabled.DesiredState) } } func TestPostgresAuditContextRedactionAndNoopDesiredState(t *testing.T) { store, admin := openPostgresTestStore(t) insertBellSite(t, admin, "tenant", "site", 2) ctx := WithAuditContext(context.Background(), AuditContext{ ActorType: AuditActorUser, ActorID: "operator-7", Reason: "approved change", TraceID: "trace-7", }) value := videoDevice(1, "tenant", "site") if err := store.CreateDevice(ctx, value); err != nil { t.Fatal(err) } if err := store.SetDesiredState(ctx, value.ID, device.DesiredEnabled); err != nil { t.Fatal(err) } stored, err := store.GetDevice(ctx, value.ID) if err != nil { t.Fatal(err) } if stored.Generation != 1 { t.Fatalf("no-op desired-state request changed generation to %d", stored.Generation) } rows, err := admin.QueryContext(ctx, `SELECT event_id, event_type, actor_type, actor_id, COALESCE(reason, ''), COALESCE(trace_id, ''), payload::text FROM sense.device_operation_outbox ORDER BY occurred_at, event_id`) if err != nil { t.Fatal(err) } defer rows.Close() var count int for rows.Next() { var eventID, eventType, actorType, actorID, reason, traceID, payload string if err := rows.Scan(&eventID, &eventType, &actorType, &actorID, &reason, &traceID, &payload); err != nil { t.Fatal(err) } count++ if !strings.HasPrefix(eventID, "audit_") || len(eventID) != 38 { t.Fatalf("invalid audit event ID %q", eventID) } if actorType != "user" || actorID != "operator-7" || reason != "approved change" || traceID != "trace-7" { t.Fatalf("audit principal/context drift: %s/%s %s %s", actorType, actorID, reason, traceID) } for _, secret := range []string{value.EndpointRef, value.CredentialRef, value.PathName} { if strings.Contains(payload, secret) { t.Fatalf("audit payload leaked sensitive runtime data for %s", eventType) } } } if err := rows.Err(); err != nil { t.Fatal(err) } if count != 2 { t.Fatalf("expected create and no-op audit facts, got %d", count) } } func TestPostgresOutboxFailureRollsBackAdmission(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) if _, err := admin.ExecContext(ctx, `CREATE FUNCTION sense.t010_reject_outbox() RETURNS trigger LANGUAGE plpgsql AS $function$ BEGIN RAISE EXCEPTION 'synthetic outbox failure'; END $function$; CREATE TRIGGER t010_reject_outbox BEFORE INSERT ON sense.device_operation_outbox FOR EACH ROW EXECUTE FUNCTION sense.t010_reject_outbox()`); err != nil { t.Fatal(err) } defer func() { _, _ = admin.ExecContext(context.Background(), `DROP TRIGGER IF EXISTS t010_reject_outbox ON sense.device_operation_outbox; DROP FUNCTION IF EXISTS sense.t010_reject_outbox()`) }() if err := store.CreateDevice(ctx, videoDevice(1, "tenant", "site")); err == nil { t.Fatal("synthetic Outbox failure did not reject device creation") } var devices, projections, audits int if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.devices`).Scan(&devices); err != nil { t.Fatal(err) } if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.area_policy_projection_state`).Scan(&projections); err != nil { t.Fatal(err) } if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.device_operation_outbox`).Scan(&audits); err != nil { t.Fatal(err) } if devices != 0 || projections != 0 || audits != 0 { t.Fatalf("Outbox failure left partial transaction: devices=%d projections=%d audits=%d", devices, projections, audits) } } func TestPostgresOutboxFailureRollsBackDesiredState(t *testing.T) { store, admin := openPostgresTestStore(t) ctx := context.Background() insertBellSite(t, admin, "tenant", "site", 2) value := videoDevice(1, "tenant", "site") value.DesiredState = device.DesiredDisabled if err := store.CreateDevice(ctx, value); err != nil { t.Fatal(err) } if _, err := admin.ExecContext(ctx, `CREATE FUNCTION sense.t010_reject_state_audit() RETURNS trigger LANGUAGE plpgsql AS $function$ BEGIN RAISE EXCEPTION 'synthetic state-audit failure'; END $function$; CREATE TRIGGER t010_reject_state_audit BEFORE INSERT ON sense.device_operation_outbox FOR EACH ROW EXECUTE FUNCTION sense.t010_reject_state_audit()`); err != nil { t.Fatal(err) } defer func() { _, _ = admin.ExecContext(context.Background(), `DROP TRIGGER IF EXISTS t010_reject_state_audit ON sense.device_operation_outbox; DROP FUNCTION IF EXISTS sense.t010_reject_state_audit()`) }() if err := store.SetDesiredState(ctx, value.ID, device.DesiredEnabled); err == nil { t.Fatal("synthetic Outbox failure did not reject desired-state change") } stored, err := store.GetDevice(ctx, value.ID) if err != nil { t.Fatal(err) } if stored.DesiredState != device.DesiredDisabled || stored.Generation != 1 { t.Fatalf("Outbox failure committed desired state: %+v", stored) } var audits int if err := admin.QueryRowContext(ctx, `SELECT count(*) FROM sense.device_operation_outbox`).Scan(&audits); err != nil { t.Fatal(err) } if audits != 1 { t.Fatalf("failed desired-state transaction changed Outbox count to %d", audits) } } func TestPostgresOpenRejectsAreaSourcePrivilege(t *testing.T) { _, admin := openPostgresTestStore(t) ctx := context.Background() if _, err := admin.ExecContext(ctx, `GRANT SELECT ON bell.areas TO yovision_t010_sense`); err != nil { t.Fatal(err) } defer func() { _, _ = admin.ExecContext(context.Background(), `REVOKE SELECT ON bell.areas FROM yovision_t010_sense`) }() value, err := OpenPostgres(ctx, os.Getenv(postgresTestDSNEnv)) if value != nil { _ = value.Close() t.Fatal("Area-source privilege was accepted") } if err == nil || !strings.Contains(err.Error(), "privilege boundary") { t.Fatalf("expected Area privilege-boundary error, got %v", err) } } func openPostgresTestStore(t *testing.T) (*Postgres, *sql.DB) { t.Helper() dsn := os.Getenv(postgresTestDSNEnv) adminDSN := os.Getenv(postgresTestAdminDSNEnv) if dsn == "" || adminDSN == "" { t.Skip("PostgreSQL integration DSNs are not configured") } admin, err := sql.Open("pgx", adminDSN) if err != nil { t.Fatal(err) } if err := admin.PingContext(context.Background()); err != nil { admin.Close() t.Fatal("connect PostgreSQL test administrator") } if _, err := admin.ExecContext(context.Background(), `TRUNCATE sense.device_operation_outbox, sense.device_capabilities, sense.reconcile_state, sense.devices, sense.site_quota_projection_state, sense.area_policy_projection_state, bell.areas, bell.sites CASCADE`); err != nil { admin.Close() t.Fatal(err) } store, err := OpenPostgres(context.Background(), dsn) if err != nil { admin.Close() t.Fatal(err) } t.Cleanup(func() { _ = store.Close() _ = admin.Close() }) return store, admin } func insertBellSite(t *testing.T, admin *sql.DB, tenantID, siteID string, quota int) { t.Helper() query := `INSERT INTO bell.sites(tenant_id, id, name, max_video_channels) VALUES ($1, $2, $3, $4)` arguments := []any{tenantID, siteID, "Test Site", quota} if quota == 0 { query = `INSERT INTO bell.sites(tenant_id, id, name) VALUES ($1, $2, $3)` arguments = arguments[:3] } if _, err := admin.ExecContext(context.Background(), query, arguments...); err != nil { t.Fatal(err) } insertBellArea(t, admin, tenantID, siteID, "area-default", "video_allowed") } func insertBellArea( t *testing.T, admin *sql.DB, tenantID, siteID, areaID, capturePolicy string, ) { t.Helper() if _, err := admin.ExecContext(context.Background(), `INSERT INTO bell.areas( tenant_id, site_id, id, name, capture_policy ) VALUES ($1, $2, $3, $4, $5)`, tenantID, siteID, areaID, "Test Area", capturePolicy); err != nil { t.Fatal(err) } }