Files
Gondulf/tests/integration/services/test_happ_parser.py
Phil Skentelbery e1f79af347 feat(test): add Phase 5b integration and E2E tests
Add comprehensive integration and end-to-end test suites:
- Integration tests for API flows (authorization, token, verification)
- Integration tests for middleware chain and security headers
- Integration tests for domain verification services
- E2E tests for complete authentication flows
- E2E tests for error scenarios and edge cases
- Shared test fixtures and utilities in conftest.py
- Rename Dockerfile to Containerfile for Podman compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 22:22:04 -07:00

171 lines
5.7 KiB
Python

"""
Integration tests for h-app parser service.
Tests client metadata fetching with mocked HTTP responses.
"""
import pytest
from unittest.mock import MagicMock, Mock, patch
class TestHAppParserIntegration:
"""Integration tests for h-app metadata parsing."""
@pytest.fixture
def happ_parser_with_mock_fetcher(self):
"""Create h-app parser with mocked HTML fetcher."""
from gondulf.services.happ_parser import HAppParser
html = '''
<!DOCTYPE html>
<html>
<head><title>Test App</title></head>
<body>
<div class="h-app">
<h1 class="p-name">Example Application</h1>
<img class="u-logo" src="https://app.example.com/logo.png" alt="Logo">
<a class="u-url" href="https://app.example.com">Home</a>
</div>
</body>
</html>
'''
mock_fetcher = Mock()
mock_fetcher.fetch = Mock(return_value=html)
return HAppParser(html_fetcher=mock_fetcher)
def test_fetch_and_parse_happ_metadata(self, happ_parser_with_mock_fetcher):
"""Test fetching and parsing h-app microformat."""
import asyncio
result = asyncio.get_event_loop().run_until_complete(
happ_parser_with_mock_fetcher.fetch_and_parse("https://app.example.com")
)
assert result is not None
assert result.name == "Example Application"
assert result.logo == "https://app.example.com/logo.png"
def test_parse_page_without_happ(self, mock_urlopen):
"""Test parsing page without h-app returns fallback."""
from gondulf.services.happ_parser import HAppParser
from gondulf.services.html_fetcher import HTMLFetcherService
# Setup mock to return page without h-app
html = b'<html><head><title>Plain Page</title></head><body>No h-app</body></html>'
mock_response = MagicMock()
mock_response.read.return_value = html
mock_response.status = 200
mock_response.__enter__ = Mock(return_value=mock_response)
mock_response.__exit__ = Mock(return_value=False)
mock_urlopen.return_value = mock_response
fetcher = HTMLFetcherService()
parser = HAppParser(html_fetcher=fetcher)
import asyncio
result = asyncio.get_event_loop().run_until_complete(
parser.fetch_and_parse("https://app.example.com")
)
# Should return fallback metadata using domain
assert result is not None
assert "example.com" in result.name.lower() or result.name == "Plain Page"
def test_fetch_timeout_returns_fallback(self, mock_urlopen_timeout):
"""Test HTTP timeout returns fallback metadata."""
from gondulf.services.happ_parser import HAppParser
from gondulf.services.html_fetcher import HTMLFetcherService
fetcher = HTMLFetcherService()
parser = HAppParser(html_fetcher=fetcher)
import asyncio
result = asyncio.get_event_loop().run_until_complete(
parser.fetch_and_parse("https://slow-app.example.com")
)
# Should return fallback metadata
assert result is not None
# Should use domain as fallback name
assert "slow-app.example.com" in result.name or result.url == "https://slow-app.example.com"
class TestClientMetadataCaching:
"""Tests for client metadata caching behavior."""
def test_metadata_fetched_from_url(self, mock_urlopen_with_happ):
"""Test metadata is actually fetched from URL."""
from gondulf.services.happ_parser import HAppParser
from gondulf.services.html_fetcher import HTMLFetcherService
fetcher = HTMLFetcherService()
parser = HAppParser(html_fetcher=fetcher)
import asyncio
result = asyncio.get_event_loop().run_until_complete(
parser.fetch_and_parse("https://app.example.com")
)
# urlopen should have been called
mock_urlopen_with_happ.assert_called()
class TestHAppMicroformatVariants:
"""Tests for various h-app microformat formats."""
@pytest.fixture
def create_parser_with_html(self):
"""Factory to create parser with specific HTML content."""
def _create(html_content):
from gondulf.services.happ_parser import HAppParser
mock_fetcher = Mock()
mock_fetcher.fetch = Mock(return_value=html_content)
return HAppParser(html_fetcher=mock_fetcher)
return _create
def test_parse_happ_with_minimal_data(self, create_parser_with_html):
"""Test parsing h-app with only name."""
html = '''
<html>
<body>
<div class="h-app">
<span class="p-name">Minimal App</span>
</div>
</body>
</html>
'''
parser = create_parser_with_html(html)
import asyncio
result = asyncio.get_event_loop().run_until_complete(
parser.fetch_and_parse("https://minimal.example.com")
)
assert result.name == "Minimal App"
def test_parse_happ_with_logo_relative_url(self, create_parser_with_html):
"""Test parsing h-app with relative logo URL."""
html = '''
<html>
<body>
<div class="h-app">
<span class="p-name">Relative Logo App</span>
<img class="u-logo" src="/logo.png">
</div>
</body>
</html>
'''
parser = create_parser_with_html(html)
import asyncio
result = asyncio.get_event_loop().run_until_complete(
parser.fetch_and_parse("https://relative.example.com")
)
assert result.name == "Relative Logo App"
# Logo should be resolved to absolute URL
assert result.logo is not None