import_segments_validation_test.go
package imp
import (
"strings"
"testing"
"skraak/datafile"
"skraak/db"
)
// --- validateSegmentHierarchy ---
func TestValidateSegmentHierarchy(t *testing.T) {
database := setupImportTestDB(t)
defer database.Close()
t.Run("valid hierarchy", func(t *testing.T) {
err := validateSegmentHierarchy(database, "dstest000001", "loctest00001", "cltest000001")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
})
t.Run("unstructured dataset rejected", func(t *testing.T) {
err := validateSegmentHierarchy(database, "dstest000002", "loctest00001", "cltest000001")
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, "dstest000001", "loctest00002", "cltest000001")
if err == nil {
t.Error("expected error for inactive location")
}
})
t.Run("inactive cluster rejected", func(t *testing.T) {
err := validateSegmentHierarchy(database, "dstest000001", "loctest00001", "cltest000002")
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, "test.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"] != "fitest000001" {
t.Errorf("kiwi.txt ID = %q, want fitest000001", 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 := 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"] != "sptest000001" {
t.Errorf("Kiwi ID = %q, want sptest000001", speciesMap["Kiwi"])
}
if speciesMap["Roroa"] != "sptest000002" {
t.Errorf("Roroa ID = %q, want sptest000002", 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"] != "cttest000001" {
t.Errorf("Kiwi/song ID = %q, want cttest000001", calltypeMap["Kiwi"]["song"])
}
if calltypeMap["Kiwi"]["duet"] != "cttest000002" {
t.Errorf("Kiwi/duet ID = %q, want cttest000002", 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, "cltest000001", "dstest000001")
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"
hash := createTestWAV(t, wavPath)
// Insert a file with the actual hash
db.InsertTestFileForCluster(t, database, "fitest000004", "cltest000001", "loctest00001", "test.wav", hash, 60.0)
// Link file to dataset
mustExec(t, database, "INSERT INTO file_dataset (file_id, dataset_id, created_at, last_modified) VALUES (?, ?, now(), now())", "fitest000004", "dstest000001")
scanned := []scannedDataFile{{
DataPath: tmpDir + "/test.data",
WavPath: wavPath,
Duration: 60.0,
Segments: []*datafile.Segment{{StartTime: 1.0, EndTime: 2.0, Labels: []*datafile.Label{}}},
}}
fileMap, errors := validateAndMapFiles(database, scanned, "cltest000001", "dstest000001")
// 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"
createTestWAV(t, wavPath)
scanned := []scannedDataFile{{
DataPath: tmpDir + "/test.data",
WavPath: wavPath,
Duration: 60.0,
Segments: []*datafile.Segment{{StartTime: 1.0, EndTime: 2.0, Labels: []*datafile.Label{}}},
}}
fileMap, errors := validateAndMapFiles(database, scanned, "cltest000001", "dstest000001")
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")
}
}