Newer
Older
navi-1 / tests / unit / api / test_push_routes.py
"""Unit tests for /push routes (patched service, no real container)."""


import pytest
from fastapi.testclient import TestClient

import navi.api.routes.push as push_mod


class FakeService:
    def __init__(self, enabled=True, public_key="PUBKEY"):
        self.enabled = enabled
        self.public_key = public_key
        self.subs: list[dict] = []
        self.deleted: list[str] = []

    async def subscribe(self, user_id, endpoint, p256dh, auth, user_agent=None):
        self.subs.append(dict(user_id=user_id, endpoint=endpoint,
                              p256dh=p256dh, auth=auth, user_agent=user_agent))
        return "sub-1"

    async def unsubscribe(self, endpoint):
        self.deleted.append(endpoint)


@pytest.fixture
def client(monkeypatch):
    service = FakeService()
    monkeypatch.setattr(push_mod, "get_push_service", lambda: service)

    from navi.main import app
    from navi.auth.deps import require_user, _ANONYMOUS_USER

    # require_user reads global settings; other tests (integration conftest)
    # leave dependency_overrides on the shared app — pin the anonymous user
    # so these tests don't depend on evaluation order.
    app.dependency_overrides[require_user] = lambda: _ANONYMOUS_USER.model_copy()
    try:
        yield TestClient(app), service
    finally:
        app.dependency_overrides.pop(require_user, None)


def test_vapid_key_ok(client):
    c, service = client
    resp = c.get("/push/vapid-key")
    assert resp.status_code == 200
    assert resp.json() == {"public_key": "PUBKEY"}


def test_vapid_key_503_when_disabled(client):
    c, service = client
    service.enabled = False
    assert c.get("/push/vapid-key").status_code == 503
    assert c.post("/push/subscribe", json={"endpoint": "e", "keys": {"p256dh": "p", "auth": "a"}}).status_code == 503


def test_subscribe_upserts(client):
    c, service = client
    resp = c.post("/push/subscribe", json={
        "endpoint": "https://push.example/ep1",
        "keys": {"p256dh": "P256DH", "auth": "AUTH"},
        "user_agent": "pytest",
    })
    assert resp.status_code == 200
    assert resp.json() == {"id": "sub-1", "status": "subscribed"}
    assert service.subs == [dict(
        user_id="anonymous", endpoint="https://push.example/ep1",
        p256dh="P256DH", auth="AUTH", user_agent="pytest",
    )]


def test_subscribe_rejects_missing_keys(client):
    c, _ = client
    resp = c.post("/push/subscribe", json={"endpoint": "e", "keys": {}})
    assert resp.status_code == 422
    resp = c.post("/push/subscribe", json={"endpoint": "e", "keys": {"p256dh": "p"}})
    assert resp.status_code == 422


def test_delete_subscribe_idempotent(client):
    c, service = client
    payload = {"endpoint": "https://push.example/ep1", "keys": {"p256dh": "p", "auth": "a"}}
    assert c.request("DELETE", "/push/subscribe", json=payload).status_code == 200
    assert c.request("DELETE", "/push/subscribe", json=payload).status_code == 200
    assert service.deleted == ["https://push.example/ep1"] * 2