fixed tests with cyclo over 15

quietlight
May 4, 2026, 9:43 PM
HYCZTLSZ5WVJFMP4EPVHAVWRYSYNCIBJ62LLBNO4IRVEBY7WJI6QC

Dependencies

Change contents

  • edit in utils/wav_metadata_test.go at line 757
    [4.22247]
    [4.22247]
    // assertWAVHeader creates a WAV file and verifies ParseWAVHeaderMinimal returns expected values.
    func assertWAVHeader(t *testing.T, tmpDir, filename string, wantSR int, wantDur float64, opts struct {
    duration float64
    sampleRate int
    channels int
    bitsPerSample int
    comment string
    artist string
    }) {
    t.Helper()
    path := createTestWAVFile(t, tmpDir, filename, opts)
    sr, dur, err := ParseWAVHeaderMinimal(path)
    if err != nil {
    t.Fatalf("Failed to parse WAV header: %v", err)
    }
    if sr != wantSR {
    t.Errorf("SampleRate: got %d, want %d", sr, wantSR)
    }
    if dur < wantDur-0.1 || dur > wantDur+0.1 {
    t.Errorf("Duration: got %f, want ~%f", dur, wantDur)
    }
    }
  • replacement in utils/wav_metadata_test.go at line 783
    [4.22318][4.22318:22449]()
    t.Run("should parse basic WAV metadata", func(t *testing.T) {
    path := createTestWAVFile(t, tmpDir, "test_minimal.wav", struct {
    [4.22318]
    [4.22449]
    t.Run("basic", func(t *testing.T) {
    assertWAVHeader(t, tmpDir, "test_minimal.wav", 44100, 10.0, struct {
  • replacement in utils/wav_metadata_test.go at line 791
    [4.22585][4.22585:23073]()
    }{
    duration: 10.0,
    sampleRate: 44100,
    channels: 1,
    bitsPerSample: 16,
    comment: "",
    artist: "",
    })
    sampleRate, duration, err := ParseWAVHeaderMinimal(path)
    if err != nil {
    t.Fatalf("Failed to parse WAV header: %v", err)
    }
    if sampleRate != 44100 {
    t.Errorf("SampleRate incorrect: got %d, want 44100", sampleRate)
    }
    if duration < 9.9 || duration > 10.1 {
    t.Errorf("Duration incorrect: got %f, want ~10.0", duration)
    }
    [4.22585]
    [4.23073]
    }{10.0, 44100, 1, 16, "", ""})
  • replacement in utils/wav_metadata_test.go at line 794
    [4.23078][4.23078:23239]()
    t.Run("should handle different sample rates", func(t *testing.T) {
    sampleRates := []int{8000, 22050, 44100, 48000, 96000}
    for _, sr := range sampleRates {
    [4.23078]
    [4.23239]
    t.Run("sample_rates", func(t *testing.T) {
    for _, sr := range []int{8000, 22050, 44100, 48000, 96000} {
  • replacement in utils/wav_metadata_test.go at line 797
    [4.23294][4.23294:23379]()
    path := createTestWAVFile(t, tmpDir, fmt.Sprintf("test_sr_%d.wav", sr), struct {
    [4.23294]
    [4.23379]
    assertWAVHeader(t, tmpDir, fmt.Sprintf("test_sr_%d.wav", sr), sr, 5.0, struct {
  • replacement in utils/wav_metadata_test.go at line 804
    [4.23527][4.23527:24043]()
    }{
    duration: 5.0,
    sampleRate: sr,
    channels: 1,
    bitsPerSample: 16,
    comment: "",
    artist: "",
    })
    sampleRate, duration, err := ParseWAVHeaderMinimal(path)
    if err != nil {
    t.Fatalf("Failed to parse WAV header: %v", err)
    }
    if sampleRate != sr {
    t.Errorf("SampleRate incorrect: got %d, want %d", sampleRate, sr)
    }
    if duration < 4.9 || duration > 5.1 {
    t.Errorf("Duration incorrect: got %f, want ~5.0", duration)
    }
    [4.23527]
    [4.24043]
    }{5.0, sr, 1, 16, "", ""})
  • replacement in utils/wav_metadata_test.go at line 809
    [4.24058][4.24058:24183]()
    t.Run("should handle stereo files", func(t *testing.T) {
    path := createTestWAVFile(t, tmpDir, "test_stereo.wav", struct {
    [4.24058]
    [4.24183]
    t.Run("stereo", func(t *testing.T) {
    assertWAVHeader(t, tmpDir, "test_stereo.wav", 44100, 3.0, struct {
  • replacement in utils/wav_metadata_test.go at line 817
    [4.24319][4.24319:24804]()
    }{
    duration: 3.0,
    sampleRate: 44100,
    channels: 2,
    bitsPerSample: 16,
    comment: "",
    artist: "",
    })
    sampleRate, duration, err := ParseWAVHeaderMinimal(path)
    if err != nil {
    t.Fatalf("Failed to parse WAV header: %v", err)
    }
    if sampleRate != 44100 {
    t.Errorf("SampleRate incorrect: got %d, want 44100", sampleRate)
    }
    if duration < 2.9 || duration > 3.1 {
    t.Errorf("Duration incorrect: got %f, want ~3.0", duration)
    }
    [4.24319]
    [4.24804]
    }{3.0, 44100, 2, 16, "", ""})
  • replacement in utils/wav_metadata_test.go at line 820
    [4.24809][4.24809:24882]()
    t.Run("should return error for non-existent file", func(t *testing.T) {
    [4.24809]
    [4.24882]
    t.Run("nonexistent", func(t *testing.T) {
  • replacement in utils/wav_metadata_test.go at line 827
    [4.25022][4.25022:25114]()
    t.Run("should return error for non-WAV file", func(t *testing.T) {
    // Create a text file
    [4.25022]
    [4.25114]
    t.Run("non_wav", func(t *testing.T) {
  • edit in utils/wav_metadata_test.go at line 832
    [4.25293][4.25293:25294]()
  • edit in utils/terminal_image_test.go at line 267
    [4.17951]
    [4.17951]
    }
    }
    // assertRGBAPixel checks that the pixel at (x,y) in an RGBA image matches the expected RGBA values.
    func assertRGBAPixel(t *testing.T, rgba *image.RGBA, x, y int, wantR, wantG, wantB, wantA uint8) {
    t.Helper()
    off := y*rgba.Stride + x*4
    got := rgba.Pix[off : off+4]
    if got[0] != wantR || got[1] != wantG || got[2] != wantB || got[3] != wantA {
    t.Errorf("pixel (%d,%d) = [%d,%d,%d,%d], want [%d,%d,%d,%d]",
    x, y, got[0], got[1], got[2], got[3], wantR, wantG, wantB, wantA)
  • replacement in utils/terminal_image_test.go at line 300
    [4.18410][4.18410:18998]()
    // 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 1
    off := rgba.Stride
    if 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])
    }
    [4.18410]
    [4.56362]
    assertRGBAPixel(t, rgba, 0, 0, 255, 0, 0, 255) // red
    assertRGBAPixel(t, rgba, 1, 0, 0, 255, 0, 255) // green
    assertRGBAPixel(t, rgba, 0, 1, 0, 0, 255, 255) // blue
  • edit in utils/file_import_test.go at line 8
    [4.130957]
    [4.130957]
    // mustGenerateID is a test helper that calls GenerateLongID and fatals on error.
    func mustGenerateID(t *testing.T) string {
    t.Helper()
    id, err := GenerateLongID()
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    return id
    }
    // isValidAlphabetChar checks if c is in the nanoid default alphabet (0-9, A-Z, a-z, _, -).
    func isValidAlphabetChar(c rune) bool {
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '-'
    }
  • replacement in utils/file_import_test.go at line 26
    [4.131055][4.131055:131148]()
    id, err := GenerateLongID()
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    [4.131055]
    [4.131148]
    id := mustGenerateID(t)
  • replacement in utils/file_import_test.go at line 33
    [4.131304][4.131304:131456]()
    id, err := GenerateLongID()
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    // Default nanoid alphabet includes: 0-9, A-Z, a-z, _, -
    [4.131304]
    [4.131456]
    id := mustGenerateID(t)
  • replacement in utils/file_import_test.go at line 35
    [4.131481][4.131481:131582]()
    if (c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && c != '_' && c != '-' {
    [4.131481]
    [4.131582]
    if !isValidAlphabetChar(c) {
  • replacement in utils/file_import_test.go at line 44
    [4.131759][4.131759:131856]()
    id, err := GenerateLongID()
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    [4.131759]
    [4.131856]
    id := mustGenerateID(t)
  • edit in utils/file_import_test.go at line 51
    [4.131952]
    [4.131952]
    }
    // mustResolveTimestamp is a test helper that calls ResolveTimestamp and fatals on error.
    func mustResolveTimestamp(t *testing.T, meta *WAVMetadata, filename, tz string, useModTime bool, preParsed *time.Time) *TimestampResult {
    t.Helper()
    result, err := ResolveTimestamp(meta, filename, tz, useModTime, preParsed)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    return result
  • replacement in utils/file_import_test.go at line 69
    [4.132261][2.8387:8482](),[2.8482][4.132351:132414](),[4.132351][4.132351:132414]()
    result, err := ResolveTimestamp(meta, "20250224_210000.wav", "Pacific/Auckland", false, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    [4.132261]
    [4.132414]
    result := mustResolveTimestamp(t, meta, "20250224_210000.wav", "Pacific/Auckland", false, nil)
  • edit in utils/file_import_test.go at line 76
    [4.132571][4.132571:132621]()
    // AudioMoth parser returns UTC+13 fixed offset
  • replacement in utils/file_import_test.go at line 83
    [4.132892][4.132892:132952](),[4.132952][2.8483:8578](),[2.8578][4.133042:133105](),[4.133042][4.133042:133105]()
    meta := &WAVMetadata{
    Comment: "",
    Artist: "",
    }
    result, err := ResolveTimestamp(meta, "20250224_210000.wav", "Pacific/Auckland", false, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    [4.132892]
    [4.133105]
    result := mustResolveTimestamp(t, &WAVMetadata{}, "20250224_210000.wav", "Pacific/Auckland", false, nil)
  • replacement in utils/file_import_test.go at line 94
    [4.133398][4.133398:133491](),[4.133491][2.8579:8667](),[2.8667][4.133574:133637](),[4.133574][4.133574:133637]()
    meta := &WAVMetadata{
    Comment: "",
    Artist: "",
    FileModTime: modTime,
    }
    result, err := ResolveTimestamp(meta, "nopattern.wav", "Pacific/Auckland", true, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    [4.133398]
    [4.133637]
    meta := &WAVMetadata{FileModTime: modTime}
    result := mustResolveTimestamp(t, meta, "nopattern.wav", "Pacific/Auckland", true, nil)
  • replacement in utils/file_import_test.go at line 101
    [4.133758][4.133758:133907]()
    t.Run("errors when no timestamp available and file mod time disabled", func(t *testing.T) {
    meta := &WAVMetadata{
    Comment: "",
    Artist: "",
    [4.133758]
    [4.133907]
    t.Run("errors_on_no_timestamp", func(t *testing.T) {
    cases := []struct {
    name string
    useModTime bool
    }{
    {"mod_time_disabled", false},
    {"no_file_mod_time", true},
  • replacement in utils/file_import_test.go at line 109
    [4.133911][2.8668:8752](),[2.8752][4.133990:134065](),[4.133990][4.133990:134065]()
    _, err := ResolveTimestamp(meta, "nopattern.wav", "Pacific/Auckland", false, nil)
    if err == nil {
    t.Error("expected error when no timestamp available")
    [4.133911]
    [4.134065]
    for _, tc := range cases {
    t.Run(tc.name, func(t *testing.T) {
    _, err := ResolveTimestamp(&WAVMetadata{}, "nopattern.wav", "Pacific/Auckland", tc.useModTime, nil)
    if err == nil {
    t.Error("expected error when no timestamp available")
    }
    })
  • edit in utils/file_import_test.go at line 119
    [4.134074][4.134074:134221](),[4.134221][2.8753:8836](),[2.8836][4.134299:134383](),[4.134299][4.134299:134383]()
    t.Run("errors when no timestamp available and no file mod time", func(t *testing.T) {
    meta := &WAVMetadata{
    Comment: "",
    Artist: "",
    }
    _, err := ResolveTimestamp(meta, "nopattern.wav", "Pacific/Auckland", true, nil)
    if err == nil {
    t.Error("expected error when no timestamp available")
    }
    })
  • replacement in utils/file_import_test.go at line 120
    [4.134472][4.134472:134554](),[4.134554][2.8837:8932](),[2.8932][4.134644:134707](),[4.134644][4.134644:134707]()
    meta := &WAVMetadata{
    Comment: "AudioMoth garbage data",
    Artist: "",
    }
    result, err := ResolveTimestamp(meta, "20250224_210000.wav", "Pacific/Auckland", false, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    [4.134472]
    [4.134707]
    meta := &WAVMetadata{Comment: "AudioMoth garbage data"}
    result := mustResolveTimestamp(t, meta, "20250224_210000.wav", "Pacific/Auckland", false, nil)
  • edit in tools/integration_test.go at line 11
    [4.304081][4.304081:304110]()
    // Setup: Use test database
  • edit in tools/integration_test.go at line 16
    [4.304287][4.304287:304288]()
  • edit in tools/integration_test.go at line 18
    [4.304318][4.304318:304367]()
    // First, verify we can query existing patterns
  • replacement in tools/integration_test.go at line 19
    [4.304420][4.304420:304955]()
    input := ExecuteSQLInput{
    Query: "SELECT id, record_s, sleep_s FROM cyclic_recording_pattern WHERE active = true ORDER BY record_s, sleep_s",
    }
    output, err := ExecuteSQL(ctx, input)
    if err != nil {
    t.Fatalf("Failed to query patterns: %v", err)
    }
    if len(output.Rows) == 0 {
    t.Fatal("Expected at least one pattern")
    }
    t.Logf("Found %d patterns", len(output.Rows))
    for i, row := range output.Rows {
    t.Logf("Pattern %d: ID=%v, record_s=%v, sleep_s=%v", i+1, row["id"], row["record_s"], row["sleep_s"])
    }
    [4.304420]
    [4.304955]
    testQueryExistingPatterns(t, ctx)
  • edit in tools/integration_test.go at line 22
    [4.304960][4.304960:305007]()
    // Create a cluster using an existing pattern
  • replacement in tools/integration_test.go at line 23
    [4.305071][4.305071:305431]()
    // First, find a valid dataset and location
    datasetSQL := ExecuteSQLInput{
    Query: "SELECT id FROM dataset WHERE active = true LIMIT 1",
    }
    datasetOutput, err := ExecuteSQL(ctx, datasetSQL)
    if err != nil || len(datasetOutput.Rows) == 0 {
    t.Skip("No active datasets found in test database")
    }
    datasetID := datasetOutput.Rows[0]["id"].(string)
    [4.305071]
    [4.305431]
    testCreateClusterWithPattern(t, ctx)
    })
    }
  • replacement in tools/integration_test.go at line 27
    [4.305432][4.305432:306312]()
    locationSQL := ExecuteSQLInput{
    Query: "SELECT id FROM location WHERE dataset_id = ? AND active = true LIMIT 1",
    Parameters: []any{datasetID},
    }
    locationOutput, err := ExecuteSQL(ctx, locationSQL)
    if err != nil || len(locationOutput.Rows) == 0 {
    t.Skip("No active locations found in test database")
    }
    locationID := locationOutput.Rows[0]["id"].(string)
    t.Logf("Using dataset: %s, location: %s", datasetID, locationID)
    sampleRate := 16000
    input := ClusterInput{
    DatasetID: &datasetID,
    LocationID: &locationID,
    Name: new("Integration Test Cluster"),
    SampleRate: &sampleRate,
    CyclicRecordingPatternID: new("IBv_KxDGsNQs"), // 60s/1740s pattern
    }
    output, err := CreateOrUpdateCluster(ctx, input)
    if err != nil {
    t.Fatalf("Failed to create cluster: %v", err)
    }
    [4.305432]
    [4.306312]
    func testQueryExistingPatterns(t *testing.T, ctx context.Context) {
    t.Helper()
    input := ExecuteSQLInput{
    Query: "SELECT id, record_s, sleep_s FROM cyclic_recording_pattern WHERE active = true ORDER BY record_s, sleep_s",
    }
  • replacement in tools/integration_test.go at line 33
    [4.306313][4.306313:306412]()
    clusterID := output.Cluster.ID
    t.Logf("Created cluster: %s with pattern reference", clusterID)
    [4.306313]
    [4.306412]
    output, err := ExecuteSQL(ctx, input)
    if err != nil {
    t.Fatalf("Failed to query patterns: %v", err)
    }
  • replacement in tools/integration_test.go at line 38
    [4.306413][4.306413:306724]()
    // Verify the cluster has the pattern reference
    sqlInput := ExecuteSQLInput{
    Query: "SELECT c.name, c.cyclic_recording_pattern_id, p.record_s, p.sleep_s FROM cluster c LEFT JOIN cyclic_recording_pattern p ON c.cyclic_recording_pattern_id = p.id WHERE c.id = ?",
    Parameters: []any{clusterID},
    }
    [4.306413]
    [4.306724]
    if len(output.Rows) == 0 {
    t.Fatal("Expected at least one pattern")
    }
  • replacement in tools/integration_test.go at line 42
    [4.306725][4.306725:306842]()
    sqlOutput, err := ExecuteSQL(ctx, sqlInput)
    if err != nil {
    t.Fatalf("Failed to verify cluster: %v", err)
    }
    [4.306725]
    [4.306842]
    t.Logf("Found %d patterns", len(output.Rows))
    for i, row := range output.Rows {
    t.Logf("Pattern %d: ID=%v, record_s=%v, sleep_s=%v", i+1, row["id"], row["record_s"], row["sleep_s"])
    }
    }
  • replacement in tools/integration_test.go at line 48
    [4.306843][4.306843:306938]()
    if len(sqlOutput.Rows) != 1 {
    t.Fatalf("Expected 1 row, got %d", len(sqlOutput.Rows))
    }
    [4.306843]
    [4.306938]
    func testCreateClusterWithPattern(t *testing.T, ctx context.Context) {
    t.Helper()
    // Find a valid dataset
    datasetOutput, err := ExecuteSQL(ctx, ExecuteSQLInput{
    Query: "SELECT id FROM dataset WHERE active = true LIMIT 1",
    })
    if err != nil || len(datasetOutput.Rows) == 0 {
    t.Skip("No active datasets found in test database")
    }
    datasetID := datasetOutput.Rows[0]["id"].(string)
  • replacement in tools/integration_test.go at line 59
    [4.306939][4.306939:306966]()
    row := sqlOutput.Rows[0]
    [4.306939]
    [4.306966]
    // Find a valid location
    locationOutput, err := ExecuteSQL(ctx, ExecuteSQLInput{
    Query: "SELECT id FROM location WHERE dataset_id = ? AND active = true LIMIT 1",
    Parameters: []any{datasetID},
    })
    if err != nil || len(locationOutput.Rows) == 0 {
    t.Skip("No active locations found in test database")
    }
    locationID := locationOutput.Rows[0]["id"].(string)
  • replacement in tools/integration_test.go at line 69
    [4.306967][4.306967:306998]()
    t.Logf("Row data: %+v", row)
    [4.306967]
    [4.306998]
    t.Logf("Using dataset: %s, location: %s", datasetID, locationID)
  • replacement in tools/integration_test.go at line 71
    [4.306999][4.306999:307194]()
    // Check the pattern ID
    patternIDStr := row["cyclic_recording_pattern_id"]
    if patternIDStr != "IBv_KxDGsNQs" {
    t.Errorf("Expected pattern ID 'IBv_KxDGsNQs', got '%v'", patternIDStr)
    }
    [4.306999]
    [4.307194]
    sampleRate := 16000
    output, err := CreateOrUpdateCluster(ctx, ClusterInput{
    DatasetID: &datasetID,
    LocationID: &locationID,
    Name: new("Integration Test Cluster"),
    SampleRate: &sampleRate,
    CyclicRecordingPatternID: new("IBv_KxDGsNQs"),
    })
    if err != nil {
    t.Fatalf("Failed to create cluster: %v", err)
    }
    clusterID := output.Cluster.ID
    t.Logf("Created cluster: %s with pattern reference", clusterID)
  • replacement in tools/integration_test.go at line 85
    [4.307195][4.307195:307289]()
    // Check record_s and sleep_s
    recordSVal := row["record_s"]
    sleepSVal := row["sleep_s"]
    [4.307195]
    [4.307289]
    // Verify the cluster has the pattern reference
    sqlOutput, err := ExecuteSQL(ctx, ExecuteSQLInput{
    Query: "SELECT c.name, c.cyclic_recording_pattern_id, p.record_s, p.sleep_s FROM cluster c LEFT JOIN cyclic_recording_pattern p ON c.cyclic_recording_pattern_id = p.id WHERE c.id = ?",
    Parameters: []any{clusterID},
    })
    if err != nil {
    t.Fatalf("Failed to verify cluster: %v", err)
    }
    if len(sqlOutput.Rows) != 1 {
    t.Fatalf("Expected 1 row, got %d", len(sqlOutput.Rows))
    }
  • replacement in tools/integration_test.go at line 97
    [4.307290][4.307290:307421]()
    t.Logf("✓ Verified cluster has correct pattern reference: ID=%v, record=%v, sleep=%v",
    patternIDStr, recordSVal, sleepSVal)
    [4.307290]
    [4.307421]
    row := sqlOutput.Rows[0]
    t.Logf("Row data: %+v", row)
  • replacement in tools/integration_test.go at line 100
    [4.307422][4.307422:307629]()
    if patternIDStr == nil || patternIDStr == "" {
    t.Error("Pattern ID is empty")
    }
    if recordSVal == nil {
    t.Error("record_s is nil")
    }
    if sleepSVal == nil {
    t.Error("sleep_s is nil")
    }
    })
    [4.307422]
    [4.307629]
    if row["cyclic_recording_pattern_id"] != "IBv_KxDGsNQs" {
    t.Errorf("Expected pattern ID 'IBv_KxDGsNQs', got '%v'", row["cyclic_recording_pattern_id"])
    }
    if row["cyclic_recording_pattern_id"] == nil || row["cyclic_recording_pattern_id"] == "" {
    t.Error("Pattern ID is empty")
    }
    if row["record_s"] == nil {
    t.Error("record_s is nil")
    }
    if row["sleep_s"] == nil {
    t.Error("sleep_s is nil")
    }
  • edit in tools/calls_propagate_test.go at line 539
    [4.425872]
    [4.425872]
    }
    // assertPropagateStats checks output stats against expected values.
    func assertPropagateStats(t *testing.T, got, want CallsPropagateFolderOutput) {
    t.Helper()
    checks := []struct {
    name string
    got int
    want int
    }{
    {"FilesTotal", got.FilesTotal, want.FilesTotal},
    {"FilesWithBothFilters", got.FilesWithBothFilters, want.FilesWithBothFilters},
    {"FilesSkippedNoFilter", got.FilesSkippedNoFilter, want.FilesSkippedNoFilter},
    {"FilesChanged", got.FilesChanged, want.FilesChanged},
    {"FilesErrored", got.FilesErrored, want.FilesErrored},
    {"TargetsExamined", got.TargetsExamined, want.TargetsExamined},
    {"Propagated", got.Propagated, want.Propagated},
    {"SkippedNoOverlap", got.SkippedNoOverlap, want.SkippedNoOverlap},
    }
    for _, c := range checks {
    if c.got != c.want {
    t.Errorf("%s: got %d, want %d", c.name, c.got, c.want)
    }
    }
  • replacement in tools/calls_propagate_test.go at line 594
    [4.426917][4.426917:427710]()
    if out.FilesTotal != 4 {
    t.Errorf("FilesTotal: got %d, want 4", out.FilesTotal)
    }
    if out.FilesWithBothFilters != 2 {
    t.Errorf("FilesWithBothFilters: got %d, want 2", out.FilesWithBothFilters)
    }
    if out.FilesSkippedNoFilter != 2 {
    t.Errorf("FilesSkippedNoFilter: got %d, want 2", out.FilesSkippedNoFilter)
    }
    if out.FilesChanged != 1 {
    t.Errorf("FilesChanged: got %d, want 1", out.FilesChanged)
    }
    if out.FilesErrored != 0 {
    t.Errorf("FilesErrored: got %d, want 0", out.FilesErrored)
    }
    if out.TargetsExamined != 2 {
    t.Errorf("TargetsExamined: got %d, want 2", out.TargetsExamined)
    }
    if out.Propagated != 1 {
    t.Errorf("Propagated: got %d, want 1", out.Propagated)
    }
    if out.SkippedNoOverlap != 1 {
    t.Errorf("SkippedNoOverlap: got %d, want 1", out.SkippedNoOverlap)
    }
    [4.426917]
    [4.427710]
    assertPropagateStats(t, out, CallsPropagateFolderOutput{
    FilesTotal: 4,
    FilesWithBothFilters: 2,
    FilesSkippedNoFilter: 2,
    FilesChanged: 1,
    FilesErrored: 0,
    TargetsExamined: 2,
    Propagated: 1,
    SkippedNoOverlap: 1,
    })
  • replacement in tools/calls_propagate_test.go at line 605
    [4.427711][4.427711:428073]()
    // File A was changed; check on-disk state.
    aDf := readFile(t, aPath)
    if aDf.Meta.Reviewer != "Skraak" {
    t.Errorf("a.wav.data reviewer: got %q, want Skraak", aDf.Meta.Reviewer)
    }
    if l := findLabel(aDf, fTo, 100, 125); l == nil || l.Certainty != 90 || l.CallType != "Male" {
    t.Errorf("a.wav.data target label: got %+v, want cert=90 calltype=Male", l)
    }
    [4.427711]
    [4.428073]
    t.Run("file_a_propagated", func(t *testing.T) {
    aDf := readFile(t, aPath)
    if aDf.Meta.Reviewer != "Skraak" {
    t.Errorf("reviewer: got %q, want Skraak", aDf.Meta.Reviewer)
    }
    if l := findLabel(aDf, fTo, 100, 125); l == nil || l.Certainty != 90 || l.CallType != "Male" {
    t.Errorf("target label: got %+v, want cert=90 calltype=Male", l)
    }
    })
  • replacement in tools/calls_propagate_test.go at line 615
    [4.428074][4.428074:428269]()
    // File B was skipped — reviewer untouched.
    bDf := readFile(t, bPath)
    if bDf.Meta.Reviewer != "David" {
    t.Errorf("b.wav.data reviewer should not be touched, got %q", bDf.Meta.Reviewer)
    }
    [4.428074]
    [4.428269]
    t.Run("file_b_skipped", func(t *testing.T) {
    bDf := readFile(t, bPath)
    if bDf.Meta.Reviewer != "David" {
    t.Errorf("reviewer should not be touched, got %q", bDf.Meta.Reviewer)
    }
    })
  • replacement in tools/calls_propagate_test.go at line 622
    [4.428270][4.428270:428643]()
    // File D had no overlap — reviewer untouched, target still cert=70.
    dDf := readFile(t, dPath)
    if dDf.Meta.Reviewer != "David" {
    t.Errorf("d.wav.data reviewer should not be touched, got %q", dDf.Meta.Reviewer)
    }
    if l := findLabel(dDf, fTo, 500, 525); l == nil || l.Certainty != 70 {
    t.Errorf("d.wav.data target label should be unchanged cert=70, got %+v", l)
    }
    [4.428270]
    [4.428643]
    t.Run("file_d_no_overlap", func(t *testing.T) {
    dDf := readFile(t, dPath)
    if dDf.Meta.Reviewer != "David" {
    t.Errorf("reviewer should not be touched, got %q", dDf.Meta.Reviewer)
    }
    if l := findLabel(dDf, fTo, 500, 525); l == nil || l.Certainty != 70 {
    t.Errorf("target label should be unchanged cert=70, got %+v", l)
    }
    })
  • replacement in tools/calls_classify_load_test.go at line 9
    [4.590731][4.590731:591097]()
    func TestLoadDataFilesFiltersFilesWithNoMatchingSegments(t *testing.T) {
    // Create a temp directory with test .data files
    tempDir := t.TempDir()
    // File 1: Kiwi segments
    file1 := `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 90}]]]`
    if err := os.WriteFile(filepath.Join(tempDir, "file1.data"), []byte(file1), 0644); err != nil {
    [4.590731]
    [4.591097]
    // writeDataFileContent creates a .data file in dir with the given raw content.
    func writeDataFileContent(t *testing.T, dir, name, content string) {
    t.Helper()
    if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0644); err != nil {
  • edit in tools/calls_classify_load_test.go at line 15
    [4.591115]
    [4.591115]
    }
  • replacement in tools/calls_classify_load_test.go at line 17
    [4.591116][4.591116:591745]()
    // File 2: Tomtit segments only
    file2 := `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Tomtit", "certainty": 90}]]]`
    if err := os.WriteFile(filepath.Join(tempDir, "file2.data"), []byte(file2), 0644); err != nil {
    t.Fatal(err)
    }
    // File 3: Kiwi segments
    file3 := `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 90}]]]`
    if err := os.WriteFile(filepath.Join(tempDir, "file3.data"), []byte(file3), 0644); err != nil {
    t.Fatal(err)
    }
    // Test 1: No filter - should load all 3 files
    config1 := ClassifyConfig{Folder: tempDir, Certainty: -1}
    state1, err := LoadDataFiles(config1)
    [4.591116]
    [4.591745]
    // mustLoadDataFiles is a test helper that calls LoadDataFiles and fatals on error.
    func mustLoadDataFiles(t *testing.T, config ClassifyConfig) *ClassifyState {
    t.Helper()
    state, err := LoadDataFiles(config)
  • edit in tools/calls_classify_load_test.go at line 23
    [4.591777][4.591777:592006]()
    }
    if len(state1.DataFiles) != 3 {
    t.Errorf("No filter: expected 3 files, got %d", len(state1.DataFiles))
    }
    if state1.TotalSegments() != 3 {
    t.Errorf("No filter: expected 3 segments total, got %d", state1.TotalSegments())
  • edit in tools/calls_classify_load_test.go at line 24
    [4.592009]
    [4.592009]
    return state
    }
  • replacement in tools/calls_classify_load_test.go at line 27
    [4.592010][4.592010:592227]()
    // Test 2: Filter by Species "Kiwi" - should load only files 1 and 3
    config2 := ClassifyConfig{Folder: tempDir, Species: "Kiwi", Certainty: -1}
    state2, err := LoadDataFiles(config2)
    if err != nil {
    t.Fatal(err)
    [4.592010]
    [4.592227]
    // assertFileSegCounts checks file count and total segment count match expected values.
    func assertFileSegCounts(t *testing.T, state *ClassifyState, wantFiles, wantSegs int, label string) {
    t.Helper()
    if len(state.DataFiles) != wantFiles {
    t.Errorf("%s: expected %d files, got %d", label, wantFiles, len(state.DataFiles))
  • replacement in tools/calls_classify_load_test.go at line 33
    [4.592230][4.592230:592339]()
    if len(state2.DataFiles) != 2 {
    t.Errorf("Species=Kiwi: expected 2 files, got %d", len(state2.DataFiles))
    [4.592230]
    [4.592339]
    if state.TotalSegments() != wantSegs {
    t.Errorf("%s: expected %d segments total, got %d", label, wantSegs, state.TotalSegments())
  • replacement in tools/calls_classify_load_test.go at line 36
    [4.592342][4.592342:592465]()
    if state2.TotalSegments() != 2 {
    t.Errorf("Species=Kiwi: expected 2 segments total, got %d", state2.TotalSegments())
    }
    [4.592342]
    [4.592465]
    }
    const (
    kiwiSeg = `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 90}]]]`
    tomtitSeg = `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Tomtit", "certainty": 90}]]]`
    )
    func TestLoadDataFilesFiltersFilesWithNoMatchingSegments(t *testing.T) {
    tempDir := t.TempDir()
    writeDataFileContent(t, tempDir, "file1.data", kiwiSeg)
    writeDataFileContent(t, tempDir, "file2.data", tomtitSeg)
    writeDataFileContent(t, tempDir, "file3.data", kiwiSeg)
    t.Run("no_filter", func(t *testing.T) {
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Certainty: -1})
    assertFileSegCounts(t, state, 3, 3, "No filter")
    })
    t.Run("species_kiwi", func(t *testing.T) {
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Species: "Kiwi", Certainty: -1})
    assertFileSegCounts(t, state, 2, 2, "Species=Kiwi")
    })
  • replacement in tools/calls_classify_load_test.go at line 60
    [4.592466][4.592466:592920]()
    // Test 3: Filter by Species "Tomtit" - should load only file 2
    config3 := ClassifyConfig{Folder: tempDir, Species: "Tomtit", Certainty: -1}
    state3, err := LoadDataFiles(config3)
    if err != nil {
    t.Fatal(err)
    }
    if len(state3.DataFiles) != 1 {
    t.Errorf("Species=Tomtit: expected 1 file, got %d", len(state3.DataFiles))
    }
    if state3.TotalSegments() != 1 {
    t.Errorf("Species=Tomtit: expected 1 segment total, got %d", state3.TotalSegments())
    }
    [4.592466]
    [4.592920]
    t.Run("species_tomtit", func(t *testing.T) {
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Species: "Tomtit", Certainty: -1})
    assertFileSegCounts(t, state, 1, 1, "Species=Tomtit")
    })
  • replacement in tools/calls_classify_load_test.go at line 65
    [4.592921][4.592921:593502]()
    // Test 4: Filter by non-existent species - should return empty file list
    // (handled gracefully by caller in cmd/calls_classify.go)
    config4 := ClassifyConfig{Folder: tempDir, Species: "NonExistent", Certainty: -1}
    state4, err := LoadDataFiles(config4)
    if err != nil {
    t.Fatalf("Species=NonExistent: unexpected error: %v", err)
    }
    if len(state4.DataFiles) != 0 {
    t.Errorf("Species=NonExistent: expected 0 files, got %d", len(state4.DataFiles))
    }
    if state4.TotalSegments() != 0 {
    t.Errorf("Species=NonExistent: expected 0 segments, got %d", state4.TotalSegments())
    }
    [4.592921]
    [4.593502]
    t.Run("species_nonexistent", func(t *testing.T) {
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Species: "NonExistent", Certainty: -1})
    assertFileSegCounts(t, state, 0, 0, "Species=NonExistent")
    })
  • edit in tools/calls_classify_load_test.go at line 72
    [4.593561][4.593561:593632]()
    // Create a temp directory with a file containing mixed segment types
  • edit in tools/calls_classify_load_test.go at line 74
    [4.593657][4.593657:593713]()
    // File with multiple segments: some Kiwi, some Tomtit
  • replacement in tools/calls_classify_load_test.go at line 80
    [4.593942][4.593942:594056]()
    if err := os.WriteFile(filepath.Join(tempDir, "mixed.data"), []byte(file), 0644); err != nil {
    t.Fatal(err)
    }
    [4.593942]
    [4.594056]
    writeDataFileContent(t, tempDir, "mixed.data", file)
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Species: "Kiwi", Certainty: -1})
  • edit in tools/calls_classify_load_test.go at line 84
    [4.594057][4.594057:594272]()
    // Filter by Species "Kiwi" - should show 2 segments from the file
    config := ClassifyConfig{Folder: tempDir, Species: "Kiwi", Certainty: -1}
    state, err := LoadDataFiles(config)
    if err != nil {
    t.Fatal(err)
    }
  • replacement in tools/calls_classify_load_test.go at line 112
    [4.595271][4.595271:595384]()
    if err := os.WriteFile(filepath.Join(tempDir, "test.data"), []byte(file), 0644); err != nil {
    t.Fatal(err)
    }
    [4.595271]
    [4.595384]
    writeDataFileContent(t, tempDir, "test.data", file)
  • replacement in tools/calls_classify_load_test.go at line 114
    [4.595385][4.595385:595532]()
    config := ClassifyConfig{Folder: tempDir, Species: "Kiwi", Certainty: -1}
    state, err := LoadDataFiles(config)
    if err != nil {
    t.Fatal(err)
    }
    [4.595385]
    [4.595532]
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Species: "Kiwi", Certainty: -1})
  • edit in tools/calls_classify_load_test.go at line 135
    [4.596143][4.596143:596193]()
    // Create a temp directory with test .data files
  • replacement in tools/calls_classify_load_test.go at line 137
    [4.596218][4.596218:596453]()
    // File 1: certainty 70
    file1 := `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 70}]]]`
    if err := os.WriteFile(filepath.Join(tempDir, "file1.data"), []byte(file1), 0644); err != nil {
    t.Fatal(err)
    }
    [4.596218]
    [4.596453]
    writeDataFileContent(t, tempDir, "file1.data", `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 70}]]]`)
    writeDataFileContent(t, tempDir, "file2.data", `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 100}]]]`)
  • replacement in tools/calls_classify_load_test.go at line 140
    [4.596454][4.596454:596691]()
    // File 2: certainty 100
    file2 := `[{"Operator": "test"}, [0, 10, 100, 1000, [{"species": "Kiwi", "certainty": 100}]]]`
    if err := os.WriteFile(filepath.Join(tempDir, "file2.data"), []byte(file2), 0644); err != nil {
    t.Fatal(err)
    }
    [4.596454]
    [4.596691]
    state := mustLoadDataFiles(t, ClassifyConfig{Folder: tempDir, Certainty: 100})
  • replacement in tools/calls_classify_load_test.go at line 142
    [4.596692][4.596692:597101]()
    // Filter by certainty 100 - should load only file2
    config := ClassifyConfig{Folder: tempDir, Certainty: 100}
    state, err := LoadDataFiles(config)
    if err != nil {
    t.Fatal(err)
    }
    if len(state.DataFiles) != 1 {
    t.Errorf("Certainty=100: expected 1 file, got %d", len(state.DataFiles))
    }
    if state.TotalSegments() != 1 {
    t.Errorf("Certainty=100: expected 1 segment, got %d", state.TotalSegments())
    }
    [4.596692]
    [4.597101]
    assertFileSegCounts(t, state, 1, 1, "Certainty=100")
  • replacement in lint_test.go at line 48
    [4.158][3.19651:19703]()
    cmd := exec.Command("gocyclo", "-over", "18", ".")
    [4.158]
    [4.210]
    cmd := exec.Command("gocyclo", "-over", "15", ".")