feat(sense): establish M1 offline intake skeleton
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"yovision/sense/internal/device"
|
||||
)
|
||||
|
||||
func TestDefaultVideoQuotaRejectsSeventeenthChannel(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := openTestStore(t)
|
||||
ctx := context.Background()
|
||||
if err := store.EnsureSite(ctx, device.Site{TenantID: "tenant-a", ID: "site-a", Name: "Site A"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for index := 1; index <= device.DefaultVideoChannels; index++ {
|
||||
if err := store.CreateDevice(ctx, videoDevice(index, "tenant-a", "site-a")); err != nil {
|
||||
t.Fatalf("create channel %d: %v", index, err)
|
||||
}
|
||||
}
|
||||
err := store.CreateDevice(ctx, videoDevice(17, "tenant-a", "site-a"))
|
||||
var quotaError *device.QuotaExceededError
|
||||
if !errors.As(err, "aError) || quotaError.Limit != 16 {
|
||||
t.Fatalf("expected 16-channel quota error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfiguredMaximumAccepts128AndRejects129(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := openTestStore(t)
|
||||
ctx := context.Background()
|
||||
if err := store.EnsureSite(ctx, device.Site{
|
||||
TenantID: "tenant-b", ID: "site-b", Name: "Site B", MaxVideoChannels: 128,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for index := 1; index <= 128; index++ {
|
||||
if err := store.CreateDevice(ctx, videoDevice(index, "tenant-b", "site-b")); err != nil {
|
||||
t.Fatalf("create channel %d: %v", index, err)
|
||||
}
|
||||
}
|
||||
err := store.CreateDevice(ctx, videoDevice(129, "tenant-b", "site-b"))
|
||||
var quotaError *device.QuotaExceededError
|
||||
if !errors.As(err, "aError) || quotaError.Limit != 128 {
|
||||
t.Fatalf("expected 128-channel quota error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSiteRejectsCapacityAbove128(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := openTestStore(t)
|
||||
err := store.EnsureSite(context.Background(), device.Site{
|
||||
TenantID: "tenant", ID: "site", Name: "Site", MaxVideoChannels: 129,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected capacity 129 to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonVideoDeviceDoesNotConsumeVideoQuota(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := openTestStore(t)
|
||||
ctx := context.Background()
|
||||
if err := store.EnsureSite(ctx, device.Site{
|
||||
TenantID: "tenant", ID: "site", Name: "Site", MaxVideoChannels: 1,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
radar := device.Device{
|
||||
ID: "radar-1", TenantID: "tenant", SiteID: "site", SerialNumber: "radar-1",
|
||||
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.Fatal(err)
|
||||
}
|
||||
if err := store.CreateDevice(ctx, videoDevice(1, "tenant", "site")); err != nil {
|
||||
t.Fatalf("video channel should remain available: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnablingSeventeenthVideoDeviceIsRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := openTestStore(t)
|
||||
ctx := context.Background()
|
||||
if err := store.EnsureSite(ctx, device.Site{TenantID: "tenant-c", ID: "site-c", Name: "Site C"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for index := 1; index <= 17; index++ {
|
||||
value := videoDevice(index, "tenant-c", "site-c")
|
||||
if index == 17 {
|
||||
value.DesiredState = device.DesiredDisabled
|
||||
}
|
||||
if err := store.CreateDevice(ctx, value); err != nil {
|
||||
t.Fatalf("create device %d: %v", index, err)
|
||||
}
|
||||
}
|
||||
err := store.SetDesiredState(ctx, "camera-017", device.DesiredEnabled)
|
||||
var quotaError *device.QuotaExceededError
|
||||
if !errors.As(err, "aError) || quotaError.Limit != 16 {
|
||||
t.Fatalf("expected enable to enforce quota, got %v", err)
|
||||
}
|
||||
value, err := store.GetDevice(ctx, "camera-017")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if value.DesiredState != device.DesiredDisabled {
|
||||
t.Fatal("failed enable must leave the existing desired state unchanged")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowerQuotaDoesNotDisableExistingStreams(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := openTestStore(t)
|
||||
ctx := context.Background()
|
||||
if err := store.EnsureSite(ctx, device.Site{
|
||||
TenantID: "tenant-d", ID: "site-d", Name: "Site D", MaxVideoChannels: 2,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for index := 1; index <= 2; index++ {
|
||||
if err := store.CreateDevice(ctx, videoDevice(index, "tenant-d", "site-d")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := store.EnsureSite(ctx, device.Site{
|
||||
TenantID: "tenant-d", ID: "site-d", Name: "Site D", MaxVideoChannels: 1,
|
||||
}); 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("existing channel %d was disabled", index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openTestStore(t *testing.T) *SQLite {
|
||||
t.Helper()
|
||||
dsn := "file:" + filepath.ToSlash(filepath.Join(t.TempDir(), "sense.db"))
|
||||
store, err := OpenSQLite(context.Background(), dsn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = store.Close() })
|
||||
return store
|
||||
}
|
||||
|
||||
func videoDevice(index int, tenantID, siteID string) device.Device {
|
||||
id := fmt.Sprintf("camera-%03d", index)
|
||||
return device.Device{
|
||||
ID: id, TenantID: tenantID, SiteID: siteID, SerialNumber: id, Name: id,
|
||||
Modality: device.ModalityVideo,
|
||||
Capabilities: []device.Capability{device.CapabilityVideoCapture, device.CapabilitySpatialRule},
|
||||
DesiredState: device.DesiredEnabled, ActualState: device.ActualPending,
|
||||
EndpointRef: "onvif://" + id, CredentialRef: "secret://" + id,
|
||||
PathName: "sense/" + tenantID + "/" + siteID + "/" + id,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user