Files
oko_public/coordinator/matrix_test.go
T
2026-09-09 21:44:05 -05:00

204 lines
5.5 KiB
Go

package main
import (
"encoding/json"
"strings"
"testing"
"frame"
"gocv.io/x/gocv"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
const testClipsRoom = id.RoomID("!clips:server")
const testDetsRoom = id.RoomID("!dets:server")
const testViewRoom = id.RoomID("!view:server")
// testClip builds a clip with the given motion percentage (via Comparisons)
// and optional detections. Width/Height of 10x10 makes PixelsChanged == the
// motion percentage directly.
func testClip(motionPercent int, detections bool) *frame.Clip {
clp := &frame.Clip{
Width: 10,
Height: 10,
}
if motionPercent > 0 {
clp.Comparisons = []frame.Comparison{{PixelsChanged: motionPercent, Threshold: 30}}
}
if detections {
clp.Detections = []map[string][]frame.Detection{
{"person": {{DetectionTitle: "person", DetectionCertainty: 0.95}}},
}
}
return clp
}
func sameRooms(got, want []id.RoomID) bool {
if len(got) != len(want) {
return false
}
for i := range got {
if got[i] != want[i] {
return false
}
}
return true
}
func TestRouteTargets(t *testing.T) {
tests := []struct {
name string
motion int
hasDet bool
want []id.RoomID
}{
{"no motion, no detections", 0, false, nil},
{"no motion, detections present", 0, true, nil},
{"motion, no detections", 5, false, []id.RoomID{testClipsRoom}},
{"motion with detections", 5, true, []id.RoomID{testClipsRoom, testDetsRoom}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := routeTargets(testClip(tt.motion, tt.hasDet), testClipsRoom, testDetsRoom)
if !sameRooms(got, tt.want) {
t.Errorf("routeTargets = %v, want %v", got, tt.want)
}
})
}
}
func TestHasDetections(t *testing.T) {
tests := []struct {
name string
dets []map[string][]frame.Detection
want bool
}{
{"nil", nil, false},
{"empty frame map", []map[string][]frame.Detection{{}}, false},
{"empty frame slices", []map[string][]frame.Detection{{"person": {}}}, false},
{"one detection", []map[string][]frame.Detection{{"person": {{DetectionTitle: "person"}}}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
clp := &frame.Clip{Detections: tt.dets}
if got := hasDetections(clp); got != tt.want {
t.Errorf("hasDetections = %v, want %v", got, tt.want)
}
})
}
}
func TestVideoContent(t *testing.T) {
c := videoContent("aabb-100.mp4", id.MustParseContentURI("mxc://server/mediaid"), 2048)
if c.MsgType != event.MsgVideo {
t.Errorf("MsgType = %v, want MsgVideo", c.MsgType)
}
if c.Body != "aabb-100.mp4" {
t.Errorf("Body = %v, want aabb-100.mp4", c.Body)
}
if c.URL != "mxc://server/mediaid" {
t.Errorf("URL = %v, want mxc://server/mediaid", c.URL)
}
if c.Info == nil || c.Info.MimeType != "video/mp4" {
t.Errorf("Info.MimeType = %v, want video/mp4", c.Info.MimeType)
}
if c.Info == nil || c.Info.Size != 2048 {
t.Errorf("Info.Size = %v, want 2048", c.Info.Size)
}
}
func TestMetadataContent(t *testing.T) {
meta := `{"source_data":"oko--front"}`
c := metadataContent(id.EventID("$videoevent"), meta)
if c.MsgType != event.MsgText {
t.Errorf("MsgType = %v, want MsgText", c.MsgType)
}
if c.Body != meta {
t.Errorf("Body = %v, want %v", c.Body, meta)
}
if c.RelatesTo == nil || c.RelatesTo.InReplyTo == nil {
t.Fatalf("RelatesTo.InReplyTo not set")
}
if got := c.RelatesTo.InReplyTo.EventID; got != "$videoevent" {
t.Errorf("InReplyTo.EventID = %v, want $videoevent", got)
}
}
func TestBuildClipMetadata(t *testing.T) {
clp := frame.Clip{
PixelMats: [][]byte{{1, 2, 3}},
Width: 2,
Height: 2,
Types: gocv.MatTypeCV8UC1,
Channels: 1,
Guids: [][]byte{{0xaa, 0xbb}},
Timestamps: []uint64{100},
SourceData: "oko--front_door",
Detections: []map[string][]frame.Detection{
{"person": {{DetectionTitle: "person", DetectionCertainty: 0.9}}},
},
Comparisons: []frame.Comparison{{PixelsChanged: 1, Threshold: 30}},
}
data, err := buildClipMetadata(&clp, 30.0)
if err != nil {
t.Fatalf("buildClipMetadata: %v", err)
}
var meta map[string]interface{}
if err := json.Unmarshal([]byte(data), &meta); err != nil {
t.Fatalf("output is not valid JSON: %v", err)
}
if meta["source_data"] != "oko--front_door" {
t.Errorf("source_data = %v, want oko--front_door", meta["source_data"])
}
if meta["motion_percent"] != float64(25) {
t.Errorf("motion_percent = %v, want 25 (1 of 4 pixels)", meta["motion_percent"])
}
if meta["frame_count"] != float64(1) {
t.Errorf("frame_count = %v, want 1", meta["frame_count"])
}
if meta["fps"] != float64(30) {
t.Errorf("fps = %v, want 30", meta["fps"])
}
guids, ok := meta["guids"].([]interface{})
if !ok || len(guids) != 1 || guids[0] != "aabb" {
t.Errorf("guids = %v, want hex aabb", meta["guids"])
}
dets, ok := meta["detections"].([]interface{})
if !ok || len(dets) != 1 {
t.Errorf("detections not preserved in metadata")
}
}
func TestViewCaption(t *testing.T) {
clp := frame.Clip{
PixelMats: [][]byte{{1, 2, 3}, {4, 5, 6}},
Width: 10,
Height: 10,
SourceData: "oko--front_door",
Comparisons: []frame.Comparison{
{PixelsChanged: 10, Threshold: 30},
{PixelsChanged: 20, Threshold: 30},
},
}
// minimal, human-readable, and free of JSON
got := viewCaption(&clp, 15.5)
want := "oko--front_door · 2 frames · 15.5 fps · motion 20%"
if got != want {
t.Errorf("viewCaption = %q, want %q", got, want)
}
for _, bad := range []string{"{", "}", `"`, "motion_percent", "source_data"} {
if strings.Contains(got, bad) {
t.Errorf("viewCaption contains JSON-ish token %q: %q", bad, got)
}
}
}