package frame import ( "bufio" "bytes" "fmt" "image" "net" "strings" "testing" "time" "gocv.io/x/gocv" ) func ptr[T any](v T) *T { return &v } func testFrame(t *testing.T, width, height int, matType gocv.MatType, fillVal float64) Frame { t.Helper() mat := gocv.NewMatWithSize(height, width, matType) mat.SetTo(gocv.NewScalar(fillVal, fillVal, fillVal, 0)) return Frame{ PixelBytes: mat.ToBytes(), Width: uint(mat.Cols()), Height: uint(mat.Rows()), GocvImageType: mat.Type(), Channels: mat.Channels(), Guid: []byte("test-guid"), SourceData: "test--cam0", Timestamp: uint64(time.Now().UnixNano()), } } func TestToMat_Roundtrip(t *testing.T) { mat := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1) mat.SetTo(gocv.NewScalar(128, 0, 0, 0)) original := mat.ToBytes() f := Frame{ PixelBytes: original, Width: 4, Height: 4, GocvImageType: gocv.MatTypeCV8UC1, Channels: 1, } result, err := f.ToMat() if err != nil { t.Fatalf("ToMat() returned error: %v", err) } defer result.Close() if result.Cols() != 4 || result.Rows() != 4 { t.Errorf("got size %dx%d, want 4x4", result.Cols(), result.Rows()) } got := result.ToBytes() if !bytes.Equal(got, original) { t.Errorf("pixel bytes differ after roundtrip") } } func TestToMat_NonNilResult(t *testing.T) { f := Frame{ PixelBytes: []byte{0, 1, 2, 3}, Width: 2, Height: 2, GocvImageType: gocv.MatTypeCV8UC1, Channels: 1, } mat, err := f.ToMat() if err != nil { t.Fatalf("ToMat() error: %v", err) } defer mat.Close() if mat.Empty() { t.Error("expected non-empty Mat") } } func TestToMat_EmptyBytes(t *testing.T) { f := Frame{ PixelBytes: nil, Width: 0, Height: 0, GocvImageType: gocv.MatTypeCV8UC1, Channels: 1, } _, err := f.ToMat() if err == nil { t.Error("expected error for empty pixel data, got nil") } } func TestEncryptDecrypt_Roundtrip(t *testing.T) { original := []byte("these are my pixel bytes, there are many like them but these are mine") f := Frame{ PixelBytes: original, Width: 8, Height: 2, GocvImageType: gocv.MatTypeCV8UC3, Channels: 3, } if err := f.Encrypt("secret"); err != nil { t.Fatalf("Encrypt() error: %v", err) } if bytes.Equal(f.PixelBytes, original) { t.Error("PixelBytes unchanged after encryption") } if err := f.Decrypt("secret"); err != nil { t.Fatalf("Decrypt() error: %v", err) } if !bytes.Equal(f.PixelBytes, original) { t.Error("PixelBytes differ after decrypt roundtrip") } } func TestEncryptDecrypt_WrongPassphrase(t *testing.T) { original := []byte("sensitive pixel data") f := Frame{ PixelBytes: original, Width: 4, Height: 1, GocvImageType: gocv.MatTypeCV8UC3, Channels: 3, } if err := f.Encrypt("correct-horse-battery-staple"); err != nil { t.Fatalf("Encrypt() error: %v", err) } if err := f.Decrypt("wrong-passphrase"); err == nil { t.Error("expected error for wrong passphrase, got nil") } } func TestEncryptDecrypt_EmptyBytes(t *testing.T) { f := Frame{PixelBytes: nil} if err := f.Encrypt("pass"); err != nil { t.Errorf("Encrypt on empty bytes should succeed: %v", err) } if err := f.Decrypt("pass"); err != nil { t.Errorf("Decrypt on empty bytes should succeed: %v", err) } } func TestDecrypt_TooShort(t *testing.T) { f := Frame{PixelBytes: []byte{0, 1, 2}} if err := f.Decrypt("pass"); err == nil { t.Error("expected error for too-short data, got nil") } } func TestEncrypt_UniqueSaltPerCall(t *testing.T) { data := []byte("same data every time") f1 := Frame{PixelBytes: bytes.Clone(data)} f2 := Frame{PixelBytes: bytes.Clone(data)} f1.Encrypt("pass") f2.Encrypt("pass") if bytes.Equal(f1.PixelBytes, f2.PixelBytes) { t.Error("two encryptions of same data should differ (salt)") } } func testClip() Clip { return Clip{ PixelMats: [][]byte{{1, 2, 3}, {4, 5, 6}}, Width: 1, Height: 1, Types: gocv.MatTypeCV8UC3, Channels: 3, Guids: [][]byte{{0x01}, {0x02}}, Timestamps: []uint64{100, 200}, } } func TestClipEncryptDecrypt_Roundtrip(t *testing.T) { clp := testClip() if err := clp.Encrypt("secret"); err != nil { t.Fatalf("Encrypt() error: %v", err) } if len(clp.Salt) != saltSize { t.Fatalf("clip salt is %d bytes, want %d", len(clp.Salt), saltSize) } for i, px := range clp.PixelMats { if len(px) != 3+clipEncryptedOverhead { t.Fatalf("frame %d is %d bytes, want %d encrypted", i, len(px), 3+clipEncryptedOverhead) } } if err := clp.Decrypt("secret"); err != nil { t.Fatalf("Decrypt() error: %v", err) } want := testClip() for i := range clp.PixelMats { if !bytes.Equal(clp.PixelMats[i], want.PixelMats[i]) { t.Errorf("frame %d differs after clip decrypt roundtrip", i) } } } func TestClipEncryptDecrypt_WrongPassphrase(t *testing.T) { clp := testClip() if err := clp.Encrypt("correct-horse"); err != nil { t.Fatalf("Encrypt() error: %v", err) } if err := clp.Decrypt("wrong-passphrase"); err == nil { t.Error("expected error for wrong passphrase, got nil") } } func TestClipDecrypt_LegacyPerFrameFallback(t *testing.T) { want := testClip() legacy := testClip() for i := range legacy.PixelMats { f := Frame{PixelBytes: legacy.PixelMats[i]} if err := f.Encrypt("secret"); err != nil { t.Fatalf("frame %d Encrypt() error: %v", i, err) } legacy.PixelMats[i] = f.PixelBytes } if len(legacy.Salt) != 0 { t.Fatal("legacy clip must not carry a clip-level salt") } if err := legacy.Decrypt("secret"); err != nil { t.Fatalf("Decrypt() error: %v", err) } for i := range legacy.PixelMats { if !bytes.Equal(legacy.PixelMats[i], want.PixelMats[i]) { t.Errorf("frame %d differs after legacy per-frame decrypt", i) } } } func TestClipEncrypt_RecordsIterationCount(t *testing.T) { orig := DefaultPBKDF2Iter defer SetDefaultPBKDF2Iter(orig) SetDefaultPBKDF2Iter(12345) clp := testClip() if err := clp.Encrypt("secret"); err != nil { t.Fatalf("Encrypt() error: %v", err) } if clp.PBKDF2Iter != 12345 { t.Fatalf("clip records PBKDF2Iter=%d, want 12345", clp.PBKDF2Iter) } } func TestClipDecrypt_HonorsStoredIterationCount(t *testing.T) { orig := DefaultPBKDF2Iter defer SetDefaultPBKDF2Iter(orig) SetDefaultPBKDF2Iter(12345) want := testClip() clp := testClip() if err := clp.Encrypt("secret"); err != nil { t.Fatalf("Encrypt() error: %v", err) } if clp.PBKDF2Iter != 12345 { t.Fatalf("clip PBKDF2Iter=%d, want 12345", clp.PBKDF2Iter) } // Decrypting node is configured with a different default; it must still // use the count recorded on the clip. SetDefaultPBKDF2Iter(600000) if err := clp.Decrypt("secret"); err != nil { t.Fatalf("Decrypt with mismatched default: %v", err) } for i := range clp.PixelMats { if !bytes.Equal(clp.PixelMats[i], want.PixelMats[i]) { t.Errorf("frame %d differs after decrypt honoring stored iterations", i) } } } func TestClipDecrypt_TamperedIterationCountFails(t *testing.T) { orig := DefaultPBKDF2Iter defer SetDefaultPBKDF2Iter(orig) clp := testClip() if err := clp.Encrypt("secret"); err != nil { t.Fatalf("Encrypt() error: %v", err) } // Changing the count invalidates the derived key. clp.PBKDF2Iter = 600001 if err := clp.Decrypt("secret"); err == nil { t.Error("expected decrypt error after tampering with PBKDF2Iter, got nil") } } func TestClipEncrypt_Empty(t *testing.T) { clp := Clip{} if err := clp.Encrypt("secret"); err != nil { t.Errorf("Encrypt on empty clip should succeed: %v", err) } if len(clp.Salt) != 0 { t.Error("empty clip should not be assigned a salt") } } func TestAuthHandshake_Match(t *testing.T) { SetAuthToken("s3cret") t.Cleanup(func() { SetAuthToken("") }) server, client := net.Pipe() defer server.Close() defer client.Close() delivered := make(chan Clip, 1) go HandleClipConn(server, NewMalformedAttempts(), func(clp Clip) { delivered <- clp }) if err := writeAuthToken(client); err != nil { t.Fatalf("writeAuthToken: %v", err) } clp := validTestClip() if _, err := writeWireClip(client, &clp); err != nil { t.Fatalf("encode: %v", err) } select { case <-delivered: case <-time.After(3 * time.Second): t.Fatal("timed out waiting for authenticated clip") } } func TestAuthHandshake_Mismatch(t *testing.T) { SetAuthToken("s3cret") t.Cleanup(func() { SetAuthToken("") }) server, client := net.Pipe() defer server.Close() defer client.Close() done := make(chan struct{}) go func() { HandleClipConn(server, NewMalformedAttempts(), func(Clip) { t.Error("callback must not run for a bad token") }) close(done) }() if _, err := fmt.Fprintf(client, "wrong-token\n"); err != nil { t.Fatalf("write: %v", err) } client.Close() select { case <-done: case <-time.After(3 * time.Second): t.Fatal("HandleClipConn did not return for bad token") } } func TestAuthSend_EndToEnd(t *testing.T) { SetAuthToken("s3cret") t.Cleanup(func() { SetAuthToken("") }) ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen: %v", err) } defer ln.Close() received := make(chan Clip, 1) go func() { conn, err := ln.Accept() if err != nil { return } HandleClipConn(conn, NewMalformedAttempts(), func(clp Clip) { received <- clp }) }() clp := testClip() if err := clp.Send(ln.Addr().String()); err != nil { t.Fatalf("Send() error: %v", err) } select { case got := <-received: if !bytes.Equal(got.PixelMats[0], []byte{1, 2, 3}) { t.Error("received wrong pixel data") } case <-time.After(3 * time.Second): t.Fatal("timed out waiting for clip") } } func TestSublimate_Basic(t *testing.T) { clp := Clip{ PixelMats: [][]byte{ {1, 2, 3}, {4, 5, 6}, }, Width: 1, Height: 1, Types: gocv.MatTypeCV8UC3, Channels: 3, Guids: [][]byte{{0x01}, {0x02}}, Timestamps: []uint64{100, 200}, SourceData: "test--cam0", } frames := clp.Sublimate() if len(frames) != 2 { t.Fatalf("got %d frames, want 2", len(frames)) } for i, f := range frames { if f.Width != 1 || f.Height != 1 { t.Errorf("frame %d: got %dx%d, want 1x1", i, f.Width, f.Height) } if f.Channels != 3 { t.Errorf("frame %d: got %d channels, want 3", i, f.Channels) } if f.Guid[0] != byte(i+1) { t.Errorf("frame %d: guid = %x, want %x", i, f.Guid[0], byte(i+1)) } if f.Timestamp != uint64(100*(i+1)) { t.Errorf("frame %d: timestamp = %d, want %d", i, f.Timestamp, uint64(100*(i+1))) } if f.SourceData != "test--cam0" { t.Errorf("frame %d: sourceData = %q", i, f.SourceData) } } } func TestSublimate_EmptyClip(t *testing.T) { clp := Clip{} frames := clp.Sublimate() if len(frames) != 0 { t.Errorf("got %d frames, want 0", len(frames)) } } func TestSublimate_DetectionsCarryOver(t *testing.T) { dets := []map[string][]Detection{ {"human": {{DetectionTitle: "human", DetectionRegion: image.Rect(0, 0, 10, 10)}}}, nil, } clp := Clip{ PixelMats: [][]byte{{0}, {0}}, Guids: [][]byte{{1}, {2}}, Timestamps: []uint64{0, 0}, Detections: dets, } frames := clp.Sublimate() if len(frames[0].Detections["human"]) != 1 { t.Error("detections not carried over to frame 0") } if frames[1].Detections != nil { t.Error("expected nil detections for frame 1") } } func TestCheckLenCorrelations_Match(t *testing.T) { clp := Clip{ PixelMats: [][]byte{{0}, {0}, {0}}, Guids: [][]byte{{1}, {2}, {3}}, Timestamps: []uint64{10, 20, 30}, } ok, err := clp.CheckLenCorrelations() if !ok { t.Errorf("expected match, got error: %v", err) } } func TestCheckLenCorrelations_PixelMatsGuidsMismatch(t *testing.T) { clp := Clip{ PixelMats: [][]byte{{0}, {0}}, Guids: [][]byte{{1}}, Timestamps: []uint64{10, 20}, } ok, _ := clp.CheckLenCorrelations() if ok { t.Error("expected mismatch") } } func TestCheckLenCorrelations_TimestampsMismatch(t *testing.T) { clp := Clip{ PixelMats: [][]byte{{0}, {0}}, Guids: [][]byte{{1}, {2}}, Timestamps: []uint64{10}, } ok, _ := clp.CheckLenCorrelations() if ok { t.Error("expected mismatch") } } func TestCheckLenCorrelations_Empty(t *testing.T) { clp := Clip{} ok, err := clp.CheckLenCorrelations() if !ok { t.Errorf("empty clip should match: %v", err) } } func TestClipSend(t *testing.T) { SetAuthToken("test-token") t.Cleanup(func() { SetAuthToken("") }) ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen: %v", err) } defer ln.Close() addr := ln.Addr().String() received := make(chan Clip, 1) go func() { conn, err := ln.Accept() if err != nil { return } defer conn.Close() br := bufio.NewReader(conn) line, err := br.ReadString('\n') if err != nil || strings.TrimSpace(line) != "test-token" { return } clp, err := DecodeWireClip(br) if err == nil { received <- clp } }() sent := Clip{ PixelMats: [][]byte{{1, 2, 3}}, Width: 1, Height: 1, Types: gocv.MatTypeCV8UC3, Channels: 3, Guids: [][]byte{{42}}, Timestamps: []uint64{99}, SourceData: "test--cam0", } if err := sent.Send(addr); err != nil { t.Fatalf("Send() error: %v", err) } select { case got := <-received: if !bytes.Equal(got.PixelMats[0], []byte{1, 2, 3}) { t.Error("received wrong pixel data") } if got.Guids[0][0] != 42 { t.Error("received wrong guid") } if got.Timestamps[0] != 99 { t.Error("received wrong timestamp") } case <-time.After(3 * time.Second): t.Fatal("timed out waiting for clip") } } func TestClipSend_InvalidAddress(t *testing.T) { clp := Clip{} err := clp.Send("256.0.0.1:1") if err == nil { t.Error("expected error for unreachable address, got nil") } } func TestFrameFields_ZeroValues(t *testing.T) { f := Frame{} if f.Detections != nil { t.Error("expected nil Detections for zero-value Frame") } } func TestClipFields_ZeroValues(t *testing.T) { c := Clip{} if c.Comparisons != nil { t.Error("expected nil Comparisons for zero-value Clip") } }