Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Broken Social Preview Links #4183

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions mealie/routes/spa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ def serve_recipe_with_meta_public(
public_repos = AllRepositories(session)
group = public_repos.groups.get_by_slug_or_id(group_slug)

if not group or group.preferences.private_group: # type: ignore
if not (group and group.preferences) or group.preferences.private_group:
return response_404()

group_repos = AllRepositories(session, group_id=group.id)
group_repos = AllRepositories(session, group_id=group.id, household_id=None)
recipe = group_repos.recipes.get_one(recipe_slug)

if not recipe or not recipe.settings.public: # type: ignore
if not (recipe and recipe.settings) or not recipe.settings.public:
return response_404()

# Inject meta tags
Expand All @@ -190,9 +190,9 @@ async def serve_recipe_with_meta(
return serve_recipe_with_meta_public(group_slug, recipe_slug, session)

try:
repos = AllRepositories(session, group_id=user.group_id)
group_repos = AllRepositories(session, group_id=user.group_id, household_id=None)

recipe = repos.recipes.get_one(recipe_slug, "slug")
recipe = group_repos.recipes.get_one(recipe_slug, "slug")
if recipe is None:
return response_404()

Expand All @@ -204,8 +204,8 @@ async def serve_recipe_with_meta(

async def serve_shared_recipe_with_meta(group_slug: str, token_id: str, session: Session = Depends(generate_session)):
try:
repos = AllRepositories(session)
token_summary = repos.recipe_share_tokens.get_one(token_id)
public_repos = AllRepositories(session, group_id=None)
token_summary = public_repos.recipe_share_tokens.get_one(token_id)
if token_summary is None:
raise Exception("Token Not Found")

Expand Down
131 changes: 126 additions & 5 deletions tests/integration_tests/test_spa.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
import pytest
from bs4 import BeautifulSoup

from mealie.routes.spa import MetaTag, inject_meta, inject_recipe_json
from mealie.routes import spa
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.recipe.recipe_share_token import RecipeShareTokenSave
from tests import data as test_data
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser


@pytest.fixture(autouse=True)
def set_spa_contents():
"""Inject a simple HTML string into the SPA module to enable metadata injection"""

spa.__contents = "<!DOCTYPE html><html><head></head><body></body></html>"


def set_group_is_private(unique_user: TestUser, *, is_private: bool):
group = unique_user.repos.groups.get_by_slug_or_id(unique_user.group_id)
assert group and group.preferences
group.preferences.private_group = is_private
unique_user.repos.group_preferences.update(group.id, group.preferences)


def set_recipe_is_public(unique_user: TestUser, recipe: Recipe, *, is_public: bool):
assert recipe.settings
recipe.settings.public = is_public
unique_user.repos.recipes.update(recipe.slug, recipe)


def create_recipe(user: TestUser) -> Recipe:
recipe = user.repos.recipes.create(
Recipe(
user_id=user.user_id,
group_id=user.group_id,
name=random_string(),
)
)
set_group_is_private(user, is_private=False)
set_recipe_is_public(user, recipe, is_public=True)

return recipe


def test_spa_metadata_injection():
Expand All @@ -22,9 +60,9 @@ def test_spa_metadata_injection():

assert title_tag and title_tag["content"]

new_title_tag = MetaTag(hid="og:title", property_name="og:title", content=random_string())
new_arbitrary_tag = MetaTag(hid=random_string(), property_name=random_string(), content=random_string())
new_html = inject_meta(str(soup), [new_title_tag, new_arbitrary_tag])
new_title_tag = spa.MetaTag(hid="og:title", property_name="og:title", content=random_string())
new_arbitrary_tag = spa.MetaTag(hid=random_string(), property_name=random_string(), content=random_string())
new_html = spa.inject_meta(str(soup), [new_title_tag, new_arbitrary_tag])

# verify changes were injected
soup = BeautifulSoup(new_html, "lxml")
Expand Down Expand Up @@ -63,8 +101,91 @@ def test_spa_recipe_json_injection():
soup = BeautifulSoup(f, "lxml")
assert "https://schema.org" not in str(soup)

html = inject_recipe_json(str(soup), schema)
html = spa.inject_recipe_json(str(soup), schema)

assert "@context" in html
assert "https://schema.org" in html
assert recipe_name in html


@pytest.mark.parametrize("use_public_user", [True, False])
@pytest.mark.asyncio
async def test_spa_serve_recipe_with_meta(unique_user: TestUser, use_public_user: bool):
recipe = create_recipe(unique_user)
user = unique_user.repos.users.get_by_username(unique_user.username)
assert user

response = await spa.serve_recipe_with_meta(
user.group_slug, recipe.slug, user=None if use_public_user else user, session=unique_user.repos.session
)
assert response.status_code == 200
assert "https://schema.org" in response.body.decode()
Fixed Show fixed Hide fixed
Dismissed Show dismissed Hide dismissed


@pytest.mark.parametrize("use_public_user", [True, False])
@pytest.mark.asyncio
async def test_spa_serve_recipe_with_meta_invalid_data(unique_user: TestUser, use_public_user: bool):
recipe = create_recipe(unique_user)
user = unique_user.repos.users.get_by_username(unique_user.username)
assert user

response = await spa.serve_recipe_with_meta(
random_string(), recipe.slug, user=None if use_public_user else user, session=unique_user.repos.session
)
assert response.status_code == 404

response = await spa.serve_recipe_with_meta(
user.group_slug, random_string(), user=None if use_public_user else user, session=unique_user.repos.session
)
assert response.status_code == 404

set_recipe_is_public(unique_user, recipe, is_public=False)
response = await spa.serve_recipe_with_meta(
user.group_slug, recipe.slug, user=None if use_public_user else user, session=unique_user.repos.session
)
if use_public_user:
assert response.status_code == 404
else:
assert response.status_code == 200

set_group_is_private(unique_user, is_private=True)
set_recipe_is_public(unique_user, recipe, is_public=True)
response = await spa.serve_recipe_with_meta(
user.group_slug, recipe.slug, user=None if use_public_user else user, session=unique_user.repos.session
)
if use_public_user:
assert response.status_code == 404
else:
assert response.status_code == 200


@pytest.mark.parametrize("use_private_group", [True, False])
@pytest.mark.parametrize("use_public_recipe", [True, False])
@pytest.mark.asyncio
async def test_spa_service_shared_recipe_with_meta(
unique_user: TestUser, use_private_group: bool, use_public_recipe: bool
):
group = unique_user.repos.groups.get_by_slug_or_id(unique_user.group_id)
assert group
recipe = create_recipe(unique_user)

# visibility settings shouldn't matter for shared recipes
set_group_is_private(unique_user, is_private=use_private_group)
set_recipe_is_public(unique_user, recipe, is_public=use_public_recipe)

token = unique_user.repos.recipe_share_tokens.create(
RecipeShareTokenSave(recipe_id=recipe.id, group_id=unique_user.group_id)
)

response = await spa.serve_shared_recipe_with_meta(group.slug, token.id, session=unique_user.repos.session)
assert response.status_code == 200
assert "https://schema.org" in response.body.decode()
Dismissed Show dismissed Hide dismissed


@pytest.mark.asyncio
async def test_spa_service_shared_recipe_with_meta_invalid_data(unique_user: TestUser):
group = unique_user.repos.groups.get_by_slug_or_id(unique_user.group_id)
assert group

response = await spa.serve_shared_recipe_with_meta(group.slug, random_string(), session=unique_user.repos.session)
assert response.status_code == 404
Loading