bloombase/plants/tests/test_identify_views.py
Stephan Kerkman adea702a77 feat: implement step 3 field picker view and template
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 17:48:53 +02:00

209 lines
8.2 KiB
Python

import pytest
from django.test import Client
from django.urls import reverse
import io
from unittest.mock import patch
from django.core.files.uploadedfile import SimpleUploadedFile
@pytest.fixture
def client():
return Client()
def test_upload_get(client):
resp = client.get(reverse('identify_upload'))
assert resp.status_code == 200
def test_confirm_redirects_without_session(client):
resp = client.get(reverse('identify_confirm'))
assert resp.status_code == 302
assert reverse('identify_upload') in resp['Location']
def test_fields_redirects_without_session(client):
resp = client.get(reverse('identify_fields'))
assert resp.status_code == 302
assert reverse('identify_upload') in resp['Location']
def _fake_image(name='test.jpg'):
return SimpleUploadedFile(name, b'fake-image-bytes', content_type='image/jpeg')
def test_upload_post_no_images(client):
resp = client.post(reverse('identify_upload'), {})
assert resp.status_code == 200
assert b'select at least one' in resp.content.lower() or b'Please' in resp.content
@pytest.mark.django_db
def test_upload_post_calls_plantnet_and_redirects(client, settings):
settings.PLANTNET_API_KEY = 'test-key'
fake_matches = [
{'scientific_name': 'Liatris spicata', 'common_names': ['Blazing star'],
'score': 0.94, 'gbif_id': '123', 'family': 'Asteraceae', 'genus': 'Liatris'},
]
with patch('plants.views.identify.plantnet.identify', return_value=fake_matches):
with patch('plants.views.identify.default_storage') as mock_storage:
mock_storage.save.return_value = 'plants/temp_identify/abc_0.jpg'
mock_storage.path.return_value = '/tmp/abc_0.jpg'
resp = client.post(reverse('identify_upload'), {
'images': [_fake_image()],
'organs': ['flower'],
})
assert resp.status_code == 302
assert reverse('identify_confirm') in resp['Location']
assert client.session['identify_matches'] == fake_matches
def test_upload_post_plantnet_error_rerenders(client, settings):
settings.PLANTNET_API_KEY = 'test-key'
from plants.services.plantnet import PlantNetError
with patch('plants.views.identify.plantnet.identify',
side_effect=PlantNetError('No match found')):
with patch('plants.views.identify.default_storage') as mock_storage:
mock_storage.save.return_value = 'plants/temp_identify/abc_0.jpg'
mock_storage.path.return_value = '/tmp/abc_0.jpg'
mock_storage.exists.return_value = True
resp = client.post(reverse('identify_upload'), {
'images': [_fake_image()],
'organs': ['flower'],
})
assert resp.status_code == 200
assert b'No match found' in resp.content
def _set_matches(client):
session = client.session
session['identify_matches'] = [
{'scientific_name': 'Liatris spicata', 'common_names': ['Blazing star'],
'score': 0.94, 'gbif_id': '123', 'family': 'Asteraceae', 'genus': 'Liatris'},
{'scientific_name': 'Liatris pycnostachya', 'common_names': ['Prairie blazing star'],
'score': 0.04, 'gbif_id': '456', 'family': 'Asteraceae', 'genus': 'Liatris'},
]
session['identify_image_paths'] = [{'path': 'plants/temp_identify/x_0.jpg', 'organ': 'flower'}]
session.save()
@pytest.mark.django_db
def test_confirm_get_with_session(client):
_set_matches(client)
resp = client.get(reverse('identify_confirm'))
assert resp.status_code == 200
assert b'Liatris spicata' in resp.content
@pytest.mark.django_db
def test_confirm_post_stores_selection_and_redirects(client):
_set_matches(client)
with patch('plants.views.identify.vpc_search', return_value=[
{'slug': 'liatris-spicata', 'common_name': 'Liatris spicata', 'scientific_name': 'Liatris spicata', 'thumbnail_url': '', 'source': 'vpc'},
]):
resp = client.post(reverse('identify_confirm'), {'match_idx': '0'})
assert resp.status_code == 302
assert reverse('identify_fields') in resp['Location']
assert client.session['identify_selected']['scientific_name'] == 'Liatris spicata'
assert len(client.session['identify_vpc_results']) == 1
@pytest.mark.django_db
def test_confirm_post_handles_vpc_failure(client):
_set_matches(client)
from plants.services.vpc import VPCError
with patch('plants.views.identify.vpc_search', side_effect=VPCError('down')):
resp = client.post(reverse('identify_confirm'), {'match_idx': '0'})
assert resp.status_code == 302
assert client.session['identify_vpc_results'] == []
from plants.models import Plant, Species
def _set_fields_session(client, vpc_results=None):
session = client.session
session['identify_selected'] = {
'scientific_name': 'Liatris spicata',
'common_names': ['Blazing star', 'Pronkster'],
'score': 0.94, 'gbif_id': '123', 'family': 'Asteraceae', 'genus': 'Liatris',
}
session['identify_vpc_results'] = vpc_results if vpc_results is not None else [
{'slug': 'liatris-spicata', 'common_name': 'Liatris spicata', 'scientific_name': 'Liatris spicata', 'thumbnail_url': '', 'source': 'vpc'},
]
session['identify_vpc_idx'] = 0
session['identify_matches'] = []
session['identify_image_paths'] = []
session.save()
@pytest.mark.django_db
def test_fields_get_with_session(client):
_set_fields_session(client)
resp = client.get(reverse('identify_fields'))
assert resp.status_code == 200
assert b'Liatris spicata' in resp.content
@pytest.mark.django_db
def test_fields_post_creates_species_and_plant(client):
_set_fields_session(client)
with patch('plants.views.identify.default_storage') as mock_storage:
mock_storage.exists.return_value = False
with patch('plants.views.identify._apply_vpc_care_fields'):
resp = client.post(reverse('identify_fields'), {
'common_name_source': 'plantnet',
'scientific_name_source': 'plantnet',
'name': 'Liatris',
})
assert resp.status_code == 302
assert Plant.objects.filter(name='Liatris').exists()
species = Species.objects.get(scientific_name='Liatris spicata')
assert species.common_name == 'Blazing star'
@pytest.mark.django_db
def test_fields_post_deduplicates_species(client):
Species.objects.create(scientific_name='Liatris spicata', common_name='Old name')
_set_fields_session(client)
with patch('plants.views.identify.default_storage') as mock_storage:
mock_storage.exists.return_value = False
with patch('plants.views.identify._apply_vpc_care_fields'):
client.post(reverse('identify_fields'), {
'common_name_source': 'plantnet',
'scientific_name_source': 'plantnet',
'name': 'Liatris',
})
assert Species.objects.filter(scientific_name='Liatris spicata').count() == 1
@pytest.mark.django_db
def test_fields_post_clears_session(client):
_set_fields_session(client)
with patch('plants.views.identify.default_storage') as mock_storage:
mock_storage.exists.return_value = False
with patch('plants.views.identify._apply_vpc_care_fields'):
client.post(reverse('identify_fields'), {
'common_name_source': 'plantnet',
'scientific_name_source': 'plantnet',
'name': 'Liatris',
})
assert 'identify_selected' not in client.session
@pytest.mark.django_db
def test_fields_post_uses_vpc_common_name(client):
_set_fields_session(client, vpc_results=[
{'slug': 'liatris-spicata-kobold', 'common_name': "Liatris 'Kobold'",
'scientific_name': 'Liatris spicata', 'thumbnail_url': '', 'source': 'vpc'},
])
with patch('plants.views.identify.default_storage') as mock_storage:
mock_storage.exists.return_value = False
with patch('plants.views.identify._apply_vpc_care_fields'):
client.post(reverse('identify_fields'), {
'common_name_source': 'vpc',
'scientific_name_source': 'plantnet',
'name': 'Liatris Kobold',
})
species = Species.objects.get(scientific_name='Liatris spicata')
assert species.common_name == "Liatris 'Kobold'"