package utils
import (
"os"
"path/filepath"
"testing"
)
func TestLoadMappingFile(t *testing.T) {
t.Run("valid mapping", func(t *testing.T) {
content := `{
"GSK": {"species": "Roroa", "calltypes": {"Male": "Male - Solo"}},
"Don't Know": {"species": "Don't Know"}
}`
path := createTempFile(t, content)
defer os.Remove(path)
mapping, err := LoadMappingFile(path)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
if len(mapping) != 2 {
t.Errorf("expected 2 entries, got %d", len(mapping))
}
if mapping["GSK"].Species != "Roroa" {
t.Errorf("expected GSK -> Roroa, got %s", mapping["GSK"].Species)
}
if mapping["GSK"].Calltypes["Male"] != "Male - Solo" {
t.Errorf("expected GSK Male -> Male - Solo, got %s", mapping["GSK"].Calltypes["Male"])
}
})
t.Run("invalid JSON", func(t *testing.T) {
content := `{invalid json}`
path := createTempFile(t, content)
defer os.Remove(path)
_, err := LoadMappingFile(path)
if err == nil {
t.Fatal("expected error for invalid JSON")
}
})
t.Run("empty file", func(t *testing.T) {
content := `{}`
path := createTempFile(t, content)
defer os.Remove(path)
_, err := LoadMappingFile(path)
if err == nil {
t.Fatal("expected error for empty mapping")
}
})
t.Run("missing species field", func(t *testing.T) {
content := `{"GSK": {"calltypes": {"Male": "Male - Solo"}}}`
path := createTempFile(t, content)
defer os.Remove(path)
_, err := LoadMappingFile(path)
if err == nil {
t.Fatal("expected error for missing species field")
}
})
t.Run("empty species field", func(t *testing.T) {
content := `{"GSK": {"species": ""}}`
path := createTempFile(t, content)
defer os.Remove(path)
_, err := LoadMappingFile(path)
if err == nil {
t.Fatal("expected error for empty species field")
}
})
t.Run("nonexistent file", func(t *testing.T) {
_, err := LoadMappingFile("/nonexistent/path/mapping.json")
if err == nil {
t.Fatal("expected error for nonexistent file")
}
})
}
func TestGetDBSpecies(t *testing.T) {
mapping := MappingFile{
"GSK": {Species: "Roroa"},
"K-M": {Species: "Kiwi"},
}
t.Run("found", func(t *testing.T) {
species, ok := mapping.GetDBSpecies("GSK")
if !ok {
t.Fatal("expected to find GSK")
}
if species != "Roroa" {
t.Errorf("expected Roroa, got %s", species)
}
})
t.Run("not found", func(t *testing.T) {
_, ok := mapping.GetDBSpecies("UNKNOWN")
if ok {
t.Fatal("expected not to find UNKNOWN")
}
})
}
func TestGetDBCalltype(t *testing.T) {
mapping := MappingFile{
"GSK": {
Species: "Roroa",
Calltypes: map[string]string{
"Male": "Male - Solo",
"Female": "Female - Solo",
},
},
"K-M": {Species: "Kiwi"}, }
t.Run("with mapping", func(t *testing.T) {
ct := mapping.GetDBCalltype("GSK", "Male")
if ct != "Male - Solo" {
t.Errorf("expected 'Male - Solo', got %s", ct)
}
})
t.Run("without mapping - passthrough", func(t *testing.T) {
ct := mapping.GetDBCalltype("GSK", "Unknown")
if ct != "Unknown" {
t.Errorf("expected passthrough 'Unknown', got %s", ct)
}
})
t.Run("species not in mapping - passthrough", func(t *testing.T) {
ct := mapping.GetDBCalltype("UNKNOWN", "Male")
if ct != "Male" {
t.Errorf("expected passthrough 'Male', got %s", ct)
}
})
t.Run("species without calltypes - passthrough", func(t *testing.T) {
ct := mapping.GetDBCalltype("K-M", "Male")
if ct != "Male" {
t.Errorf("expected passthrough 'Male', got %s", ct)
}
})
}
func TestMappingValidationResult(t *testing.T) {
t.Run("HasErrors - no errors", func(t *testing.T) {
r := MappingValidationResult{}
if r.HasErrors() {
t.Error("expected no errors")
}
})
t.Run("HasErrors - missing species", func(t *testing.T) {
r := MappingValidationResult{MissingSpecies: []string{"UNKNOWN"}}
if !r.HasErrors() {
t.Error("expected errors")
}
})
t.Run("HasErrors - missing DB species", func(t *testing.T) {
r := MappingValidationResult{MissingDBSpecies: []string{"Phantom"}}
if !r.HasErrors() {
t.Error("expected errors")
}
})
t.Run("HasErrors - missing calltypes", func(t *testing.T) {
r := MappingValidationResult{MissingCalltypes: map[string]string{"GSK/Male": "Roroa/Male - Solo"}}
if !r.HasErrors() {
t.Error("expected errors")
}
})
t.Run("Error - all error types", func(t *testing.T) {
r := MappingValidationResult{
MissingSpecies: []string{"UNKNOWN"},
MissingDBSpecies: []string{"Phantom"},
MissingCalltypes: map[string]string{"GSK/Male": "Roroa/Male - Solo"},
}
errStr := r.Error()
if errStr == "" {
t.Error("expected non-empty error string")
}
if !containsSubstring(errStr, "UNKNOWN") {
t.Error("error string should contain MISSING species")
}
if !containsSubstring(errStr, "Phantom") {
t.Error("error string should contain missing DB species")
}
if !containsSubstring(errStr, "GSK/Male") {
t.Error("error string should contain missing calltype")
}
})
}
func createTempFile(t *testing.T, content string) string {
t.Helper()
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "mapping.json")
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
return path
}
func containsSubstring(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsSubstringHelper(s, substr))
}
func containsSubstringHelper(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
func TestMappingClassify(t *testing.T) {
m := MappingFile{
"noise": {Species: MappingNegative},
"ignore": {Species: MappingIgnore},
"kiwi": {Species: "Kiwi"},
}
c, k, ok := m.Classify("noise")
if !ok || k != MappingNeg || c != "" {
t.Error("failed classify negative")
}
c, k, ok = m.Classify("ignore")
if !ok || k != MappingIgn || c != "" {
t.Error("failed classify ignore")
}
c, k, ok = m.Classify("kiwi")
if !ok || k != MappingReal || c != "Kiwi" {
t.Error("failed classify real")
}
_, _, ok = m.Classify("missing")
if ok {
t.Error("expected missing to be not ok")
}
}
func TestMappingValidateCoversSpecies(t *testing.T) {
m := MappingFile{"kiwi": {Species: "Kiwi"}}
missing := m.ValidateCoversSpecies(map[string]bool{"kiwi": true, "tui": true})
if len(missing) != 1 || missing[0] != "tui" {
t.Errorf("expected [tui], got %v", missing)
}
}
func TestMappingClasses(t *testing.T) {
m := MappingFile{
"noise": {Species: MappingNegative},
"kiwi": {Species: "Kiwi"},
"tui": {Species: "Tui"},
"duplicate": {Species: "Kiwi"},
}
classes := m.Classes()
if len(classes) != 2 || classes[0] != "Kiwi" || classes[1] != "Tui" {
t.Errorf("expected [Kiwi, Tui], got %v", classes)
}
}