import pytest from django.urls import reverse from datetime import date from plants.models import Plant, PruningLog @pytest.mark.django_db class TestDashboard: def test_returns_200(self, client): resp = client.get(reverse('dashboard')) assert resp.status_code == 200 def test_shows_total_plant_count(self, client): Plant.objects.create(name='Fern', location='Office') Plant.objects.create(name='Rose', location='Garden') resp = client.get(reverse('dashboard')) assert resp.context['total_plants'] == 2 def test_overdue_plant_appears_in_context(self, client): plant = Plant.objects.create(name='Rose', location='Garden', pruning_months=[3]) # No log — overdue relative to current date (month 3 is more than 6 months ago only if checked after September) # Use a month that's definitely in the recent past: use today's month - 1 resp = client.get(reverse('dashboard')) # Just check the view works; pruning logic is tested separately assert 'overdue' in resp.context def test_due_this_month_plant_appears_in_context(self, client): today = date.today() plant = Plant.objects.create( name='Apple', location='Garden', pruning_months=[today.month] ) resp = client.get(reverse('dashboard')) assert plant in resp.context['due_this_month'] @pytest.mark.django_db class TestPlantList: def test_returns_200(self, client): resp = client.get(reverse('plant_list')) assert resp.status_code == 200 def test_lists_plants(self, client): Plant.objects.create(name='Monstera', location='Living room') resp = client.get(reverse('plant_list')) assert 'Monstera' in resp.content.decode() def test_search_filters_by_name(self, client): Plant.objects.create(name='Monstera', location='Living room') Plant.objects.create(name='Rose', location='Garden') resp = client.get(reverse('plant_list') + '?q=Rose') content = resp.content.decode() assert 'Rose' in content assert 'Monstera' not in content def test_htmx_search_uses_partial_template(self, client): resp = client.get( reverse('plant_list') + '?q=rose', HTTP_HX_REQUEST='true', ) assert resp.status_code == 200 assert resp.templates[0].name == 'plants/partials/plant_list_results.html' def test_indoor_filter(self, client): Plant.objects.create(name='Fern', location='Office', is_indoor=True) Plant.objects.create(name='Rose', location='Garden', is_indoor=False) resp = client.get(reverse('plant_list') + '?filter=indoor') content = resp.content.decode() assert 'Fern' in content assert 'Rose' not in content @pytest.mark.django_db class TestPlantDetail: def test_returns_200(self, client): plant = Plant.objects.create(name='Fern', location='Office') resp = client.get(reverse('plant_detail', args=[plant.pk])) assert resp.status_code == 200 def test_404_for_missing_plant(self, client): resp = client.get(reverse('plant_detail', args=[9999])) assert resp.status_code == 404 def test_context_contains_plant_and_status(self, client): plant = Plant.objects.create(name='Rose', location='Garden', pruning_months=[]) resp = client.get(reverse('plant_detail', args=[plant.pk])) assert resp.context['plant'] == plant assert resp.context['pruning_status'] == 'no_schedule' @pytest.mark.django_db class TestLogPruning: def test_creates_pruning_log(self, client): plant = Plant.objects.create(name='Rose', location='Garden') resp = client.post( reverse('log_pruning', args=[plant.pk]), {'pruned_on': '2026-05-01', 'notes': ''}, ) assert resp.status_code == 200 assert PruningLog.objects.filter(plant=plant, pruned_on='2026-05-01').exists() def test_returns_pruning_strip_partial(self, client): plant = Plant.objects.create(name='Rose', location='Garden') resp = client.post( reverse('log_pruning', args=[plant.pk]), {'pruned_on': '2026-05-01', 'notes': ''}, ) assert resp.templates[0].name == 'plants/partials/pruning_strip.html'