"""Acceptance: a private recipe is reachable by its owner only – in no view and under no URL, including image URLs. Other users and admins get 404 (indistinguishable from "does not exist").""" import pytest from django.test import Client from django.urls import reverse from recipes.models import Favorite, Recipe, RecipeImage from .recipe_helpers import image_upload, make_recipe pytestmark = pytest.mark.django_db SECRET_TITLE = "Geheimrezept Omas Gulasch" SECRET_INGREDIENT = "Zauberpaprika" SECRET_TAG = "geheimtag" @pytest.fixture def private_recipe(user): recipe = make_recipe(user, title=SECRET_TITLE, visibility=Recipe.Visibility.PRIVATE, ingredients=f"500 g {SECRET_INGREDIENT}", tags=(SECRET_TAG,)) owner_client(user).post(reverse("recipes:photo_upload", args=[recipe.pk]), {"photos": [image_upload()]}) assert recipe.images.count() == 1 return recipe @pytest.fixture def image(private_recipe): return RecipeImage.objects.get(recipe=private_recipe) @pytest.fixture(params=["other", "admin"]) def outsider(request, other_user, admin_user): c = Client() c.force_login(other_user if request.param == "other" else admin_user) return c def owner_client(user): c = Client() c.force_login(user) return c def get_urls(recipe, image): return [ reverse("recipes:detail", args=[recipe.pk]), reverse("recipes:detail", args=[recipe.pk]) + "?portionen=8", reverse("recipes:edit", args=[recipe.pk]), reverse("recipes:delete", args=[recipe.pk]), reverse("recipes:photo", args=[image.uuid, "gross"]), reverse("recipes:photo", args=[image.uuid, "klein"]), *extra_get_urls(recipe, image), ] def post_urls(recipe, image): return [ reverse("recipes:edit", args=[recipe.pk]), reverse("recipes:delete", args=[recipe.pk]), reverse("recipes:favorite", args=[recipe.pk]), reverse("recipes:photo_upload", args=[recipe.pk]), reverse("recipes:photo_cover", args=[recipe.pk, image.pk]), reverse("recipes:photo_delete", args=[recipe.pk, image.pk]), *extra_post_urls(recipe, image), ] def extra_get_urls(recipe, image): """Hook for later features (comments, cooking mode, …).""" return [] def extra_post_urls(recipe, image): return [] def test_owner_sees_everything(user, private_recipe, image): c = owner_client(user) for url in get_urls(private_recipe, image): assert c.get(url).status_code == 200, url assert SECRET_TITLE in c.get(reverse("recipes:list")).content.decode() def test_outsider_gets_404_on_every_get_url(outsider, private_recipe, image): for url in get_urls(private_recipe, image): assert outsider.get(url).status_code == 404, url def test_outsider_gets_404_on_every_post_url(outsider, private_recipe, image): for url in post_urls(private_recipe, image): response = outsider.post(url, {"title": "gehackt", "servings": 1, "visibility": "public", "text": "x"}) assert response.status_code == 404, url private_recipe.refresh_from_db() assert private_recipe.title == SECRET_TITLE and not private_recipe.is_public assert RecipeImage.objects.filter(pk=image.pk).exists() assert not Favorite.objects.exists() @pytest.mark.parametrize( "query", [ {}, {"q": "Geheimrezept"}, {"q": SECRET_INGREDIENT}, {"q": SECRET_TAG}, {"zutat": SECRET_INGREDIENT}, {"tag": SECRET_TAG}, {"ansicht": "favoriten"}, ], ) def test_private_recipe_absent_from_list_and_search(outsider, private_recipe, image, query): html = outsider.get(reverse("recipes:list"), query).content.decode() assert SECRET_TITLE not in html assert str(image.uuid) not in html assert reverse("recipes:detail", args=[private_recipe.pk]) not in html def test_private_tag_not_listed(outsider, private_recipe): assert f"#{SECRET_TAG}" not in outsider.get(reverse("recipes:list")).content.decode() def test_existing_favorite_disappears_when_recipe_turns_private(user, other_user): recipe = make_recipe(user, title="Bald privat") Favorite.objects.create(user=other_user, recipe=recipe) recipe.visibility = Recipe.Visibility.PRIVATE recipe.save() c = owner_client(other_user) assert "Bald privat" not in c.get(reverse("recipes:list"), {"ansicht": "favoriten"}).content.decode() assert c.get(reverse("recipes:detail", args=[recipe.pk])).status_code == 404 def test_anonymous_gets_redirected_everywhere(private_recipe, image): c = Client() for url in get_urls(private_recipe, image): response = c.get(url) assert response.status_code == 302 and "/anmelden/" in response["Location"], url def test_visible_to_queryset_rule(user, other_user, admin_user, private_recipe): public = make_recipe(user, title="Öffentlich") assert set(Recipe.objects.visible_to(user)) == {private_recipe, public} assert set(Recipe.objects.visible_to(other_user)) == {public} assert set(Recipe.objects.visible_to(admin_user)) == {public}