more tests

quietlight
May 13, 2026, 4:07 AM
TSOJUMHVLPASHBAVCTUK6WSGZOSBDZIC47FYILGQ2QAU7Z4BUZMAC

Dependencies

Change contents

  • file addition: import_segments_validation_test.go (----------)
    [4.1]
    package imp
    import (
    "database/sql"
    "os"
    "strings"
    "testing"
    "skraak/db"
    "skraak/utils"
    )
    // setupImportTestDB creates a DB with the full schema + test data for import validation.
    //
    // dataset (structured): ds_imptest00001
    // dataset (unstructured): ds_imptest00002
    // location in ds1: loc_imptest0001
    // location (inactive): loc_imptest0002 (in ds1)
    // cluster in loc1: cl_imptest00001
    // cluster (inactive): cl_imptest00002 (in loc1)
    // species: Kiwi (sp_kiwi000000), Roroa (sp_roroa00000)
    // calltypes: Kiwi/song (ct_kiwi000001), Kiwi/duet (ct_kiwi000002)
    // filter: kiwi.txt (fi_kiwi0000001), tomtit.txt (fi_tomtit000001)
    func setupImportTestDB(t *testing.T) *sql.DB {
    t.Helper()
    database := db.SetupTestDB(t)
    db.InsertTestDatasetWithType(t, database, "ds_imptest00001", "Imp Structured", "structured")
    db.InsertTestDatasetWithType(t, database, "ds_imptest00002", "Imp Unstructured", "unstructured")
    db.InsertTestLocation(t, database, "loc_imptest0001", "ds_imptest00001", "Loc Active")
    db.InsertTestLocation(t, database, "loc_imptest0002", "ds_imptest00001", "Loc Inactive")
    mustExecImport(t, database, "UPDATE location SET active = false WHERE id = 'loc_imptest0002'")
    db.InsertTestCluster(t, database, "cl_imptest00001", "ds_imptest00001", "loc_imptest0001", "Cl Active")
    db.InsertTestCluster(t, database, "cl_imptest00002", "ds_imptest00001", "loc_imptest0001", "Cl Inactive")
    mustExecImport(t, database, "UPDATE cluster SET active = false WHERE id = 'cl_imptest00002'")
    db.InsertTestSpecies(t, database, "sp_kiwi000000", "Kiwi")
    db.InsertTestSpecies(t, database, "sp_roroa00000", "Roroa")
    db.InsertTestCallType(t, database, "ct_kiwi000001", "sp_kiwi000000", "song")
    db.InsertTestCallType(t, database, "ct_kiwi000002", "sp_kiwi000000", "duet")
    db.InsertTestFilter(t, database, "fi_kiwi0000001", "kiwi.txt")
    db.InsertTestFilter(t, database, "fi_tomtit000001", "tomtit.txt")
    return database
    }
    func mustExecImport(t *testing.T, database *sql.DB, query string, args ...any) {
    t.Helper()
    if _, err := database.Exec(query, args...); err != nil {
    t.Fatalf("exec: %v", err)
    }
    }
    // --- validateSegmentHierarchy ---
    func TestValidateSegmentHierarchy(t *testing.T) {
    database := setupImportTestDB(t)
    defer database.Close()
    t.Run("valid hierarchy", func(t *testing.T) {
    err := validateSegmentHierarchy(database, "ds_imptest00001", "loc_imptest0001", "cl_imptest00001")
    if err != nil {
    t.Errorf("unexpected error: %v", err)
    }
    })
    t.Run("unstructured dataset rejected", func(t *testing.T) {
    err := validateSegmentHierarchy(database, "ds_imptest00002", "loc_imptest0001", "cl_imptest00001")
    if err == nil || !strings.Contains(err.Error(), "structured") {
    t.Errorf("expected structured-only error, got: %v", err)
    }
    })
    t.Run("inactive location rejected", func(t *testing.T) {
    err := validateSegmentHierarchy(database, "ds_imptest00001", "loc_imptest0002", "cl_imptest00001")
    if err == nil {
    t.Error("expected error for inactive location")
    }
    })
    t.Run("inactive cluster rejected", func(t *testing.T) {
    err := validateSegmentHierarchy(database, "ds_imptest00001", "loc_imptest0001", "cl_imptest00002")
    if err == nil {
    t.Error("expected error for inactive cluster")
    }
    })
    }
    // --- validateFiltersExist ---
    func TestValidateFiltersExist(t *testing.T) {
    database := setupImportTestDB(t)
    defer database.Close()
    t.Run("all filters exist", func(t *testing.T) {
    filterMap, err := validateFiltersExist(database, map[string]bool{"kiwi.txt": true, "tomtit.txt": true})
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if len(filterMap) != 2 {
    t.Errorf("expected 2 filters, got %d", len(filterMap))
    }
    if filterMap["kiwi.txt"] != "fi_kiwi0000001" {
    t.Errorf("kiwi.txt ID = %q, want fi_kiwi0000001", filterMap["kiwi.txt"])
    }
    })
    t.Run("missing filter reported", func(t *testing.T) {
    _, err := validateFiltersExist(database, map[string]bool{"phantom.txt": true})
    if err == nil || !strings.Contains(err.Error(), "not found") {
    t.Errorf("expected not-found error, got: %v", err)
    }
    })
    t.Run("empty filter set returns empty map", func(t *testing.T) {
    filterMap, err := validateFiltersExist(database, map[string]bool{})
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if len(filterMap) != 0 {
    t.Errorf("expected empty map, got %d entries", len(filterMap))
    }
    })
    }
    // --- loadSpeciesCalltypeIDs ---
    func TestLoadSpeciesCalltypeIDs(t *testing.T) {
    database := setupImportTestDB(t)
    defer database.Close()
    mapping := utils.MappingFile{
    "K-M": {Species: "Kiwi"},
    "GSK": {Species: "Roroa", Calltypes: map[string]string{"brrr": "brrr"}},
    }
    t.Run("loads species IDs", func(t *testing.T) {
    speciesMap, _, err := loadSpeciesCalltypeIDs(
    database, mapping,
    map[string]bool{"K-M": true, "GSK": true},
    map[string]map[string]bool{"K-M": {"song": true}, "GSK": {"brrr": true}},
    )
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if speciesMap["Kiwi"] != "sp_kiwi000000" {
    t.Errorf("Kiwi ID = %q, want sp_kiwi000000", speciesMap["Kiwi"])
    }
    if speciesMap["Roroa"] != "sp_roroa00000" {
    t.Errorf("Roroa ID = %q, want sp_roroa00000", speciesMap["Roroa"])
    }
    })
    t.Run("loads calltype IDs", func(t *testing.T) {
    _, calltypeMap, err := loadSpeciesCalltypeIDs(
    database, mapping,
    map[string]bool{"K-M": true},
    map[string]map[string]bool{"K-M": {"song": true, "duet": true}},
    )
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if calltypeMap["Kiwi"]["song"] != "ct_kiwi000001" {
    t.Errorf("Kiwi/song ID = %q, want ct_kiwi000001", calltypeMap["Kiwi"]["song"])
    }
    if calltypeMap["Kiwi"]["duet"] != "ct_kiwi000002" {
    t.Errorf("Kiwi/duet ID = %q, want ct_kiwi000002", calltypeMap["Kiwi"]["duet"])
    }
    })
    t.Run("missing calltype returns empty ID", func(t *testing.T) {
    _, calltypeMap, err := loadSpeciesCalltypeIDs(
    database, mapping,
    map[string]bool{"K-M": true},
    map[string]map[string]bool{"K-M": {"phantom": true}},
    )
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if calltypeMap["Kiwi"]["phantom"] != "" {
    t.Errorf("missing calltype should have empty ID, got %q", calltypeMap["Kiwi"]["phantom"])
    }
    })
    }
    // --- validateAndMapFiles ---
    func TestValidateAndMapFiles_NoScannedFiles(t *testing.T) {
    database := setupImportTestDB(t)
    defer database.Close()
    fileMap, errors := validateAndMapFiles(database, nil, "cl_imptest00001", "ds_imptest00001")
    if len(fileMap) != 0 {
    t.Errorf("expected empty map, got %d entries", len(fileMap))
    }
    if len(errors) != 0 {
    t.Errorf("expected no errors, got %d", len(errors))
    }
    }
    func TestValidateAndMapFiles_FileFoundByHash(t *testing.T) {
    database := setupImportTestDB(t)
    defer database.Close()
    // Create a temp WAV file so hash can be computed
    tmpDir := t.TempDir()
    wavPath := tmpDir + "/test.wav"
    if err := createFakeWAV(wavPath); err != nil {
    t.Fatalf("create WAV: %v", err)
    }
    // Compute the actual hash of the file we created
    hash, err := utils.ComputeXXH64(wavPath)
    if err != nil {
    t.Fatalf("compute hash: %v", err)
    }
    // Insert a file with the actual hash
    db.InsertTestFileForCluster(t, database, "fi_test00000001", "cl_imptest00001", "loc_imptest0001", "test.wav", hash, 60.0)
    // Link file to dataset
    mustExecImport(t, database, "INSERT INTO file_dataset (file_id, dataset_id, created_at, last_modified) VALUES (?, ?, now(), now())", "fi_test00000001", "ds_imptest00001")
    scanned := []scannedDataFile{{
    DataPath: tmpDir + "/test.data",
    WavPath: wavPath,
    Duration: 60.0,
    Segments: []*utils.Segment{{StartTime: 1.0, EndTime: 2.0, Labels: []*utils.Label{}}},
    }}
    fileMap, errors := validateAndMapFiles(database, scanned, "cl_imptest00001", "ds_imptest00001")
    // Should find the file by hash
    if len(fileMap) != 1 {
    t.Errorf("expected 1 file mapped, got %d; errors: %v", len(fileMap), errors)
    }
    }
    func TestValidateAndMapFiles_HashNotFound(t *testing.T) {
    database := setupImportTestDB(t)
    defer database.Close()
    // No file in DB — any hash lookup fails
    tmpDir := t.TempDir()
    wavPath := tmpDir + "/test.wav"
    if err := createFakeWAV(wavPath); err != nil {
    t.Fatalf("create WAV: %v", err)
    }
    scanned := []scannedDataFile{{
    DataPath: tmpDir + "/test.data",
    WavPath: wavPath,
    Duration: 60.0,
    Segments: []*utils.Segment{{StartTime: 1.0, EndTime: 2.0, Labels: []*utils.Label{}}},
    }}
    fileMap, errors := validateAndMapFiles(database, scanned, "cl_imptest00001", "ds_imptest00001")
    if len(fileMap) != 0 {
    t.Errorf("expected 0 files mapped, got %d", len(fileMap))
    }
    if len(errors) == 0 {
    t.Error("expected hash-not-found error")
    }
    }
    // createFakeWAV creates a minimal WAV file (44-byte header + 1 sample)
    func createFakeWAV(path string) error {
    // Minimal valid WAV: 44-byte header + 4 bytes of data
    header := make([]byte, 44)
    copy(header[0:4], "RIFF")
    copy(header[4:8], "\x24\x00\x00\x00") // file size - 8
    copy(header[8:12], "WAVE")
    copy(header[12:16], "fmt ")
    copy(header[16:20], "\x10\x00\x00\x00") // chunk size
    copy(header[20:22], "\x01\x00") // PCM
    copy(header[22:24], "\x01\x00") // mono
    copy(header[24:28], "\x80\x3E\x00\x00") // 16000 sample rate
    copy(header[28:32], "\x00\x7D\x00\x00") // byte rate
    copy(header[32:34], "\x02\x00") // block align
    copy(header[34:36], "\x10\x00") // bits per sample
    copy(header[36:40], "data")
    copy(header[40:44], "\x00\x00\x00\x00") // data size
    return os.WriteFile(path, header, 0644)
    }
  • replacement in tools/import/import_segments.go at line 242
    [4.18127][4.18127:18344]()
    func validateSegmentHierarchy(dbConn *sql.DB, datasetID, locationID, clusterID string) error {
    // Validate dataset exists and is structured
    if err := db.ValidateDatasetTypeForImport(dbConn, datasetID); err != nil {
    [4.18127]
    [4.18344]
    func validateSegmentHierarchy(q db.Querier, datasetID, locationID, clusterID string) error {
    if err := db.ValidateDatasetTypeForImport(q, datasetID); err != nil {
  • replacement in tools/import/import_segments.go at line 247
    [4.18361][4.18361:18494]()
    // Validate location belongs to dataset
    if err := db.ValidateLocationBelongsToDataset(dbConn, locationID, datasetID); err != nil {
    [4.18361]
    [4.18494]
    if err := db.ValidateLocationBelongsToDataset(q, locationID, datasetID); err != nil {
  • replacement in tools/import/import_segments.go at line 251
    [4.18511][4.18511:18636]()
    // Validate cluster belongs to location
    if err := db.ClusterBelongsToLocation(dbConn, clusterID, locationID); err != nil {
    [4.18511]
    [4.18636]
    if err := db.ClusterBelongsToLocation(q, clusterID, locationID); err != nil {
  • replacement in tools/import/import_segments.go at line 321
    [4.20558][4.20558:20658]()
    func validateFiltersExist(dbConn *sql.DB, filterNames map[string]bool) (map[string]string, error) {
    [4.20558]
    [4.20658]
    func validateFiltersExist(q db.Querier, filterNames map[string]bool) (map[string]string, error) {
  • replacement in tools/import/import_segments.go at line 339
    [4.21064][4.21064:21107]()
    rows, err := dbConn.Query(query, args...)
    [4.21064]
    [4.21107]
    rows, err := q.Query(query, args...)
  • replacement in tools/import/import_segments.go at line 369
    [4.21745][4.21745:21762]()
    dbConn *sql.DB,
    [4.21745]
    [4.21762]
    q db.Querier,
  • replacement in tools/import/import_segments.go at line 398
    [4.22703][4.22703:22747]()
    rows, err := dbConn.Query(query, args...)
    [4.22703]
    [4.22747]
    rows, err := q.Query(query, args...)
  • replacement in tools/import/import_segments.go at line 428
    [4.23398][4.23398:23426]()
    err := dbConn.QueryRow(`
    [4.23398]
    [4.23426]
    err := q.QueryRow(`
  • replacement in tools/import/import_segments.go at line 446
    [4.23877][4.23877:23894]()
    dbConn *sql.DB,
    [4.23877]
    [4.23894]
    q db.Querier,
  • replacement in tools/import/import_segments.go at line 470
    [4.24516][4.24516:24542]()
    err = dbConn.QueryRow(`
    [4.24516]
    [4.24542]
    err = q.QueryRow(`
  • replacement in tools/import/import_segments.go at line 496
    [4.25330][4.25330:25356]()
    err = dbConn.QueryRow(`
    [4.25330]
    [4.25356]
    err = q.QueryRow(`
  • replacement in tools/import/import_segments.go at line 518
    [4.26059][4.26059:26085]()
    err = dbConn.QueryRow(`
    [4.26059]
    [4.26085]
    err = q.QueryRow(`
  • replacement in tools/import/bulk_file_import.go at line 216
    [4.57746][4.57746:57863]()
    func bulkValidateLocationsBelongToDataset(dbConn *sql.DB, locations []bulkLocationData, datasetID string) []string {
    [4.57746]
    [4.57863]
    func bulkValidateLocationsBelongToDataset(q db.Querier, locations []bulkLocationData, datasetID string) []string {
  • replacement in tools/import/bulk_file_import.go at line 227
    [4.58117][4.58117:58210]()
    if err := db.ValidateLocationBelongsToDataset(dbConn, locationID, datasetID); err != nil {
    [4.58117]
    [4.58210]
    if err := db.ValidateLocationBelongsToDataset(q, locationID, datasetID); err != nil {
  • file addition: calls_classify_nav_test.go (----------)
    [4.67281]
    package calls
    import (
    "testing"
    "skraak/utils"
    )
    // --- Navigation: NextSegment / PrevSegment ---
    func TestNextSegment(t *testing.T) {
    df := &utils.DataFile{
    FilePath: "/test/f1.data",
    Segments: []*utils.Segment{
    {StartTime: 0, EndTime: 10},
    {StartTime: 10, EndTime: 20},
    },
    }
    df2 := &utils.DataFile{
    FilePath: "/test/f2.data",
    Segments: []*utils.Segment{
    {StartTime: 0, EndTime: 10},
    },
    }
    state := NewClassifyState(ClassifyConfig{Certainty: -1}, []*utils.DataFile{df, df2})
    // Start at file 0, segment 0
    if state.FileIdx != 0 || state.SegmentIdx != 0 {
    t.Fatalf("initial: file=%d seg=%d", state.FileIdx, state.SegmentIdx)
    }
    // Advance within same file
    if !state.NextSegment() {
    t.Error("next within file should succeed")
    }
    if state.FileIdx != 0 || state.SegmentIdx != 1 {
    t.Errorf("after next: file=%d seg=%d", state.FileIdx, state.SegmentIdx)
    }
    // Advance to next file
    if !state.NextSegment() {
    t.Error("next to next file should succeed")
    }
    if state.FileIdx != 1 || state.SegmentIdx != 0 {
    t.Errorf("after next file: file=%d seg=%d", state.FileIdx, state.SegmentIdx)
    }
    // At end
    if state.NextSegment() {
    t.Error("should fail at end")
    }
    }
    func TestPrevSegment(t *testing.T) {
    df := &utils.DataFile{
    FilePath: "/test/f1.data",
    Segments: []*utils.Segment{
    {StartTime: 0, EndTime: 10},
    },
    }
    df2 := &utils.DataFile{
    FilePath: "/test/f2.data",
    Segments: []*utils.Segment{
    {StartTime: 0, EndTime: 10},
    {StartTime: 10, EndTime: 20},
    },
    }
    state := NewClassifyState(ClassifyConfig{Certainty: -1}, []*utils.DataFile{df, df2})
    state.FileIdx = 1
    state.SegmentIdx = 1
    // Prev within same file
    if !state.PrevSegment() {
    t.Error("prev within file should succeed")
    }
    if state.FileIdx != 1 || state.SegmentIdx != 0 {
    t.Errorf("after prev: file=%d seg=%d", state.FileIdx, state.SegmentIdx)
    }
    // Prev to previous file
    if !state.PrevSegment() {
    t.Error("prev to previous file should succeed")
    }
    if state.FileIdx != 0 || state.SegmentIdx != 0 {
    t.Errorf("after prev file: file=%d seg=%d", state.FileIdx, state.SegmentIdx)
    }
    // At start
    if state.PrevSegment() {
    t.Error("should fail at start")
    }
    }
    // --- buildClassifyState goto logic ---
    func TestBuildClassifyState_GotoFound(t *testing.T) {
    df1 := &utils.DataFile{FilePath: "/test/alpha.data", Segments: []*utils.Segment{{}}}
    df2 := &utils.DataFile{FilePath: "/test/beta.data", Segments: []*utils.Segment{{}}}
    segs := [][]*utils.Segment{df1.Segments, df2.Segments}
    state, err := buildClassifyState(ClassifyConfig{Goto: "beta.data"}, []*utils.DataFile{df1, df2}, segs, 0)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if state.FileIdx != 1 {
    t.Errorf("FileIdx=%d, want 1", state.FileIdx)
    }
    }
    func TestBuildClassifyState_GotoNotFound(t *testing.T) {
    df1 := &utils.DataFile{FilePath: "/test/alpha.data", Segments: []*utils.Segment{{}}}
    segs := [][]*utils.Segment{df1.Segments}
    _, err := buildClassifyState(ClassifyConfig{Goto: "missing.data"}, []*utils.DataFile{df1}, segs, 0)
    if err == nil {
    t.Error("expected error for missing goto file")
    }
    }
    func TestBuildClassifyState_NoGoto(t *testing.T) {
    df1 := &utils.DataFile{FilePath: "/test/alpha.data", Segments: []*utils.Segment{{}}}
    segs := [][]*utils.Segment{df1.Segments}
    state, err := buildClassifyState(ClassifyConfig{}, []*utils.DataFile{df1}, segs, 0)
    if err != nil {
    t.Fatalf("unexpected error: %v", err)
    }
    if state.FileIdx != 0 {
    t.Errorf("FileIdx=%d, want 0", state.FileIdx)
    }
    }
    // --- FormatLabels ---
    func TestFormatLabels(t *testing.T) {
    labels := []*utils.Label{
    {Species: "Kiwi", CallType: "Duet", Certainty: 90, Filter: "model-1.0"},
    {Species: "Tomtit", Certainty: 70, Filter: "model-2.0"},
    }
    t.Run("no filter", func(t *testing.T) {
    got := FormatLabels(labels, "")
    if got == "" {
    t.Error("expected non-empty output")
    }
    // Both labels should appear
    if !contains(got, "Kiwi/Duet") {
    t.Errorf("expected Kiwi/Duet in %q", got)
    }
    if !contains(got, "Tomtit") {
    t.Errorf("expected Tomtit in %q", got)
    }
    })
    t.Run("with filter", func(t *testing.T) {
    got := FormatLabels(labels, "model-1.0")
    if !contains(got, "Kiwi") {
    t.Errorf("expected Kiwi in %q", got)
    }
    if contains(got, "Tomtit") {
    t.Errorf("Tomtit should be filtered out in %q", got)
    }
    })
    t.Run("with comment", func(t *testing.T) {
    labels := []*utils.Label{
    {Species: "Kiwi", Certainty: 100, Filter: "f1", Comment: "nice call"},
    }
    got := FormatLabels(labels, "")
    if !contains(got, `"nice call"`) {
    t.Errorf("expected comment in %q", got)
    }
    })
    }
    // --- Bookmark navigation ---
    func TestBookmarkNavigation(t *testing.T) {
    df := &utils.DataFile{
    FilePath: "/test/f1.data",
    Meta: &utils.DataMeta{},
    Segments: []*utils.Segment{
    {Labels: []*utils.Label{{Species: "Kiwi", Filter: "f", Bookmark: true}}},
    {Labels: []*utils.Label{{Species: "Tomtit", Filter: "f"}}},
    {Labels: []*utils.Label{{Species: "Roroa", Filter: "f", Bookmark: true}}},
    },
    }
    state := NewClassifyState(ClassifyConfig{Filter: "f", Certainty: -1}, []*utils.DataFile{df})
    // Start at segment 0 (has bookmark)
    if !state.HasBookmark() {
    t.Error("segment 0 should have bookmark")
    }
    // NextBookmark should go to segment 2
    if !state.NextBookmark() {
    t.Error("NextBookmark should find bookmark at segment 2")
    }
    if state.SegmentIdx != 2 {
    t.Errorf("after NextBookmark: seg=%d, want 2", state.SegmentIdx)
    }
    // PrevBookmark should go back to segment 0
    if !state.PrevBookmark() {
    t.Error("PrevBookmark should find bookmark at segment 0")
    }
    if state.SegmentIdx != 0 {
    t.Errorf("after PrevBookmark: seg=%d, want 0", state.SegmentIdx)
    }
    }
    func TestToggleBookmark(t *testing.T) {
    df := &utils.DataFile{
    FilePath: "/test/f1.data",
    Meta: &utils.DataMeta{},
    Segments: []*utils.Segment{
    {Labels: []*utils.Label{{Species: "Kiwi", Filter: "f"}}},
    },
    }
    state := NewClassifyState(ClassifyConfig{Filter: "f", Reviewer: "Test", Certainty: -1}, []*utils.DataFile{df})
    if state.HasBookmark() {
    t.Error("should start without bookmark")
    }
    state.ToggleBookmark()
    if !state.HasBookmark() {
    t.Error("should have bookmark after toggle")
    }
    state.ToggleBookmark()
    if state.HasBookmark() {
    t.Error("bookmark should be removed after second toggle")
    }
    }
    // --- ConfirmLabel ---
    func TestConfirmLabel(t *testing.T) {
    df := &utils.DataFile{
    FilePath: "/test/f1.data",
    Meta: &utils.DataMeta{},
    Segments: []*utils.Segment{
    {Labels: []*utils.Label{{Species: "Kiwi", Filter: "f", Certainty: 70}}},
    },
    }
    state := NewClassifyState(ClassifyConfig{Filter: "f", Reviewer: "Test", Certainty: -1}, []*utils.DataFile{df})
    // Confirm should upgrade 70 → 100
    if !state.ConfirmLabel() {
    t.Error("ConfirmLabel should return true for certainty < 100")
    }
    if df.Segments[0].Labels[0].Certainty != 100 {
    t.Errorf("certainty=%d, want 100", df.Segments[0].Labels[0].Certainty)
    }
    // Confirm again on 100 should be no-op
    if state.ConfirmLabel() {
    t.Error("ConfirmLabel should return false when already at 100")
    }
    }
  • replacement in me.txt at line 1458
    [2.29978][2.29978:30045]()
    write a PLAN.md, then begin with 1. we will take stock after that.
    [2.29978]
    write a PLAN.md, then begin with 1. we will take stock after that.
    Go line count:
    18737 total
    Test line count:
    11982 total
    go test -cover ./...
    ok skraak 2.548s coverage: 0.0% of statements
    ok skraak/cmd 0.005s coverage: 0.9% of statements
    ok skraak/db 1.269s coverage: 76.4% of statements
    ok skraak/tools 1.663s coverage: 58.4% of statements
    ok skraak/tools/calls 0.172s coverage: 60.7% of statements
    ok skraak/tools/import 0.007s coverage: 3.7% of statements
    skraak/tui coverage: 0.0% of statements
    ok skraak/utils 0.280s coverage: 78.8% of statements
    goreportcard-cli
    2026/05/13 15:49:22 disabling ineffassign on large repo...
    Grade .......... A+ 100.0%
    Files ................ 147
    Issues ................. 0
    go_vet .............. 100%
    gofmt ............... 100%
    ineffassign ......... 100%
    gocyclo ............. 100%
    license ............. 100%
    misspell ............ 100%
    Go line count:
    18864 total
    Test line count:
    13489 total
  • edit in db/validation.go at line 11
    [10.25460]
    [10.25460]
    Query(query string, args ...any) (*sql.Rows, error)
  • edit in db/testdb.go at line 128
    [3.36979]
    // InsertTestFilter inserts a filter row.
    func InsertTestFilter(t *testing.T, database *sql.DB, id, name string) {
    t.Helper()
    _, err := database.Exec(
    "INSERT INTO filter (id, name, active) VALUES (?, ?, true)",
    id, name,
    )
    if err != nil {
    t.Fatalf("failed to insert filter: %v", err)
    }
    }
    // InsertTestFileForCluster inserts a file row linked to a cluster.
    func InsertTestFileForCluster(t *testing.T, database *sql.DB, id, clusterID, locationID, fileName, hash string, duration float64) {
    t.Helper()
    _, err := database.Exec(
    `INSERT INTO file (id, file_name, xxh64_hash, location_id, cluster_id, timestamp_local, duration, sample_rate, active)
    VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?, 48000, true)`,
    id, fileName, hash, locationID, clusterID, duration,
    )
    if err != nil {
    t.Fatalf("failed to insert file: %v", err)
    }
    }
  • replacement in CHANGELOG.md at line 5
    [3.39657][3.39657:39726]()
    ## [2026-05-13] Test coverage push: Priority 1 + 2 (37.9% → 41.8%)
    [3.39657]
    [3.39726]
    ## [2026-05-13] Test coverage push: Priority 1+2+3 (37.9% → 44.1%)
  • replacement in CHANGELOG.md at line 20
    [3.40473][3.40473:40752]()
    - `db/testdb.go`: Extracted `SetupTestDB`, `InsertTestDataset`, `InsertTestDatasetWithType`, `InsertTestLocation`, `InsertTestCluster`, `InsertTestFile`, `InsertTestSpecies`, `InsertTestCallType`, `InsertTestPattern` from `invariants_test.go`. Available across `db/` test files.
    [3.40473]
    [3.40752]
    - `db/testdb.go`: Extracted `SetupTestDB`, `InsertTestDataset`, `InsertTestDatasetWithType`, `InsertTestLocation`, `InsertTestCluster`, `InsertTestFile`, `InsertTestFileForCluster`, `InsertTestSpecies`, `InsertTestCallType`, `InsertTestPattern`, `InsertTestFilter` from `invariants_test.go`. Available across `db/` test files.
  • replacement in CHANGELOG.md at line 25
    [3.40850][3.40850:41337]()
    - `db/validation_test.go`: All 11 validation functions tested against in-memory DuckDB with full schema: `GetDatasetType`, `ValidateDatasetTypeForImport`, `ValidateDatasetTypeUnstructured`, `ValidateLocationBelongsToDataset`, `DatasetExistsAndActive`, `LocationBelongsToDataset`, `ClusterExistsAndActive`, `PatternExistsAndActive`, `LocationExistsAndActive`, `ValidateDatasetTypeForExport`, `ClusterBelongsToLocation`. Each tested for valid, inactive, nonexistent, and mismatched cases.
    [3.40850]
    [3.41337]
    - `db/validation_test.go`: All 11 validation functions tested against in-memory DuckDB with full schema.
    - Extended `db.Querier` interface to include `Query` method (both `*sql.DB` and `*LoggedTx` already implement it).
  • replacement in CHANGELOG.md at line 30
    [3.41383][3.41383:41722]()
    - `utils/mapping_db_test.go`: `collectMappedLabels`, `collectUnmappedCalltypes`, `validateMappedSpecies`, `validateMappedCalltypes`, `ValidateMappingAgainstDB` tested against in-memory DuckDB with species/call_type tables. Covers valid mapping, missing species, missing DB species, inactive species, sentinel exclusion, missing calltypes.
    [3.41383]
    [3.41722]
    - `utils/mapping_db_test.go`: `collectMappedLabels`, `collectUnmappedCalltypes`, `validateMappedSpecies`, `validateMappedCalltypes`, `ValidateMappingAgainstDB` tested against in-memory DuckDB.
  • replacement in CHANGELOG.md at line 34
    [3.41790][3.41790:41910]()
    - `tools/calls/calls_clip_test.go`: `filterSegments`, `validateClipInput`, `accumulateFileResult` — pure logic tests.
    [3.41790]
    [3.41910]
    - `tools/calls/calls_clip_test.go`: `filterSegments`, `validateClipInput`, `accumulateFileResult`.
  • replacement in CHANGELOG.md at line 38
    [3.41976][3.41976:42156]()
    - `tools/export_test.go`: `orderByFKDependency`, `buildOwnedViaQuery`, `buildCountOwnedViaQuery`, `checkOutputFile`, `datasetTables` manifest completeness and relation validation.
    [3.41976]
    [3.42156]
    - `tools/export_test.go`: `orderByFKDependency`, `buildOwnedViaQuery`, `buildCountOwnedViaQuery`, `checkOutputFile`, `datasetTables` manifest completeness.
    ### 3a: Import Querier refactor (3.7% → 15.1%)
    - Refactored `tools/import/import_segments.go`: `validateSegmentHierarchy`, `validateFiltersExist`, `loadSpeciesCalltypeIDs`, `validateAndMapFiles` now accept `db.Querier` instead of `*sql.DB`.
    - Refactored `tools/import/bulk_file_import.go`: `bulkValidateLocationsBelongToDataset` now accepts `db.Querier`.
    - `tools/import/import_segments_validation_test.go`: Tests for all 4 Querier-based validation functions against in-memory DuckDB.
    ### 3b: Classify navigation tests (58.4% → 65.1%)
    - `tools/calls/calls_classify_nav_test.go`: `NextSegment`, `PrevSegment`, `buildClassifyState` (goto), `FormatLabels`, `NextBookmark`/`PrevBookmark`, `ToggleBookmark`, `ConfirmLabel`.
  • replacement in CHANGELOG.md at line 54
    [3.42247][3.42247:42281]()
    | db | 51.6% | 76.4% |
    [3.42247]
    [3.42281]
    | db | 51.6% | 75.0% |
  • replacement in CHANGELOG.md at line 56
    [3.42315][3.42315:42349]()
    | tools/calls | 58.4% | 60.7% |
    [3.42315]
    [3.42349]
    | tools/calls | 58.4% | 65.1% |
  • replacement in CHANGELOG.md at line 58
    [3.42383][3.42383:42423]()
    | **Total** | **37.9%** | **41.8%** |
    [3.42383]
    [3.42423]
    | tools/import| 3.7% | 15.1% |
    | **Total** | **37.9%** | **44.1%** |