reduce complexity to under 14, gocyclo but cilint test still has 3 functions over

quietlight
May 13, 2026, 9:21 PM
ZCCQ4P5T2AMJAPBDWZVHXIUKLI5U2E5GNDXRCWXEOJQRWPSJJFEQC

Dependencies

Change contents

  • edit in utils/mapping_db_test.go at line 5
    [3.42]
    [3.42]
    "slices"
  • edit in utils/mapping_db_test.go at line 46
    [3.1692]
    [3.1692]
    }
    // assertStringSlice checks that got matches want (order-insensitive).
    func assertStringSlice(t *testing.T, label string, got, want []string) {
    t.Helper()
    if len(want) == 0 && len(got) == 0 {
    return
    }
    if len(got) != len(want) {
    t.Errorf("%s: got %v, want %v", label, got, want)
    return
    }
    for _, w := range want {
    found := slices.Contains(got, w)
    if !found {
    t.Errorf("%s: missing %q in %v", label, w, got)
    }
    }
  • replacement in utils/mapping_db_test.go at line 234
    [3.6919][3.6919:8963]()
    t.Run("valid mapping - no errors", func(t *testing.T) {
    mapping := MappingFile{
    "GSK": {Species: "Roroa", Calltypes: map[string]string{"brrr": "brrr"}},
    "K-M": {Species: "Kiwi"},
    }
    dataSpecies := map[string]bool{"GSK": true, "K-M": true}
    dataCT := map[string]map[string]bool{
    "GSK": {"brrr": true},
    "K-M": {"song": true},
    }
    result, err := ValidateMappingAgainstDB(db, mapping, dataSpecies, dataCT)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if result.HasErrors() {
    t.Errorf("expected no errors, got: %v", result)
    }
    })
    t.Run("missing species in mapping", func(t *testing.T) {
    mapping := MappingFile{
    "GSK": {Species: "Roroa"},
    }
    dataSpecies := map[string]bool{"GSK": true, "K-M": true}
    result, err := ValidateMappingAgainstDB(db, mapping, dataSpecies, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if len(result.MissingSpecies) != 1 || result.MissingSpecies[0] != "K-M" {
    t.Errorf("expected [K-M] missing, got %v", result.MissingSpecies)
    }
    })
    t.Run("mapped species not in DB", func(t *testing.T) {
    mapping := MappingFile{
    "PHANTOM": {Species: "Phantom"},
    }
    dataSpecies := map[string]bool{"PHANTOM": true}
    result, err := ValidateMappingAgainstDB(db, mapping, dataSpecies, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if len(result.MissingDBSpecies) != 1 || result.MissingDBSpecies[0] != "Phantom" {
    t.Errorf("expected [Phantom] missing from DB, got %v", result.MissingDBSpecies)
    }
    })
    t.Run("sentinel species excluded from DB check", func(t *testing.T) {
    mapping := MappingFile{
    "noise": {Species: MappingNegative},
    "ignore": {Species: MappingIgnore},
    }
    dataSpecies := map[string]bool{"noise": true, "ignore": true}
    result, err := ValidateMappingAgainstDB(db, mapping, dataSpecies, nil)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if len(result.MissingDBSpecies) > 0 {
    t.Errorf("sentinels should not be checked against DB, got: %v", result.MissingDBSpecies)
    }
    })
    [3.6919]
    [3.8963]
    tests := []struct {
    name string
    mapping MappingFile
    dataSpecies map[string]bool
    dataCT map[string]map[string]bool
    hasErrors bool
    missingSpecies []string
    missingDBSpecies []string
    missingCalltypeCT string // substring expected in MissingCalltypes key
    errorContains string // substring expected in result.Error()
    }{
    {
    name: "valid mapping - no errors",
    mapping: MappingFile{
    "GSK": {Species: "Roroa", Calltypes: map[string]string{"brrr": "brrr"}},
    "K-M": {Species: "Kiwi"},
    },
    dataSpecies: map[string]bool{"GSK": true, "K-M": true},
    dataCT: map[string]map[string]bool{"GSK": {"brrr": true}, "K-M": {"song": true}},
    },
    {
    name: "missing species in mapping",
    mapping: MappingFile{"GSK": {Species: "Roroa"}},
    dataSpecies: map[string]bool{"GSK": true, "K-M": true},
    hasErrors: true,
    missingSpecies: []string{"K-M"},
    },
    {
    name: "mapped species not in DB",
    mapping: MappingFile{"PHANTOM": {Species: "Phantom"}},
    dataSpecies: map[string]bool{"PHANTOM": true},
    hasErrors: true,
    missingDBSpecies: []string{"Phantom"},
    },
    {
    name: "sentinel species excluded from DB check",
    mapping: MappingFile{"noise": {Species: MappingNegative}, "ignore": {Species: MappingIgnore}},
    dataSpecies: map[string]bool{"noise": true, "ignore": true},
    },
    {
    name: "missing calltype in DB",
    mapping: MappingFile{
    "K-M": {Species: "Kiwi", Calltypes: map[string]string{"song": "song", "phantom": "phantom"}},
    },
    dataSpecies: map[string]bool{"K-M": true},
    dataCT: map[string]map[string]bool{"K-M": {"song": true, "phantom": true}},
    hasErrors: true,
    missingCalltypeCT: "phantom",
    errorContains: "phantom",
    },
    }
  • replacement in utils/mapping_db_test.go at line 286
    [3.8964][3.8964:9638]()
    t.Run("missing calltype in DB", func(t *testing.T) {
    mapping := MappingFile{
    "K-M": {Species: "Kiwi", Calltypes: map[string]string{"song": "song", "phantom": "phantom"}},
    }
    dataSpecies := map[string]bool{"K-M": true}
    dataCT := map[string]map[string]bool{
    "K-M": {"song": true, "phantom": true},
    }
    result, err := ValidateMappingAgainstDB(db, mapping, dataSpecies, dataCT)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if len(result.MissingCalltypes) == 0 {
    t.Error("expected missing calltype for phantom")
    }
    if !strings.Contains(result.Error(), "phantom") {
    t.Errorf("error should mention phantom: %s", result.Error())
    }
    })
    [3.8964]
    [3.9638]
    for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
    result, err := ValidateMappingAgainstDB(db, tt.mapping, tt.dataSpecies, tt.dataCT)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if result.HasErrors() != tt.hasErrors {
    t.Errorf("HasErrors()=%v, want %v", result.HasErrors(), tt.hasErrors)
    }
    assertStringSlice(t, "MissingSpecies", result.MissingSpecies, tt.missingSpecies)
    assertStringSlice(t, "MissingDBSpecies", result.MissingDBSpecies, tt.missingDBSpecies)
    if tt.missingCalltypeCT != "" && len(result.MissingCalltypes) == 0 {
    t.Error("expected missing calltype")
    }
    if tt.errorContains != "" && !strings.Contains(result.Error(), tt.errorContains) {
    t.Errorf("error should contain %q: %s", tt.errorContains, result.Error())
    }
    })
    }
  • replacement in tui/classify.go at line 306
    [3.12276][3.234573:234683](),[3.234573][3.234573:234683]()
    if !m.state.NextSegment() {
    m.quitting = true
    return m, tea.Quit
    }
    return m, m.segmentChangeCmd()
    [3.12276]
    [3.234683]
    return m.advanceOrQuit()
  • replacement in tui/classify.go at line 309
    [3.234721][3.234721:234832]()
    m.state.ToggleBookmark()
    if err := m.state.Save(); err != nil {
    m.err = err.Error()
    }
    return m, nil
    [3.234700]
    [3.234832]
    return m.handleBookmarkToggle()
  • replacement in tui/classify.go at line 312
    [3.234849][3.12277:12294](),[3.12294][3.234930:235045](),[3.234930][3.234930:235045]()
    m.stopPlayer()
    if m.state.PrevBookmark() {
    return m, m.segmentChangeCmd()
    }
    m.err = "No bookmarks found"
    return m, nil
    [3.234849]
    [3.235045]
    return m.handleBookmarkNav(m.state.PrevBookmark)
  • replacement in tui/classify.go at line 315
    [3.235062][3.12295:12312](),[3.12312][3.235139:235254](),[3.235139][3.235139:235254]()
    m.stopPlayer()
    if m.state.NextBookmark() {
    return m, m.segmentChangeCmd()
    }
    m.err = "No bookmarks found"
    return m, nil
    [3.235062]
    [3.235254]
    return m.handleBookmarkNav(m.state.NextBookmark)
  • replacement in tui/classify.go at line 318
    [3.235266][3.12313:12330](),[3.12330][3.235379:235502](),[3.235379][3.235379:235502](),[3.235502][3.12331:12358]()
    m.stopPlayer()
    if m.state.ConfirmLabel() {
    if err := m.state.Save(); err != nil {
    m.err = err.Error()
    return m, nil
    }
    }
    return m.advanceOrQuit()
    [3.235266]
    [3.235612]
    return m.handleConfirmLabel()
  • edit in tui/classify.go at line 325
    [3.235696]
    [3.12398]
    // handleBookmarkToggle toggles the bookmark and saves.
    func (m Model) handleBookmarkToggle() (tea.Model, tea.Cmd) {
    m.state.ToggleBookmark()
    if err := m.state.Save(); err != nil {
    m.err = err.Error()
    }
    return m, nil
    }
    // handleBookmarkNav navigates to a bookmark. navFn returns true if found.
    func (m Model) handleBookmarkNav(navFn func() bool) (tea.Model, tea.Cmd) {
    m.stopPlayer()
    if navFn() {
    return m, m.segmentChangeCmd()
    }
    m.err = "No bookmarks found"
    return m, nil
    }
    // handleConfirmLabel confirms the current label and advances.
    func (m Model) handleConfirmLabel() (tea.Model, tea.Cmd) {
    m.stopPlayer()
    if m.state.ConfirmLabel() {
    if err := m.state.Save(); err != nil {
    m.err = err.Error()
    return m, nil
    }
    }
    return m.advanceOrQuit()
    }
  • edit in tools/import/import_segments.go at line 374
    [3.21928]
    [3.21928]
    speciesIDMap, err := loadSpeciesIDs(q, mapping, uniqueSpecies)
    if err != nil {
    return nil, nil, err
    }
    calltypeIDMap, err := loadCalltypeIDs(q, mapping, uniqueCalltypes)
    if err != nil {
    return nil, nil, err
    }
    return speciesIDMap, calltypeIDMap, nil
    }
    // loadSpeciesIDs queries the DB for species IDs matching the mapped species labels.
    func loadSpeciesIDs(q db.Querier, mapping utils.MappingFile, uniqueSpecies map[string]bool) (map[string]string, error) {
  • edit in tools/import/import_segments.go at line 390
    [3.21969][3.21969:22064]()
    calltypeIDMap := make(map[string]map[string]string) // (dbSpecies, dbCalltype) -> calltype_id
  • edit in tools/import/import_segments.go at line 391
    [3.22065][3.22065:22112]()
    // Collect all DB species labels from mapping
  • replacement in tools/import/import_segments.go at line 398
    [3.22297][3.22297:22482]()
    // Load species IDs
    if len(dbSpeciesSet) > 0 {
    dbSpeciesList := make([]string, 0, len(dbSpeciesSet))
    for s := range dbSpeciesSet {
    dbSpeciesList = append(dbSpeciesList, s)
    }
    [3.22297]
    [3.22482]
    if len(dbSpeciesSet) == 0 {
    return speciesIDMap, nil
    }
    dbSpeciesList := make([]string, 0, len(dbSpeciesSet))
    for s := range dbSpeciesSet {
    dbSpeciesList = append(dbSpeciesList, s)
    }
  • replacement in tools/import/import_segments.go at line 407
    [3.22483][3.22483:22702]()
    query := `SELECT id, label FROM species WHERE label IN (` + db.Placeholders(len(dbSpeciesList)) + `) AND active = true`
    args := make([]any, len(dbSpeciesList))
    for i, s := range dbSpeciesList {
    args[i] = s
    }
    [3.22483]
    [3.22702]
    query := `SELECT id, label FROM species WHERE label IN (` + db.Placeholders(len(dbSpeciesList)) + `) AND active = true`
    args := make([]any, len(dbSpeciesList))
    for i, s := range dbSpeciesList {
    args[i] = s
    }
  • replacement in tools/import/import_segments.go at line 413
    [3.22703][2.17342:17381](),[2.17381][3.22747:22857](),[3.22747][3.22747:22857]()
    rows, err := q.Query(query, args...)
    if err != nil {
    return nil, nil, fmt.Errorf("failed to query species: %w", err)
    }
    defer rows.Close()
    [3.22703]
    [3.22857]
    rows, err := q.Query(query, args...)
    if err != nil {
    return nil, fmt.Errorf("failed to query species: %w", err)
    }
    defer rows.Close()
  • replacement in tools/import/import_segments.go at line 419
    [3.22858][3.22858:22986]()
    for rows.Next() {
    var id, label string
    if err := rows.Scan(&id, &label); err == nil {
    speciesIDMap[label] = id
    }
    [3.22858]
    [3.22986]
    for rows.Next() {
    var id, label string
    if err := rows.Scan(&id, &label); err == nil {
    speciesIDMap[label] = id
  • edit in tools/import/import_segments.go at line 425
    [3.22993]
    [3.22993]
    return speciesIDMap, nil
    }
  • replacement in tools/import/import_segments.go at line 429
    [3.22994][3.22994:23016]()
    // Load calltype IDs
    [3.22994]
    [3.23016]
    // loadCalltypeIDs queries the DB for calltype IDs matching the mapped calltype labels.
    func loadCalltypeIDs(q db.Querier, mapping utils.MappingFile, uniqueCalltypes map[string]map[string]bool) (map[string]map[string]string, error) {
    calltypeIDMap := make(map[string]map[string]string)
  • edit in tools/import/import_segments.go at line 446
    [3.23349][3.23349:23373]()
    // Query calltype ID
  • replacement in tools/import/import_segments.go at line 460
    [3.23702][3.23702:23743]()
    return speciesIDMap, calltypeIDMap, nil
    [3.23702]
    [3.23743]
    return calltypeIDMap, nil
  • edit in tools/export_test.go at line 187
    [3.15817]
    [3.15817]
    }
    })
    }
    // --- createOutputDir ---
    func TestCreateOutputDir(t *testing.T) {
    t.Run("creates parent directory", func(t *testing.T) {
    base := t.TempDir()
    outputPath := filepath.Join(base, "subdir", "output.duckdb")
    if err := createOutputDir(outputPath); err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if _, err := os.Stat(filepath.Join(base, "subdir")); os.IsNotExist(err) {
    t.Error("subdirectory was not created")
    }
    })
    t.Run("current dir passes", func(t *testing.T) {
    if err := createOutputDir("output.duckdb"); err != nil {
    t.Errorf("current dir should pass: %v", err)
    }
    })
    }
    // --- createEventLogFile ---
    func TestCreateEventLogFile(t *testing.T) {
    t.Run("creates empty file", func(t *testing.T) {
    base := t.TempDir()
    path := filepath.Join(base, "test.duckdb")
    if err := createEventLogFile(path); err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    data, err := os.ReadFile(path + ".events.jsonl")
    if err != nil {
    t.Fatalf("failed to read event file: %v", err)
  • edit in tools/export_test.go at line 225
    [3.15821]
    [3.15821]
    if len(data) != 0 {
    t.Errorf("expected empty file, got %d bytes", len(data))
    }
  • replacement in tools/export.go at line 84
    [3.360968][3.360968:361016]()
    // Open source database (read-only for safety)
    [3.360968]
    [3.2425]
    orderedTables, err := prepareExport(input, &output)
    if err != nil {
    return output, err
    }
    // If dry-run, return now
    if input.DryRun {
    output.Message = fmt.Sprintf("Would export dataset '%s' (%s)", output.DatasetName, input.DatasetID)
    return output, nil
    }
    return executeExport(ctx, input, orderedTables, output)
    }
    // prepareExport validates inputs, opens the source DB, and counts rows.
    func prepareExport(input ExportDatasetInput, output *ExportDatasetOutput) ([]TableRelationship, error) {
  • replacement in tools/export.go at line 102
    [3.361077][3.361077:361148]()
    return output, fmt.Errorf("failed to open source database: %w", err)
    [3.361077]
    [3.361148]
    return nil, fmt.Errorf("failed to open source database: %w", err)
  • replacement in tools/export.go at line 108
    [3.361428][3.10943:10964]()
    return output, err
    [3.361428]
    [3.361498]
    return nil, err
  • replacement in tools/export.go at line 114
    [3.361634][3.11014:11035]()
    return output, err
    [3.361634]
    [3.361996]
    return nil, err
  • replacement in tools/export.go at line 120
    [3.362105][3.11094:11115]()
    return output, err
    [3.362105]
    [3.362175]
    return nil, err
  • replacement in tools/export.go at line 123
    [3.362276][3.11116:11215]()
    if err := countAllTableRows(ctx, sourceDB, orderedTables, input.DatasetID, &output); err != nil {
    [3.362276]
    [3.11215]
    if err := countAllTableRows(context.Background(), sourceDB, orderedTables, input.DatasetID, output); err != nil {
  • replacement in tools/export.go at line 125
    [3.11234][3.11234:11255]()
    return output, err
    [3.11234]
    [3.362598]
    return nil, err
  • replacement in tools/export.go at line 128
    [3.362602][3.362602:362868]()
    // If dry-run, return now
    if input.DryRun {
    sourceDB.Close()
    output.Message = fmt.Sprintf("Would export dataset '%s' (%s)", datasetName, input.DatasetID)
    return output, nil
    }
    // Close source DB before creating output (DuckDB can't attach same file twice)
    [3.362602]
    [3.362868]
    // Close before creating output (DuckDB can't attach same file twice)
  • edit in tools/export.go at line 130
    [3.362886]
    [3.362886]
    return orderedTables, nil
    }
  • edit in tools/export.go at line 133
    [3.362887]
    [3.11256]
    // executeExport creates the output database and copies all data.
    func executeExport(ctx context.Context, input ExportDatasetInput, orderedTables []TableRelationship, output ExportDatasetOutput) (ExportDatasetOutput, error) {
  • edit in tools/export.go at line 148
    [3.363929][3.363929:363947]()
    // Detach source
  • edit in tools/export.go at line 154
    [3.364174][3.364174:364192]()
    // Get file size
  • replacement in tools/export.go at line 158
    [3.364306][3.364306:364635]()
    // Create empty event log file
    eventLogPath := input.Output + ".events.jsonl"
    eventFile, err := os.Create(eventLogPath)
    if err != nil {
    return output, fmt.Errorf("failed to create event log file: %w", err)
    }
    if err := eventFile.Close(); err != nil {
    return output, fmt.Errorf("failed to close event log file: %w", err)
    [3.364306]
    [3.364635]
    if err := createEventLogFile(input.Output); err != nil {
    return output, err
  • replacement in tools/export.go at line 163
    [3.364718][3.364718:364764]()
    datasetName, input.DatasetID, input.Output)
    [3.364718]
    [3.364764]
    output.DatasetName, input.DatasetID, input.Output)
  • edit in tools/export.go at line 168
    [3.364788]
    [3.11516]
    // createEventLogFile creates an empty .events.jsonl file next to the export.
    func createEventLogFile(outputPath string) error {
    eventLogPath := outputPath + ".events.jsonl"
    eventFile, err := os.Create(eventLogPath)
    if err != nil {
    return fmt.Errorf("failed to create event log file: %w", err)
    }
    if err := eventFile.Close(); err != nil {
    return fmt.Errorf("failed to close event log file: %w", err)
    }
    return nil
    }
  • edit in tools/calls/calls_propagate_test.go at line 692
    [3.113754]
    [3.113754]
    }
    // --- validateFolderInput ---
    func TestValidateFolderInput(t *testing.T) {
    tests := []struct {
    name string
    input CallsPropagateFolderInput
    wantErr bool
    }{
    {name: "missing folder", input: CallsPropagateFolderInput{FromFilter: "a", ToFilter: "b", Species: "Kiwi"}, wantErr: true},
    {name: "missing from", input: CallsPropagateFolderInput{Folder: "/tmp", ToFilter: "b", Species: "Kiwi"}, wantErr: true},
    {name: "missing to", input: CallsPropagateFolderInput{Folder: "/tmp", FromFilter: "a", Species: "Kiwi"}, wantErr: true},
    {name: "missing species", input: CallsPropagateFolderInput{Folder: "/tmp", FromFilter: "a", ToFilter: "b"}, wantErr: true},
    {name: "from==to", input: CallsPropagateFolderInput{Folder: "/tmp", FromFilter: "x", ToFilter: "x", Species: "Kiwi"}, wantErr: true},
    {name: "not a dir", input: CallsPropagateFolderInput{Folder: "/dev/null", FromFilter: "a", ToFilter: "b", Species: "Kiwi"}, wantErr: true},
    }
    for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
    var output CallsPropagateFolderOutput
    err := validateFolderInput(&output, tt.input)
    if (err != nil) != tt.wantErr {
    t.Errorf("validateFolderInput() error=%v, wantErr=%v", err, tt.wantErr)
    }
    if tt.wantErr && output.Error == "" {
    t.Error("expected output.Error to be set on validation failure")
    }
    })
    }
    }
    // --- accumulatePropagateResult ---
    func TestAccumulatePropagateResult(t *testing.T) {
    t.Run("filters missing skips file", func(t *testing.T) {
    var output CallsPropagateFolderOutput
    accumulatePropagateResult("test.data", CallsPropagateOutput{FiltersMissing: true}, &output)
    if output.FilesSkippedNoFilter != 1 {
    t.Errorf("expected 1 skipped, got %d", output.FilesSkippedNoFilter)
    }
    if output.FilesWithBothFilters != 0 {
    t.Error("should not count as having both filters")
    }
    })
    t.Run("normal result aggregates counts", func(t *testing.T) {
    var output CallsPropagateFolderOutput
    accumulatePropagateResult("test.data", CallsPropagateOutput{
    TargetsExamined: 5,
    Propagated: 2,
    SkippedNoOverlap: 2,
    SkippedConflict: 1,
    Conflicts: []PropagateConflict{{TargetStart: 1.0, TargetEnd: 2.0}},
    }, &output)
    if output.TargetsExamined != 5 || output.Propagated != 2 {
    t.Errorf("counts wrong: %+v", output)
    }
    if output.FilesChanged != 1 {
    t.Errorf("should mark 1 file changed, got %d", output.FilesChanged)
    }
    if len(output.Conflicts) != 1 || output.Conflicts[0].File != "test.data" {
    t.Errorf("conflict file not tagged: %+v", output.Conflicts)
    }
    })
    t.Run("zero propagated not counted as changed", func(t *testing.T) {
    var output CallsPropagateFolderOutput
    accumulatePropagateResult("test.data", CallsPropagateOutput{
    TargetsExamined: 3,
    }, &output)
    if output.FilesChanged != 0 {
    t.Errorf("should not count as changed, got %d", output.FilesChanged)
    }
    })
  • replacement in tools/calls/calls_propagate.go at line 307
    [3.124250][3.124250:124713]()
    if input.Folder == "" {
    output.Error = "--folder is required"
    return output, fmt.Errorf("%s", output.Error)
    }
    if input.FromFilter == "" {
    output.Error = "--from is required"
    return output, fmt.Errorf("%s", output.Error)
    }
    if input.ToFilter == "" {
    output.Error = "--to is required"
    return output, fmt.Errorf("%s", output.Error)
    }
    if input.Species == "" {
    output.Error = "--species is required"
    return output, fmt.Errorf("%s", output.Error)
    [3.124250]
    [3.124713]
    if err := validateFolderInput(&output, input); err != nil {
    return output, err
  • edit in tools/calls/calls_propagate.go at line 310
    [3.124716][3.124716:124855]()
    if input.FromFilter == input.ToFilter {
    output.Error = "--from and --to must differ"
    return output, fmt.Errorf("%s", output.Error)
    }
  • edit in tools/calls/calls_propagate.go at line 311
    [3.124856][3.124856:125165]()
    info, err := os.Stat(input.Folder)
    if err != nil {
    output.Error = fmt.Sprintf("folder not found: %s", input.Folder)
    return output, fmt.Errorf("%s", output.Error)
    }
    if !info.IsDir() {
    output.Error = fmt.Sprintf("not a directory: %s", input.Folder)
    return output, fmt.Errorf("%s", output.Error)
    }
  • replacement in tools/calls/calls_propagate.go at line 330
    [3.125680][3.125680:125755]()
    if fileOut.FiltersMissing {
    output.FilesSkippedNoFilter++
    continue
    [3.125680]
    [3.125755]
    accumulatePropagateResult(f, fileOut, &output)
    }
    return output, nil
    }
    // validateFolderInput checks required fields and that the folder exists.
    func validateFolderInput(output *CallsPropagateFolderOutput, input CallsPropagateFolderInput) error {
    checks := []struct {
    val string
    msg string
    }{
    {input.Folder, "--folder is required"},
    {input.FromFilter, "--from is required"},
    {input.ToFilter, "--to is required"},
    {input.Species, "--species is required"},
    }
    for _, c := range checks {
    if c.val == "" {
    output.Error = c.msg
    return fmt.Errorf("%s", c.msg)
  • replacement in tools/calls/calls_propagate.go at line 352
    [3.125759][3.125759:126158]()
    output.FilesWithBothFilters++
    output.TargetsExamined += fileOut.TargetsExamined
    output.Propagated += fileOut.Propagated
    output.SkippedNoOverlap += fileOut.SkippedNoOverlap
    output.SkippedConflict += fileOut.SkippedConflict
    if fileOut.Propagated > 0 {
    output.FilesChanged++
    }
    for _, c := range fileOut.Conflicts {
    c.File = f
    output.Conflicts = append(output.Conflicts, c)
    }
    [3.125759]
    [3.126158]
    }
    if input.FromFilter == input.ToFilter {
    output.Error = "--from and --to must differ"
    return fmt.Errorf("%s", output.Error)
    }
    info, err := os.Stat(input.Folder)
    if err != nil {
    output.Error = fmt.Sprintf("folder not found: %s", input.Folder)
    return fmt.Errorf("%s", output.Error)
    }
    if !info.IsDir() {
    output.Error = fmt.Sprintf("not a directory: %s", input.Folder)
    return fmt.Errorf("%s", output.Error)
  • edit in tools/calls/calls_propagate.go at line 366
    [3.126161]
    [3.126161]
    return nil
    }
  • replacement in tools/calls/calls_propagate.go at line 369
    [3.126162][3.126162:126182]()
    return output, nil
    [3.126162]
    [3.126182]
    // accumulatePropagateResult merges a per-file result into the folder-level aggregate.
    func accumulatePropagateResult(filePath string, fileOut CallsPropagateOutput, output *CallsPropagateFolderOutput) {
    if fileOut.FiltersMissing {
    output.FilesSkippedNoFilter++
    return
    }
    output.FilesWithBothFilters++
    output.TargetsExamined += fileOut.TargetsExamined
    output.Propagated += fileOut.Propagated
    output.SkippedNoOverlap += fileOut.SkippedNoOverlap
    output.SkippedConflict += fileOut.SkippedConflict
    if fileOut.Propagated > 0 {
    output.FilesChanged++
    }
    for _, c := range fileOut.Conflicts {
    c.File = filePath
    output.Conflicts = append(output.Conflicts, c)
    }
  • replacement in lint_test.go at line 48
    [3.158][3.15171:15223]()
    cmd := exec.Command("gocyclo", "-over", "14", ".")
    [3.158]
    [3.210]
    cmd := exec.Command("gocyclo", "-over", "13", ".")
  • file addition: calls_test.go (----------)
    [3.1037540]
    package cmd
    import (
    "strings"
    "testing"
    )
    func TestRunCalls_NoArgs(t *testing.T) {
    err := RunCalls([]string{})
    if err == nil {
    t.Error("expected error for no args")
    }
    if !strings.Contains(err.Error(), "subcommand required") {
    t.Errorf("unexpected error: %v", err)
    }
    }
    func TestRunCalls_UnknownSubcommand(t *testing.T) {
    err := RunCalls([]string{"nonexistent"})
    if err == nil {
    t.Error("expected error for unknown subcommand")
    }
    if !strings.Contains(err.Error(), "unknown") {
    t.Errorf("unexpected error: %v", err)
    }
    }
    func TestCallsSubcommandsComplete(t *testing.T) {
    // Ensure the map has all expected subcommands
    expected := []string{
    "from-preds", "from-birda", "from-raven", "show-images",
    "classify", "clip", "modify", "push-certainty",
    "detect-anomalies", "propagate", "summarise", "clip-labels",
    }
    for _, name := range expected {
    if _, ok := callsSubcommands[name]; !ok {
    t.Errorf("callsSubcommands missing: %s", name)
    }
    }
    if len(callsSubcommands) != len(expected) {
    t.Errorf("callsSubcommands has %d entries, expected %d", len(callsSubcommands), len(expected))
    }
    }
  • edit in cmd/calls.go at line 16
    [3.1153948]
    [3.9546]
    // callsSubcommands maps subcommand names to their handler functions.
    var callsSubcommands = map[string]func([]string) error{
    "from-preds": runCallsFromPreds,
    "from-birda": runCallsFromBirda,
    "from-raven": runCallsFromRaven,
    "show-images": runCallsShowImages,
    "classify": RunCallsClassify,
    "clip": RunCallsClip,
    "modify": RunCallsModify,
    "push-certainty": runCallsPushCertainty,
    "detect-anomalies": runCallsDetectAnomalies,
    "propagate": runCallsPropagate,
    "summarise": runCallsSummarise,
    "clip-labels": runCallsClipLabels,
    }
  • replacement in cmd/calls.go at line 38
    [3.1154036][3.1154036:1154074](),[3.1154074][3.9899:9936](),[3.9647][3.1154104:1154124](),[3.9936][3.1154104:1154124](),[3.1154104][3.1154104:1154124](),[3.1154124][3.9937:9974](),[3.9661][3.1154154:1154174](),[3.9974][3.1154154:1154174](),[3.1154154][3.1154154:1154174](),[3.1154174][3.9975:10012](),[3.9675][3.1154204:1154225](),[3.10012][3.1154204:1154225](),[3.1154204][3.1154204:1154225](),[3.1154225][3.10013:10051](),[3.9689][3.1154256:1154274](),[3.10051][3.1154256:1154274](),[3.1154256][3.1154256:1154274](),[3.1154274][3.10052:10088](),[3.9703][3.1154303:1154317](),[3.10088][3.1154303:1154317](),[3.1154303][3.1154303:1154317](),[3.1154317][3.10089:10121](),[3.9717][3.1154342:1154358](),[3.10121][3.1154342:1154358](),[3.1154342][3.1154342:1154358](),[3.1154358][3.10122:10156](),[3.9731][3.1154385:1154409](),[3.10156][3.1154385:1154409](),[3.1154385][3.1154385:1154409](),[3.1154409][3.10157:10198](),[3.9745][3.1154443:1154469](),[3.10198][3.1154443:1154469](),[3.1154443][3.1154443:1154469](),[3.1154469][3.10199:10242](),[3.9759][3.1154505:1154524](),[3.10242][3.1154505:1154524](),[3.1154505][3.1154505:1154524](),[3.1154524][3.10243:10280](),[3.9773][3.1154554:1154573](),[3.10280][3.1154554:1154573](),[3.1154554][3.1154554:1154573](),[3.1154573][3.10281:10318](),[3.9787][3.1154603:1154624](),[3.10318][3.1154603:1154624](),[3.1154603][3.1154603:1154624](),[3.1154624][3.10319:10357](),[3.9801][3.1154655:1154665](),[3.10357][3.1154655:1154665](),[3.1154655][3.1154655:1154665]()
    switch args[0] {
    case "from-preds":
    return runCallsFromPreds(args[1:])
    case "from-birda":
    return runCallsFromBirda(args[1:])
    case "from-raven":
    return runCallsFromRaven(args[1:])
    case "show-images":
    return runCallsShowImages(args[1:])
    case "classify":
    return RunCallsClassify(args[1:])
    case "clip":
    return RunCallsClip(args[1:])
    case "modify":
    return RunCallsModify(args[1:])
    case "push-certainty":
    return runCallsPushCertainty(args[1:])
    case "detect-anomalies":
    return runCallsDetectAnomalies(args[1:])
    case "propagate":
    return runCallsPropagate(args[1:])
    case "summarise":
    return runCallsSummarise(args[1:])
    case "clip-labels":
    return runCallsClipLabels(args[1:])
    default:
    [3.1154036]
    [3.1154735]
    handler, ok := callsSubcommands[args[0]]
    if !ok {
  • edit in cmd/calls.go at line 43
    [3.1154771]
    [3.1154771]
    return handler(args[1:])
  • replacement in .golangci.yml at line 14
    [3.22132][3.22132:22157]()
    max-complexity: 15
    [3.22132]
    [3.22157]
    max-complexity: 14