import pytest import requests as req_lib from unittest.mock import patch, MagicMock from plants.services.plantnet import identify, PlantNetError def _mock_response(results=None, status_code=200): mock = MagicMock() mock.status_code = status_code mock.json.return_value = {'results': results or []} if status_code == 429: mock.raise_for_status.side_effect = req_lib.HTTPError(response=mock) elif status_code >= 400: mock.raise_for_status.side_effect = req_lib.HTTPError(response=mock) else: mock.raise_for_status = MagicMock() return mock def _api_results(): return [ { 'score': 0.94, 'species': { 'scientificNameWithoutAuthor': 'Liatris spicata', 'commonNames': ['Blazing star', 'Pronkster'], 'family': {'scientificNameWithoutAuthor': 'Asteraceae'}, 'genus': {'scientificNameWithoutAuthor': 'Liatris'}, }, 'gbif': {'id': '2889739'}, }, { 'score': 0.04, 'species': { 'scientificNameWithoutAuthor': 'Liatris pycnostachya', 'commonNames': ['Prairie blazing star'], 'family': {'scientificNameWithoutAuthor': 'Asteraceae'}, 'genus': {'scientificNameWithoutAuthor': 'Liatris'}, }, 'gbif': {'id': '1234567'}, }, ] def test_identify_raises_without_api_key(settings): settings.PLANTNET_API_KEY = '' with pytest.raises(PlantNetError, match='API key'): identify([(b'fake', 'flower')]) def test_identify_parses_results(settings): settings.PLANTNET_API_KEY = 'test-key' with patch('plants.services.plantnet.requests.post', return_value=_mock_response(_api_results())): results = identify([(b'fake', 'flower')]) assert len(results) == 2 assert results[0]['scientific_name'] == 'Liatris spicata' assert results[0]['score'] == pytest.approx(0.94) assert 'Blazing star' in results[0]['common_names'] assert results[0]['family'] == 'Asteraceae' assert results[0]['genus'] == 'Liatris' assert results[0]['gbif_id'] == '2889739' def test_identify_raises_on_no_results(settings): settings.PLANTNET_API_KEY = 'test-key' with patch('plants.services.plantnet.requests.post', return_value=_mock_response([])): with pytest.raises(PlantNetError, match='No match'): identify([(b'fake', 'flower')]) def test_identify_raises_on_quota_exceeded(settings): settings.PLANTNET_API_KEY = 'test-key' with patch('plants.services.plantnet.requests.post', return_value=_mock_response(status_code=429)): with pytest.raises(PlantNetError, match='Daily limit'): identify([(b'fake', 'flower')]) def test_identify_raises_on_timeout(settings): settings.PLANTNET_API_KEY = 'test-key' with patch('plants.services.plantnet.requests.post', side_effect=req_lib.Timeout): with pytest.raises(PlantNetError, match='timed out'): identify([(b'fake', 'flower')]) def test_identify_raises_on_other_http_error(settings): settings.PLANTNET_API_KEY = 'test-key' with patch('plants.services.plantnet.requests.post', return_value=_mock_response(status_code=500)): with pytest.raises(PlantNetError): identify([(b'fake', 'flower')]) def test_identify_sends_correct_organs(settings): settings.PLANTNET_API_KEY = 'test-key' mock_post = MagicMock(return_value=_mock_response(_api_results())) with patch('plants.services.plantnet.requests.post', mock_post): identify([(b'img1', 'flower'), (b'img2', 'leaf')]) call_kwargs = mock_post.call_args data = call_kwargs[1]['data'] assert ('organs', 'flower') in data assert ('organs', 'leaf') in data