Files
vrobbler/tests/birds_tests/test_importer.py

140 lines
5.1 KiB
Python

from datetime import timedelta
import pytest
from birds.importer import (
import_birding_csv,
parse_bool,
parse_coords,
parse_duration,
parse_int,
parse_timestamp,
)
from birds.models import Bird, BirdingLocation, BirdingCSVImport
from scrobbles.models import Scrobble
class TestParserHelpers:
def test_parse_duration(self):
assert parse_duration("9 minute(s)") == 9
assert parse_duration("120 minute(s)") == 120
assert parse_duration("") is None
assert parse_duration(None) is None
assert parse_duration("not a duration") is None
def test_parse_coords(self):
loc = "Some Place, US (44.384, -68.805)"
lat, lon = parse_coords(loc)
assert lat == 44.384
assert lon == -68.805
def test_parse_coords_no_match(self):
loc = "Some Place, US"
lat, lon = parse_coords(loc)
assert lat is None
assert lon is None
def test_parse_timestamp(self):
dt = parse_timestamp("May 10, 2026", "4:15 PM")
assert dt is not None
assert dt.year == 2026
assert dt.month == 5
assert dt.day == 10
assert dt.hour == 16
assert dt.minute == 15
def test_parse_timestamp_no_time(self):
dt = parse_timestamp("May 10, 2026", "")
assert dt is not None
assert dt.year == 2026
def test_parse_timestamp_invalid(self):
assert parse_timestamp("not a date", "") is None
def test_parse_bool(self):
assert parse_bool("true") is True
assert parse_bool("True") is True
assert parse_bool("yes") is True
assert parse_bool("1") is True
assert parse_bool("false") is False
assert parse_bool("") is None
assert parse_bool(None) is None
def test_parse_int(self):
assert parse_int("42") == 42
assert parse_int("") is None
assert parse_int(None) is None
assert parse_int("not a number") is None
class TestImportBirdingCSV:
def test_import_creates_birds(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
assert Bird.objects.filter(common_name="Canada Goose").exists()
assert Bird.objects.filter(common_name="Northern Cardinal").exists()
def test_import_creates_location(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
assert BirdingLocation.objects.filter(title="Test Park").exists()
def test_import_creates_scrobble(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
assert Scrobble.objects.filter(
source="Birding CSV Import"
).count() == 1
def test_import_logdata_fields(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
scrobble = Scrobble.objects.filter(source="Birding CSV Import").first()
log = scrobble.log
assert log["duration_minutes"] == 9
assert log["observation_type"] == "Stationary"
assert log["party_size"] == 4
assert log["complete_checklist"] is True
assert len(log["birds"]) == 2
def test_import_sighting_details(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
scrobble = Scrobble.objects.filter(source="Birding CSV Import").first()
birds = scrobble.log["birds"]
cardinal = next(b for b in birds if b["quantity"] == 2)
assert cardinal["sighting_notes"] == "At the feeder"
def test_import_idempotent(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
import_birding_csv(birding_csv_file, user.id)
assert Scrobble.objects.filter(
source="Birding CSV Import"
).count() == 1
def test_import_bird_quantities(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
scrobble = Scrobble.objects.filter(source="Birding CSV Import").first()
birds = scrobble.log["birds"]
goose = next(b for b in birds if b["quantity"] == 6)
assert goose is not None
def test_import_sets_stop_timestamp(self, user, birding_csv_file):
import_birding_csv(birding_csv_file, user.id)
scrobble = Scrobble.objects.filter(source="Birding CSV Import").first()
assert scrobble.stop_timestamp is not None
expected = scrobble.timestamp + timedelta(minutes=9)
assert scrobble.stop_timestamp == expected
class TestBirdingCSVImportModel:
def test_create_import_model(self, db, user):
imp = BirdingCSVImport.objects.create(user=user)
assert imp.uuid is not None
assert imp.import_type == "Birding CSV"
assert "Birding" in str(imp)
@pytest.mark.django_db(transaction=True)
def test_process_via_model(self, user, birding_csv_file):
imp = BirdingCSVImport.objects.create(user=user)
with open(birding_csv_file, "rb") as f:
imp.csv_file.save("test.csv", f, save=True)
imp.process()
imp.refresh_from_db()
assert imp.process_count == 1
assert imp.processed_finished is not None