set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
DATA_DIR="$SCRIPT_DIR/data"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
if [ ! -f "$PROJECT_DIR/skraak" ]; then
echo -e "${RED}Error: skraak binary not found. Run 'go build' first.${NC}"
exit 1
fi
compare_calls_as_set() {
local actual="$1"
local expected="$2"
local actual_sorted
local expected_sorted
actual_sorted=$(jq '(.calls // []) | sort_by(.file, .start_time, .end_time, .ebird_code, .segments)' "$actual" 2>/dev/null)
expected_sorted=$(jq '(.calls // []) | sort_by(.file, .start_time, .end_time, .ebird_code, .segments)' "$expected" 2>/dev/null)
if [ "$actual_sorted" = "$expected_sorted" ]; then
return 0
else
return 1
fi
}
compare_metadata() {
local actual="$1"
local expected="$2"
local clip_dur_act clip_dur_exp
local gap_thr_act gap_thr_exp
local total_act total_exp
local species_act species_exp
clip_dur_act=$(jq -r '.clip_duration // "null"' "$actual")
clip_dur_exp=$(jq -r '.clip_duration // "null"' "$expected")
gap_thr_act=$(jq -r '.gap_threshold // "null"' "$actual")
gap_thr_exp=$(jq -r '.gap_threshold // "null"' "$expected")
total_act=$(jq -r '.total_calls // "null"' "$actual")
total_exp=$(jq -r '.total_calls // "null"' "$expected")
species_act=$(jq -r '.species_count' "$actual")
species_exp=$(jq -r '.species_count' "$expected")
local all_match=true
if [ "$clip_dur_act" != "$clip_dur_exp" ]; then
echo " clip_duration: expected=$clip_dur_exp, actual=$clip_dur_act"
all_match=false
fi
if [ "$gap_thr_act" != "$gap_thr_exp" ]; then
echo " gap_threshold: expected=$gap_thr_exp, actual=$gap_thr_act"
all_match=false
fi
if [ "$total_act" != "$total_exp" ]; then
echo " total_calls: expected=$total_exp, actual=$total_act"
all_match=false
fi
if [ "$species_act" != "$species_exp" ]; then
echo " species_count differs"
all_match=false
fi
if [ "$all_match" = true ]; then
return 0
else
return 1
fi
}
run_test() {
local name="$1"
local csv_path="$2"
local expected_json="$3"
((TESTS_RUN++)) || true
echo ""
echo "Testing: $name"
echo " CSV: $(basename "$csv_path")"
echo " Expected: $(basename "$expected_json")"
local actual_json stderr_output
actual_json=$(mktemp --suffix=.json)
stderr_output=$(mktemp --suffix=.txt)
echo " Running: skraak calls from-preds --csv ..."
if ! "$PROJECT_DIR/skraak" calls from-preds --csv "$csv_path" --dot-data=false > "$actual_json" 2>"$stderr_output"; then
echo -e " ${RED}✗ Command failed${NC}"
cat "$stderr_output"
rm -f "$stderr_output"
((TESTS_FAILED++)) || true
return
fi
cat "$stderr_output" | head -3
rm -f "$stderr_output"
if ! jq empty "$actual_json" 2>/dev/null; then
echo -e " ${RED}✗ Output is not valid JSON${NC}"
((TESTS_FAILED++)) || true
return
fi
local calls_match=false
if compare_calls_as_set "$actual_json" "$expected_json"; then
calls_match=true
fi
local metadata_match=false
local metadata_diff=""
if compare_metadata "$actual_json" "$expected_json"; then
metadata_match=true
fi
if [ "$calls_match" = true ]; then
echo -e " ${GREEN}✓ Calls array matches (set comparison)${NC}"
local call_count
call_count=$(jq '.calls | length' "$actual_json")
local species_count
species_count=$(jq '.species_count | keys | length' "$actual_json")
echo " $call_count calls across $species_count species"
if [ "$metadata_match" = true ]; then
echo -e " ${GREEN}✓ Metadata matches${NC}"
((TESTS_PASSED++)) || true
else
echo -e " ${YELLOW}⚠ Metadata differs (calls array is primary)${NC}"
compare_metadata "$actual_json" "$expected_json"
((TESTS_PASSED++)) || true
fi
else
echo -e " ${RED}✗ Calls array differs${NC}"
local actual_count expected_count
actual_count=$(jq '.calls | length' "$actual_json")
expected_count=$(jq '.calls | length' "$expected_json")
echo " Actual calls: $actual_count, Expected calls: $expected_count"
local missing extra
missing=$(jq -n --slurpfile exp "$expected_json" --slurpfile act "$actual_json" \
'([$exp[0].calls | .[] | {file, start_time, end_time, ebird_code, segments}] | sort) - ([$act[0].calls | .[] | {file, start_time, end_time, ebird_code, segments}] | sort) | length')
extra=$(jq -n --slurpfile exp "$expected_json" --slurpfile act "$actual_json" \
'([$act[0].calls | .[] | {file, start_time, end_time, ebird_code, segments}] | sort) - ([$exp[0].calls | .[] | {file, start_time, end_time, ebird_code, segments}] | sort) | length')
echo " Missing from actual: $missing calls"
echo " Extra in actual: $extra calls"
((TESTS_FAILED++)) || true
fi
rm -f "$actual_json" "$stderr_output"
}
print_summary() {
echo ""
echo "=== Summary ==="
echo "Tests run: $TESTS_RUN"
echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
if [ "$TESTS_FAILED" -gt 0 ]; then
echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
return 1
else
echo -e "Failed: $TESTS_FAILED"
return 0
fi
}
echo "=== Testing: skraak calls from-preds ==="
echo "Comparing calls arrays as SETS (order-independent)"
run_test \
"predsST (single species: Kiwi)" \
"$DATA_DIR/predsST_opensoundscape-kiwi-1.2_2025-11-12.csv" \
"$DATA_DIR/predsST_opensoundscape-kiwi-1.2_2025-11-12.json"
run_test \
"preds1 (multi-species)" \
"$DATA_DIR/preds1_opensoundscape-multi-1.0_2025-07-22.csv" \
"$DATA_DIR/preds1_opensoundscape-multi-1.0_2025-07-22.json"
print_summary