mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 19:21:33 +02:00
Backend: - pytest configuration with async support and coverage - Unit tests for notification service (23 tests) - Unit tests for smart plug manager (12 tests) - Unit tests for archive service (16 tests) - Integration tests for API endpoints - Fix: notifications now send immediately (digest is summary only) Frontend: - Vitest configuration with jsdom and coverage - MSW for API mocking - Component tests for Toggle, Button, Card, ConfirmModal (77 tests) - Test utilities with custom render wrapper CI/CD: - GitHub Actions workflow for automated testing - Backend lint, unit tests, integration tests - Frontend lint, type-check, unit tests, build
185 lines
6.4 KiB
Python
185 lines
6.4 KiB
Python
"""Integration tests for External Links API endpoints."""
|
|
|
|
import pytest
|
|
from httpx import AsyncClient
|
|
|
|
|
|
class TestExternalLinksAPI:
|
|
"""Integration tests for /api/v1/external-links endpoints."""
|
|
|
|
@pytest.fixture
|
|
async def link_factory(self, db_session):
|
|
"""Factory to create test external links."""
|
|
_counter = [0]
|
|
|
|
async def _create_link(**kwargs):
|
|
from backend.app.models.external_link import ExternalLink
|
|
|
|
_counter[0] += 1
|
|
counter = _counter[0]
|
|
|
|
defaults = {
|
|
"name": f"Test Link {counter}",
|
|
"url": f"https://example.com/{counter}",
|
|
"icon": "Link",
|
|
"sort_order": counter,
|
|
}
|
|
defaults.update(kwargs)
|
|
|
|
link = ExternalLink(**defaults)
|
|
db_session.add(link)
|
|
await db_session.commit()
|
|
await db_session.refresh(link)
|
|
return link
|
|
|
|
return _create_link
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_list_external_links_empty(self, async_client: AsyncClient):
|
|
"""Verify empty list when no links exist."""
|
|
response = await async_client.get("/api/v1/external-links/")
|
|
assert response.status_code == 200
|
|
assert isinstance(response.json(), list)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_list_external_links_with_data(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify list returns existing links."""
|
|
await link_factory(name="My Link")
|
|
response = await async_client.get("/api/v1/external-links/")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert any(link["name"] == "My Link" for link in data)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_create_external_link(self, async_client: AsyncClient):
|
|
"""Verify external link can be created."""
|
|
data = {
|
|
"name": "New Link",
|
|
"url": "https://new-link.example.com",
|
|
"icon": "ExternalLink",
|
|
}
|
|
response = await async_client.post("/api/v1/external-links/", json=data)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
assert result["name"] == "New Link"
|
|
assert result["url"] == "https://new-link.example.com"
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_get_external_link(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify single link can be retrieved."""
|
|
link = await link_factory(name="Get Test Link")
|
|
response = await async_client.get(f"/api/v1/external-links/{link.id}")
|
|
assert response.status_code == 200
|
|
assert response.json()["name"] == "Get Test Link"
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_get_external_link_not_found(self, async_client: AsyncClient):
|
|
"""Verify 404 for non-existent link."""
|
|
response = await async_client.get("/api/v1/external-links/9999")
|
|
assert response.status_code == 404
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_update_external_link(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify link can be updated."""
|
|
link = await link_factory(name="Original")
|
|
response = await async_client.patch(
|
|
f"/api/v1/external-links/{link.id}",
|
|
json={"name": "Updated", "url": "https://updated.example.com"}
|
|
)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
assert result["name"] == "Updated"
|
|
assert result["url"] == "https://updated.example.com"
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_delete_external_link(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify link can be deleted."""
|
|
link = await link_factory()
|
|
response = await async_client.delete(f"/api/v1/external-links/{link.id}")
|
|
assert response.status_code == 200
|
|
# Verify deleted
|
|
response = await async_client.get(f"/api/v1/external-links/{link.id}")
|
|
assert response.status_code == 404
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_reorder_external_links(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify links can be reordered."""
|
|
link1 = await link_factory(name="Link 1")
|
|
link2 = await link_factory(name="Link 2")
|
|
link3 = await link_factory(name="Link 3")
|
|
|
|
# Reorder: 3, 1, 2
|
|
response = await async_client.put(
|
|
"/api/v1/external-links/reorder",
|
|
json={"ids": [link3.id, link1.id, link2.id]}
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# First link should be link3
|
|
assert data[0]["id"] == link3.id
|
|
assert data[0]["sort_order"] == 0
|
|
|
|
|
|
class TestExternalLinksIconAPI:
|
|
"""Tests for external link icon upload/delete."""
|
|
|
|
@pytest.fixture
|
|
async def link_factory(self, db_session):
|
|
"""Factory to create test external links."""
|
|
async def _create_link(**kwargs):
|
|
from backend.app.models.external_link import ExternalLink
|
|
|
|
defaults = {
|
|
"name": "Icon Test Link",
|
|
"url": "https://example.com",
|
|
"icon": "Link",
|
|
"sort_order": 0,
|
|
}
|
|
defaults.update(kwargs)
|
|
|
|
link = ExternalLink(**defaults)
|
|
db_session.add(link)
|
|
await db_session.commit()
|
|
await db_session.refresh(link)
|
|
return link
|
|
|
|
return _create_link
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_get_icon_not_set(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify 404 when no custom icon is set."""
|
|
link = await link_factory()
|
|
response = await async_client.get(f"/api/v1/external-links/{link.id}/icon")
|
|
assert response.status_code == 404
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
async def test_delete_icon_when_none(
|
|
self, async_client: AsyncClient, link_factory, db_session
|
|
):
|
|
"""Verify deleting non-existent icon succeeds silently."""
|
|
link = await link_factory()
|
|
response = await async_client.delete(f"/api/v1/external-links/{link.id}/icon")
|
|
assert response.status_code == 200
|