Fork channel

Create a new channel as a copy of main.

Rename channel

Rename main to:

Delete channel

Delete main? This cannot be undone.

calls_remove_test.go
package calls

import (
	"os"
	"path/filepath"
	"testing"

	"skraak/datafile"
)

func TestCallsRemoveLabel(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60, Reviewer: "AI"},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
					{Species: "Tui", Certainty: 90, Filter: "BirdNET"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.Removed != "label" {
		t.Errorf("expected removed=label, got %s", result.Removed)
	}
	if result.Species != "Kiwi" {
		t.Errorf("expected species=Kiwi, got %s", result.Species)
	}

	// Verify: segment should still exist with BirdNET label
	df2, err := datafile.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse file: %v", err)
	}
	if len(df2.Segments) != 1 {
		t.Fatalf("expected 1 segment, got %d", len(df2.Segments))
	}
	if len(df2.Segments[0].Labels) != 1 {
		t.Errorf("expected 1 label remaining, got %d", len(df2.Segments[0].Labels))
	}
	if df2.Segments[0].Labels[0].Species != "Tui" {
		t.Errorf("expected remaining label=Tui, got %s", df2.Segments[0].Labels[0].Species)
	}
	if df2.Meta.Reviewer != "David" {
		t.Errorf("expected Reviewer=David, got %s", df2.Meta.Reviewer)
	}
}

func TestCallsRemoveSegment(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
				},
			},
			{
				StartTime: 10,
				EndTime:   15,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Tui", Certainty: 90, Filter: "Manual"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.Removed != "segment" {
		t.Errorf("expected removed=segment, got %s", result.Removed)
	}

	// Verify: only one segment remains
	df2, err := datafile.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse file: %v", err)
	}
	if len(df2.Segments) != 1 {
		t.Errorf("expected 1 segment, got %d", len(df2.Segments))
	}
	if df2.Segments[0].Labels[0].Species != "Tui" {
		t.Errorf("expected remaining segment with Tui, got %s", df2.Segments[0].Labels[0].Species)
	}
}

func TestCallsRemoveFile(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.Removed != "file" {
		t.Errorf("expected removed=file, got %s", result.Removed)
	}

	// Verify: file should be deleted
	if _, err := os.Stat(dataPath); !os.IsNotExist(err) {
		t.Error("expected .data file to be deleted")
	}
}

func TestCallsRemoveAmbiguousCallType(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual", CallType: "Duet"},
					{Species: "Kiwi", Certainty: 90, Filter: "Manual", CallType: "Alarm"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Remove without calltype should error
	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err == nil {
		t.Error("expected error for ambiguous calltype, got nil")
	}
	if result.Error == "" {
		t.Error("expected error message in output")
	}
}

func TestCallsRemoveWithCallType(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual", CallType: "Duet"},
					{Species: "Kiwi", Certainty: 90, Filter: "Manual", CallType: "Alarm"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Remove with specific calltype
	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi+Duet",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.Removed != "label" {
		t.Errorf("expected removed=label, got %s", result.Removed)
	}

	// Verify: one label remains
	df2, err := datafile.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse file: %v", err)
	}
	if len(df2.Segments[0].Labels) != 1 {
		t.Errorf("expected 1 label remaining, got %d", len(df2.Segments[0].Labels))
	}
	if df2.Segments[0].Labels[0].CallType != "Alarm" {
		t.Errorf("expected remaining calltype=Alarm, got %s", df2.Segments[0].Labels[0].CallType)
	}
}

func TestCallsRemoveAmbiguousFreq(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  8000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
				},
			},
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   200,
				FreqHigh:  4500,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 90, Filter: "Manual"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Remove without frequency should error (ambiguous)
	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err == nil {
		t.Error("expected error for ambiguous frequency, got nil")
	}
	if result.Error == "" {
		t.Error("expected error message in output")
	}
}

func TestCallsRemoveWithFrequency(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  8000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
				},
			},
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   200,
				FreqHigh:  4500,
				Labels: []*datafile.Label{
					{Species: "Tui", Certainty: 90, Filter: "Manual"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Remove with specific frequency
	result, err := CallsRemove(CallsRemoveInput{
		File:      dataPath,
		Segment:   "2-5",
		Frequency: "200-4500",
		Species:   "Tui",
		Filter:    "Manual",
		Reviewer:  "David",
	})

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.Removed != "segment" {
		t.Errorf("expected removed=segment, got %s", result.Removed)
	}

	// Verify: one segment remains
	df2, err := datafile.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse file: %v", err)
	}
	if len(df2.Segments) != 1 {
		t.Errorf("expected 1 segment, got %d", len(df2.Segments))
	}
	if df2.Segments[0].Labels[0].Species != "Kiwi" {
		t.Errorf("expected remaining species=Kiwi, got %s", df2.Segments[0].Labels[0].Species)
	}
}

func TestCallsRemoveFileNotFound(t *testing.T) {
	result, err := CallsRemove(CallsRemoveInput{
		File:     "/tmp/nonexistent_xyz.data",
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err == nil {
		t.Error("expected error for missing file, got nil")
	}
	if result.Error == "" {
		t.Error("expected error message in output")
	}
}

func TestCallsRemoveNoMatchingLabel(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   0,
				FreqHigh:  16000,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Try to remove a species that doesn't exist
	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Tui",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err == nil {
		t.Error("expected error for no matching label, got nil")
	}
	if result.Error == "" {
		t.Error("expected error message in output")
	}
}

func TestCallsRemoveSingleSegmentNoFreqNeeded(t *testing.T) {
	tmpDir := t.TempDir()
	dataPath := filepath.Join(tmpDir, "test.data")

	df := &datafile.DataFile{
		Meta: &datafile.DataMeta{Operator: "Manual", Duration: 60},
		Segments: []*datafile.Segment{
			{
				StartTime: 2,
				EndTime:   5,
				FreqLow:   200,
				FreqHigh:  4500,
				Labels: []*datafile.Label{
					{Species: "Kiwi", Certainty: 80, Filter: "Manual"},
				},
			},
		},
	}
	if err := df.Write(dataPath); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Only one segment matches 2-5, so frequency not needed
	result, err := CallsRemove(CallsRemoveInput{
		File:     dataPath,
		Segment:  "2-5",
		Species:  "Kiwi",
		Filter:   "Manual",
		Reviewer: "David",
	})

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if result.Removed != "file" {
		t.Errorf("expected removed=file, got %s", result.Removed)
	}
	// Check that LowFreq/HighFreq are populated from the segment
	if result.LowFreq != 200 {
		t.Errorf("expected low_freq=200, got %f", result.LowFreq)
	}
	if result.HighFreq != 4500 {
		t.Errorf("expected high_freq=4500, got %f", result.HighFreq)
	}
}