FCCJNYCVGOW6WVHYUUQ3RHHKB4QU3S4LU4AINB4VKWBOXLCPXILQC }func TestConvertToFloat64_16Bit(t *testing.T) {// 16-bit signed, little-endian: 0x0001 = 1 → 1/32768data := []byte{0x01, 0x00, 0xFF, 0x7F}samples := convertToFloat64(data, 16, 1)if len(samples) != 2 {t.Fatalf("expected 2 samples, got %d", len(samples))}if math.Abs(samples[0]-1.0/32768.0) > 1e-10 {t.Errorf("sample[0] = %v, want %v", samples[0], 1.0/32768.0)}// 0x7FFF = 32767 → 32767/32768 ≈ 0.99997if math.Abs(samples[1]-32767.0/32768.0) > 1e-10 {t.Errorf("sample[1] = %v, want %v", samples[1], 32767.0/32768.0)}}func TestConvertToFloat64_16BitNegative(t *testing.T) {// 0x8000 = -32768 → -32768/32768 = -1.0data := []byte{0x00, 0x80}samples := convertToFloat64(data, 16, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-(-1.0)) > 1e-10 {t.Errorf("sample = %v, want -1.0", samples[0])}}func TestConvertToFloat64_16BitStereo(t *testing.T) {// Stereo: should extract only left channel// Left: 0x0001 = 1, Right: 0x0002 = 2data := []byte{0x01, 0x00, 0x02, 0x00}samples := convertToFloat64(data, 16, 2)if len(samples) != 1 {t.Fatalf("expected 1 sample (left only), got %d", len(samples))}if math.Abs(samples[0]-1.0/32768.0) > 1e-10 {t.Errorf("sample = %v, want %v", samples[0], 1.0/32768.0)}}func TestConvertToFloat64_24Bit(t *testing.T) {// 24-bit signed, little-endian: 0x000001 = 1 → 1/8388608data := []byte{0x01, 0x00, 0x00}samples := convertToFloat64(data, 24, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-1.0/8388608.0) > 1e-12 {t.Errorf("sample = %v, want %v", samples[0], 1.0/8388608.0)}}func TestConvertToFloat64_24BitNegative(t *testing.T) {// 24-bit: 0xFFFFFF = -1 (sign extended) → -1/8388608data := []byte{0xFF, 0xFF, 0xFF}samples := convertToFloat64(data, 24, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-(-1.0/8388608.0)) > 1e-12 {t.Errorf("sample = %v, want %v", samples[0], -1.0/8388608.0)}}func TestConvertToFloat64_24BitMaxPositive(t *testing.T) {// 24-bit max positive: 0x7FFFFF = 8388607 → ~0.9999999data := []byte{0xFF, 0xFF, 0x7F}samples := convertToFloat64(data, 24, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-8388607.0/8388608.0) > 1e-12 {t.Errorf("sample = %v, want %v", samples[0], 8388607.0/8388608.0)}}func TestConvertToFloat64_24BitMinNegative(t *testing.T) {// 24-bit min negative: 0x800000 = -8388608 → -1.0data := []byte{0x00, 0x00, 0x80}samples := convertToFloat64(data, 24, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-(-1.0)) > 1e-12 {t.Errorf("sample = %v, want -1.0", samples[0])}}func TestConvertToFloat64_24BitStereo(t *testing.T) {// 24-bit stereo: left=0x000001=1, right=0x000002=2data := []byte{0x01, 0x00, 0x00, 0x02, 0x00, 0x00}samples := convertToFloat64(data, 24, 2)if len(samples) != 1 {t.Fatalf("expected 1 sample (left only), got %d", len(samples))}if math.Abs(samples[0]-1.0/8388608.0) > 1e-12 {t.Errorf("sample = %v, want %v", samples[0], 1.0/8388608.0)}
func TestConvertToFloat64_32Bit(t *testing.T) {// 32-bit signed, little-endian: 0x00000001 = 1 → 1/2147483648data := []byte{0x01, 0x00, 0x00, 0x00}samples := convertToFloat64(data, 32, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-1.0/2147483648.0) > 1e-15 {t.Errorf("sample = %v, want %v", samples[0], 1.0/2147483648.0)}}func TestConvertToFloat64_32BitNegative(t *testing.T) {// 32-bit: 0x80000000 = -2147483648 → -1.0data := []byte{0x00, 0x00, 0x00, 0x80}samples := convertToFloat64(data, 32, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-(-1.0)) > 1e-15 {t.Errorf("sample = %v, want -1.0", samples[0])}}func TestConvertToFloat64_32BitMaxPositive(t *testing.T) {// 32-bit max positive: 0x7FFFFFFF = 2147483647 → ~0.9999999995data := []byte{0xFF, 0xFF, 0xFF, 0x7F}samples := convertToFloat64(data, 32, 1)if len(samples) != 1 {t.Fatalf("expected 1 sample, got %d", len(samples))}if math.Abs(samples[0]-2147483647.0/2147483648.0) > 1e-15 {t.Errorf("sample = %v, want %v", samples[0], 2147483647.0/2147483648.0)}}func TestConvertToFloat64_32BitStereo(t *testing.T) {// 32-bit stereo: left=1, right=2data := []byte{0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}samples := convertToFloat64(data, 32, 2)if len(samples) != 1 {t.Fatalf("expected 1 sample (left only), got %d", len(samples))}if math.Abs(samples[0]-1.0/2147483648.0) > 1e-15 {t.Errorf("sample = %v, want %v", samples[0], 1.0/2147483648.0)}}func TestConvertToFloat64_UnsupportedBitDepth(t *testing.T) {// Unsupported bit depth (8) falls back to 16-bit parsing.// blockAlign = (8/8)*1 = 1, so numSamples = len(data)/1 = 4.// But 16-bit reads 2 bytes per sample, which panics with only 1 byte per block.// This tests the fallback path exists; for valid 8-bit data, blockAlign// must be 2 (so 2 bytes per sample in the data layout).data := []byte{0x01, 0x00, 0x02, 0x00} // 4 bytes, treated as 2x 16-bit samplessamples := convertToFloat64(data, 8, 2) // 2 channels → blockAlign = 1*2 = 2if len(samples) != 2 {t.Fatalf("expected 2 samples (fallback 16-bit), got %d", len(samples))}}func TestConvertToFloat64_EmptyData(t *testing.T) {samples := convertToFloat64([]byte{}, 16, 1)if len(samples) != 0 {t.Errorf("expected 0 samples, got %d", len(samples))}}
}}func TestClampImageSize(t *testing.T) {tests := []struct {input intwant int}{{100, 224}, // below minimum, clamped up{224, 224}, // at minimum{448, 448}, // in range{600, 600}, // in range{896, 896}, // at maximum{1000, 896}, // above maximum, clamped down}for _, tt := range tests {got := ClampImageSize(tt.input)if got != tt.want {t.Errorf("ClampImageSize(%d) = %d, want %d", tt.input, got, tt.want)}}}func TestCreateGrayscaleImage_Basic(t *testing.T) {data := [][]uint8{{0, 128, 255},{64, 192, 32},}img := CreateGrayscaleImage(data)if img == nil {t.Fatal("expected non-nil image")}bounds := img.Bounds()if bounds.Dx() != 3 {t.Errorf("width = %d, want 3", bounds.Dx())}if bounds.Dy() != 2 {t.Errorf("height = %d, want 2", bounds.Dy())}gray := img.(*image.Gray)// Row 0if gray.Pix[0] != 0 {t.Errorf("pix[0] = %d, want 0", gray.Pix[0])}if gray.Pix[1] != 128 {t.Errorf("pix[1] = %d, want 128", gray.Pix[1])}if gray.Pix[2] != 255 {t.Errorf("pix[2] = %d, want 255", gray.Pix[2])}// Row 1if gray.Pix[gray.Stride+0] != 64 {t.Errorf("pix[row1+0] = %d, want 64", gray.Pix[gray.Stride])}if gray.Pix[gray.Stride+1] != 192 {t.Errorf("pix[row1+1] = %d, want 192", gray.Pix[gray.Stride+1])}if gray.Pix[gray.Stride+2] != 32 {t.Errorf("pix[row1+2] = %d, want 32", gray.Pix[gray.Stride+2])}}func TestCreateGrayscaleImage_Empty(t *testing.T) {tests := []struct {name stringdata [][]uint8}{{"nil", nil},{"empty outer", [][]uint8{}},{"empty inner", [][]uint8{{}}},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {img := CreateGrayscaleImage(tt.data)if img != nil {t.Errorf("expected nil for %s", tt.name)}})}}func TestCreateRGBImage_Basic(t *testing.T) {data := [][]RGBPixel{{{R: 255, G: 0, B: 0}, {R: 0, G: 255, B: 0}},{{R: 0, G: 0, B: 255}, {R: 255, G: 255, B: 255}},}img := CreateRGBImage(data)if img == nil {t.Fatal("expected non-nil image")
bounds := img.Bounds()if bounds.Dx() != 2 {t.Errorf("width = %d, want 2", bounds.Dx())}if bounds.Dy() != 2 {t.Errorf("height = %d, want 2", bounds.Dy())}rgba := img.(*image.RGBA)// Check red pixel (0,0)if rgba.Pix[0] != 255 || rgba.Pix[1] != 0 || rgba.Pix[2] != 0 || rgba.Pix[3] != 255 {t.Errorf("pixel (0,0) = %v, want red+alpha", rgba.Pix[0:4])}// Check green pixel (1,0)if rgba.Pix[4] != 0 || rgba.Pix[5] != 255 || rgba.Pix[6] != 0 || rgba.Pix[7] != 255 {t.Errorf("pixel (1,0) = %v, want green+alpha", rgba.Pix[4:8])}// Check blue pixel (0,1) - row 1off := rgba.Strideif rgba.Pix[off] != 0 || rgba.Pix[off+1] != 0 || rgba.Pix[off+2] != 255 || rgba.Pix[off+3] != 255 {t.Errorf("pixel (0,1) = %v, want blue+alpha", rgba.Pix[off:off+4])}
func TestCreateRGBImage_Empty(t *testing.T) {tests := []struct {name stringdata [][]RGBPixel}{{"nil", nil},{"empty outer", [][]RGBPixel{}},{"empty inner", [][]RGBPixel{{}}},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {img := CreateRGBImage(tt.data)if img != nil {t.Errorf("expected nil for %s", tt.name)}})}}func TestResizeImage_Grayscale(t *testing.T) {// 4x4 uniform gray → 2x2 should produce same gray valuedata := make([][]uint8, 4)for i := range data {data[i] = []uint8{128, 128, 128, 128}}src := CreateGrayscaleImage(data)resized := ResizeImage(src, 2, 2)gray := resized.(*image.Gray)for y := range 2 {for x := range 2 {if gray.Pix[y*gray.Stride+x] != 128 {t.Errorf("pixel (%d,%d) = %d, want 128", x, y, gray.Pix[y*gray.Stride+x])}}}}func TestResizeImage_RGBA(t *testing.T) {// 4x4 uniform color → 2x2 should preserve colorrgbData := make([][]RGBPixel, 4)for i := range rgbData {rgbData[i] = []RGBPixel{{R: 100, G: 150, B: 200}, {R: 100, G: 150, B: 200}, {R: 100, G: 150, B: 200}, {R: 100, G: 150, B: 200}}}src := CreateRGBImage(rgbData)resized := ResizeImage(src, 2, 2)rgba := resized.(*image.RGBA)for y := range 2 {for x := range 2 {off := y*rgba.Stride + x*4if rgba.Pix[off] != 100 || rgba.Pix[off+1] != 150 || rgba.Pix[off+2] != 200 {t.Errorf("pixel (%d,%d) = (%d,%d,%d), want (100,150,200)",x, y, rgba.Pix[off], rgba.Pix[off+1], rgba.Pix[off+2])}}}}func TestResizeImage_GenericFallback(t *testing.T) {// Create a paletted image (not *image.Gray or *image.RGBA) to exercise fallback pathpalette := color.Palette{color.Black, color.White}src := image.NewPaletted(image.Rect(0, 0, 4, 4), palette)for y := range 4 {for x := range 4 {src.SetColorIndex(x, y, 0) // black}}resized := ResizeImage(src, 2, 2)bounds := resized.Bounds()if bounds.Dx() != 2 || bounds.Dy() != 2 {t.Errorf("bounds = %v, want 2x2", bounds)}}func TestResizeImage_Upscale(t *testing.T) {// 2x2 → 4x4 nearest-neighbor upscaledata := [][]uint8{{0, 255},{128, 64},}src := CreateGrayscaleImage(data)resized := ResizeImage(src, 4, 4)gray := resized.(*image.Gray)// Top-left quadrant should be 0if gray.Pix[0] != 0 {t.Errorf("(0,0) = %d, want 0", gray.Pix[0])}// Top-right quadrant should be 255if gray.Pix[3] != 255 {t.Errorf("(3,0) = %d, want 255", gray.Pix[3])}}func TestWritePNG(t *testing.T) {img := image.NewGray(image.Rect(0, 0, 4, 4))var buf bytes.Bufferif err := WritePNG(img, &buf); err != nil {t.Fatalf("WritePNG: %v", err)}if buf.Len() == 0 {t.Error("expected non-empty PNG output")}// Verify it's a valid PNG by decodingdecoded, err := png.Decode(&buf)if err != nil {t.Fatalf("failed to decode PNG: %v", err)}if decoded.Bounds().Dx() != 4 || decoded.Bounds().Dy() != 4 {t.Errorf("decoded bounds = %v, want 4x4", decoded.Bounds())}}
package utilsimport ("os""path/filepath""sort""testing")func TestFindDataFiles_Basic(t *testing.T) {dir := t.TempDir()// Create some .data filesfor _, name := range []string{"a.data", "b.data", "c.data"} {if err := os.WriteFile(filepath.Join(dir, name), []byte("[]"), 0644); err != nil {t.Fatal(err)}}// Create a non-.data file that should be ignoredif err := os.WriteFile(filepath.Join(dir, "notes.txt"), []byte("ignore"), 0644); err != nil {t.Fatal(err)}files, err := FindDataFiles(dir)if err != nil {t.Fatal(err)}sort.Strings(files)if len(files) != 3 {t.Fatalf("expected 3 files, got %d: %v", len(files), files)}for i, base := range []string{"a.data", "b.data", "c.data"} {expected := filepath.Join(dir, base)if files[i] != expected {t.Errorf("file %d: got %q, want %q", i, files[i], expected)}}}func TestFindDataFiles_SkipsHidden(t *testing.T) {dir := t.TempDir()// Regular .data fileif err := os.WriteFile(filepath.Join(dir, "visible.data"), []byte("[]"), 0644); err != nil {t.Fatal(err)}// Hidden .data file (should be skipped)if err := os.WriteFile(filepath.Join(dir, ".hidden.data"), []byte("[]"), 0644); err != nil {t.Fatal(err)}files, err := FindDataFiles(dir)if err != nil {t.Fatal(err)}if len(files) != 1 {t.Fatalf("expected 1 file (hidden skipped), got %d: %v", len(files), files)}if filepath.Base(files[0]) != "visible.data" {t.Errorf("got %q, want visible.data", files[0])}}func TestFindDataFiles_NonRecursive(t *testing.T) {dir := t.TempDir()// .data file in rootif err := os.WriteFile(filepath.Join(dir, "root.data"), []byte("[]"), 0644); err != nil {t.Fatal(err)}// .data file in subdirectory (should NOT be found)sub := filepath.Join(dir, "subdir")if err := os.Mkdir(sub, 0755); err != nil {t.Fatal(err)}if err := os.WriteFile(filepath.Join(sub, "nested.data"), []byte("[]"), 0644); err != nil {t.Fatal(err)}files, err := FindDataFiles(dir)if err != nil {t.Fatal(err)}if len(files) != 1 {t.Fatalf("expected 1 file (non-recursive), got %d: %v", len(files), files)}if filepath.Base(files[0]) != "root.data" {t.Errorf("got %q, want root.data", files[0])}}func TestFindDataFiles_EmptyDir(t *testing.T) {dir := t.TempDir()files, err := FindDataFiles(dir)if err != nil {t.Fatal(err)}if len(files) != 0 {t.Errorf("expected 0 files, got %d", len(files))}}func TestFindDataFiles_NonexistentDir(t *testing.T) {_, err := FindDataFiles("/nonexistent/path/12345")if err == nil {t.Error("expected error for nonexistent directory")}}func TestFindDataFiles_NoDataFiles(t *testing.T) {dir := t.TempDir()if err := os.WriteFile(filepath.Join(dir, "readme.txt"), []byte("hello"), 0644); err != nil {t.Fatal(err)}files, err := FindDataFiles(dir)if err != nil {t.Fatal(err)}if len(files) != 0 {t.Errorf("expected 0 files, got %d", len(files))}}
package utilsimport ("testing")func TestL4Colormap_ControlPoints(t *testing.T) {tests := []struct {idx intwantR uint8wantG uint8wantB uint8desc string}{{0, 0, 0, 0, "black at index 0"},{255, 255, 255, 0, "yellow at index 255"},}for _, tt := range tests {pixel := L4Colormap[tt.idx]if pixel.R != tt.wantR || pixel.G != tt.wantG || pixel.B != tt.wantB {t.Errorf("L4Colormap[%d] (%s): got (%d,%d,%d), want (%d,%d,%d)",tt.idx, tt.desc, pixel.R, pixel.G, pixel.B, tt.wantR, tt.wantG, tt.wantB)}}// Index 85: first segment is (0,0,0)→(0.85,0,0), t=85/85=1.0p85 := L4Colormap[85]if p85.R != 216 || p85.G != 0 || p85.B != 0 {t.Errorf("L4Colormap[85]: got (%d,%d,%d), want dark red", p85.R, p85.G, p85.B)}// Index 170: second segment is (0.85,0,0)→(1.0,0.15,0), t=85/85=1.0p170 := L4Colormap[170]if p170.R != 255 || p170.G != 38 || p170.B != 0 {t.Errorf("L4Colormap[170]: got (%d,%d,%d), want orange-red", p170.R, p170.G, p170.B)}}func TestL4Colormap_MonotonicRed(t *testing.T) {// Red channel should be non-decreasing (black→red→orange→yellow)for i := 1; i < 256; i++ {if L4Colormap[i].R < L4Colormap[i-1].R {t.Errorf("R decreased at index %d: %d → %d", i-1, L4Colormap[i-1].R, L4Colormap[i].R)}}}func TestL4Colormap_GreenZeroThenIncreasing(t *testing.T) {// Green is 0 in the first segment (indices 0-85), then increasesfor i := 0; i <= 85; i++ {if L4Colormap[i].G != 0 {t.Errorf("G should be 0 at index %d, got %d", i, L4Colormap[i].G)}}// Green should be non-decreasing after index 85for i := 86; i < 256; i++ {if L4Colormap[i].G < L4Colormap[i-1].G {t.Errorf("G decreased at index %d: %d → %d", i-1, L4Colormap[i-1].G, L4Colormap[i].G)}}}func TestL4Colormap_BlueAlwaysZero(t *testing.T) {for i := range 256 {if L4Colormap[i].B != 0 {t.Errorf("B should be 0 at index %d, got %d", i, L4Colormap[i].B)}}}func TestApplyL4Colormap_Basic(t *testing.T) {gray := [][]uint8{{0, 128, 255},}result := ApplyL4Colormap(gray)if len(result) != 1 || len(result[0]) != 3 {t.Fatalf("shape mismatch: got %dx%d", len(result), len(result[0]))}// Index 0 → blackif result[0][0] != (RGBPixel{0, 0, 0}) {t.Errorf("pixel 0: got %v, want black", result[0][0])}// Index 255 → yellowif result[0][2] != (RGBPixel{255, 255, 0}) {t.Errorf("pixel 255: got %v, want yellow", result[0][2])}// Index 128 → should be a valid colormap entry (just check it matches the table)if result[0][1] != L4Colormap[128] {t.Errorf("pixel 128: got %v, want %v", result[0][1], L4Colormap[128])}}func TestApplyL4Colormap_MultiRow(t *testing.T) {gray := [][]uint8{{0, 255},{255, 0},}result := ApplyL4Colormap(gray)if len(result) != 2 || len(result[0]) != 2 {t.Fatalf("shape mismatch: got %dx%d", len(result), len(result[0]))}if result[0][0] != L4Colormap[0] {t.Errorf("(0,0): got %v, want %v", result[0][0], L4Colormap[0])}if result[1][1] != L4Colormap[0] {t.Errorf("(1,1): got %v, want %v", result[1][1], L4Colormap[0])}}func TestApplyL4Colormap_Empty(t *testing.T) {tests := []struct {name stringdata [][]uint8}{{"nil", nil},{"empty outer", [][]uint8{}},{"empty inner", [][]uint8{{}}},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {result := ApplyL4Colormap(tt.data)if result != nil {t.Errorf("expected nil for %s, got %v", tt.name, result)}})}}
func TestParseFinalClipMode(t *testing.T) {tests := []struct {input stringwant FinalClipModeerr bool}{{"none", FinalClipNone, false},{"", FinalClipNone, false},{"remainder", FinalClipRemainder, false},{"full", FinalClipFull, false},{"extend", FinalClipExtend, false},{"invalid", 0, true},{"FULL", 0, true}, // case-sensitive}for _, tt := range tests {t.Run(tt.input, func(t *testing.T) {got, err := ParseFinalClipMode(tt.input)if tt.err {if err == nil {t.Error("expected error")}} else {if err != nil {t.Errorf("unexpected error: %v", err)}if got != tt.want {t.Errorf("got %d, want %d", got, tt.want)}}})}}
package utilsimport ("database/sql""errors""testing")// mockDB implements the DB interface for testingtype mockDB struct {queryRowFunc func(query string, args ...any) *sql.Row}func (m *mockDB) Query(query string, args ...any) (*sql.Rows, error) {return nil, errors.New("not implemented")}func (m *mockDB) QueryRow(query string, args ...any) *sql.Row {return m.queryRowFunc(query, args...)}// rowScanner wraps a func to satisfy *sql.Row Scan behavior via a helper.// Since *sql.Row can't be constructed directly, we use sql.Open with an// in-memory driver pattern isn't feasible here. Instead, we use a real// test database or a simple approach with a lightweight SQLite/DuckDB.//// However, for simplicity we test CheckDuplicateHash by verifying the// logic paths through a lightweight mock that uses a real *sql.DB with// an in-memory DuckDB.func TestCheckDuplicateHash_NoRows(t *testing.T) {db := openTestDB(t)defer db.Close()// No rows exist — should return not-duplicateid, dup, err := CheckDuplicateHash(db, "abcdef0123456789")if err != nil {t.Fatalf("unexpected error: %v", err)}if dup {t.Error("expected isDuplicate=false when no rows")}if id != "" {t.Errorf("expected empty id, got %q", id)}}func TestCheckDuplicateHash_FoundDuplicate(t *testing.T) {db := openTestDB(t)defer db.Close()// Insert a file with known hashhash := "deadbeef12345678"fileID := "test_file_id_123"_, err := db.Exec(`INSERT INTO file (id, path, dataset_id, xxh64_hash, active)VALUES (?, '/test/file.wav', 'ds1', ?, true)`, fileID, hash)if err != nil {t.Fatalf("insert: %v", err)}id, dup, err := CheckDuplicateHash(db, hash)if err != nil {t.Fatalf("unexpected error: %v", err)}if !dup {t.Error("expected isDuplicate=true")}if id != fileID {t.Errorf("expected id=%q, got %q", fileID, id)}}func TestCheckDuplicateHash_InactiveNotDuplicate(t *testing.T) {db := openTestDB(t)defer db.Close()// Insert an INACTIVE file with known hashhash := "cafebeef12345678"fileID := "inactive_file_id"_, err := db.Exec(`INSERT INTO file (id, path, dataset_id, xxh64_hash, active)VALUES (?, '/test/old.wav', 'ds1', ?, false)`, fileID, hash)if err != nil {t.Fatalf("insert: %v", err)}// Inactive files should NOT be considered duplicatesid, dup, err := CheckDuplicateHash(db, hash)if err != nil {t.Fatalf("unexpected error: %v", err)}if dup {t.Error("expected isDuplicate=false for inactive file")}if id != "" {t.Errorf("expected empty id, got %q", id)}}func TestCheckDuplicateHash_DifferentHashNoDuplicate(t *testing.T) {db := openTestDB(t)defer db.Close()// Insert file with hash A_, err := db.Exec(`INSERT INTO file (id, path, dataset_id, xxh64_hash, active)VALUES ('id1', '/test/a.wav', 'ds1', 'hash_aaaa', true)`)if err != nil {t.Fatalf("insert: %v", err)}// Query for hash B — no duplicateid, dup, err := CheckDuplicateHash(db, "hash_bbbb")if err != nil {t.Fatalf("unexpected error: %v", err)}if dup {t.Error("expected isDuplicate=false for different hash")}if id != "" {t.Errorf("expected empty id, got %q", id)}}// openTestDB creates a DuckDB in-memory database with the minimal schema// needed for the file table.func openTestDB(t *testing.T) *sql.DB {t.Helper()db, err := sql.Open("duckdb", "")if err != nil {t.Fatalf("open duckdb: %v", err)}_, err = db.Exec(`CREATE TABLE file (id VARCHAR PRIMARY KEY,path VARCHAR,dataset_id VARCHAR,xxh64_hash VARCHAR,active BOOLEAN DEFAULT true)`)if err != nil {db.Close()t.Fatalf("create table: %v", err)}return db}