Files
bambuddy/backend/tests/integration/test_sponsor_prompt_api.py
T
maziggy a5fe5cb3d4 feat(sponsor-prompt): in-app toast at earned milestones
ghcr.io pull baseline (~10k/day rising → ~8-12k active installs) puts
  sponsor conversion at 0.08% — roughly an order of magnitude under
  industry-benchmark for OSS with visible CTA. The Settings banner from
  0d4b9d4e gives passive every-visit visibility on one page; this adds
  opt-out-able active visibility at moments where the user has just
  earned something with Bambuddy.

  Five trigger families with a 14-day cross-family cooldown: prints
  (100/500/1000/2500/5000), cost (100/500/1000 tracked filament +
  energy), archives (50/250/1000), anniversary (1 year), version-update
  (re-armable on each major bump). New sponsor_toast_state table with
  nullable user_id so auth-disabled installs get the same trigger logic
  through one code path (NULL-keyed install-default row).
2026-06-20 15:50:58 +02:00

49 lines
1.9 KiB
Python

"""Integration tests for /sponsor-prompt routes."""
from __future__ import annotations
import pytest
from httpx import AsyncClient
class TestSponsorPromptAPI:
@pytest.mark.asyncio
@pytest.mark.integration
async def test_check_returns_show_false_for_empty_install(self, async_client: AsyncClient):
response = await async_client.get("/api/v1/sponsor-prompt/check")
assert response.status_code == 200
body = response.json()
assert body["show"] is False
assert body.get("milestone") is None
@pytest.mark.asyncio
@pytest.mark.integration
async def test_dismiss_requires_milestone(self, async_client: AsyncClient):
response = await async_client.post("/api/v1/sponsor-prompt/dismiss", json={})
# Pydantic missing-field → 422.
assert response.status_code == 422
@pytest.mark.asyncio
@pytest.mark.integration
async def test_dismiss_returns_204(self, async_client: AsyncClient):
response = await async_client.post(
"/api/v1/sponsor-prompt/dismiss",
json={"milestone": "version-update"},
)
assert response.status_code == 204
@pytest.mark.asyncio
@pytest.mark.integration
async def test_check_then_dismiss_then_recheck_is_silent(self, async_client: AsyncClient):
"""End-to-end: even if no trigger is currently eligible, dismissing
anchors the cooldown, so a subsequent check stays {show: false}."""
first = await async_client.get("/api/v1/sponsor-prompt/check")
assert first.json()["show"] is False
dismiss = await async_client.post(
"/api/v1/sponsor-prompt/dismiss",
json={"milestone": "version-update"},
)
assert dismiss.status_code == 204
second = await async_client.get("/api/v1/sponsor-prompt/check")
assert second.json()["show"] is False