We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/brianirish/laravel-docs-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Tests for community package documentation fetching."""
import pytest
import json
import tempfile
from pathlib import Path
from unittest.mock import patch, MagicMock
import sys
import os
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from docs_updater import CommunityPackageFetcher, DocumentationSourceType
class TestCommunityPackageFetcher:
"""Test community package documentation fetching functionality."""
@pytest.fixture
def temp_dir(self):
"""Create a temporary directory for testing."""
with tempfile.TemporaryDirectory() as temp:
yield Path(temp)
@pytest.fixture
def fetcher(self, temp_dir):
"""Create a CommunityPackageFetcher instance."""
return CommunityPackageFetcher(temp_dir)
def test_init(self, fetcher, temp_dir):
"""Test CommunityPackageFetcher initialization."""
assert fetcher.target_dir == temp_dir
assert fetcher.cache_duration == 86400
assert fetcher.max_retries == 3
assert (temp_dir / "packages").exists()
def test_list_available_packages(self, fetcher):
"""Test listing available packages."""
packages = fetcher.list_available_packages()
assert "spatie" in packages
assert "livewire" in packages
assert "inertia" in packages
assert "filament" in packages
assert "debugbar" in packages
assert "ide-helper" in packages
assert len(packages) == 6
def test_get_package_cache_path(self, fetcher, temp_dir):
"""Test getting package cache path."""
# Test main package
path = fetcher.get_package_cache_path("livewire")
assert path == temp_dir / "packages" / "livewire"
assert path.exists()
# Test sub-package
sub_path = fetcher.get_package_cache_path("spatie", "laravel-permission")
assert sub_path == temp_dir / "packages" / "spatie" / "laravel-permission"
assert sub_path.exists()
def test_get_cache_metadata_path(self, fetcher, temp_dir):
"""Test getting cache metadata path."""
# Test main package
path = fetcher.get_cache_metadata_path("livewire")
assert path == temp_dir / "packages" / "livewire" / ".metadata" / "cache.json"
# Test sub-package
sub_path = fetcher.get_cache_metadata_path("spatie", "laravel-permission")
assert sub_path == temp_dir / "packages" / "spatie" / "laravel-permission" / ".metadata" / "cache.json"
def test_is_cache_valid_no_metadata(self, fetcher):
"""Test cache validation when no metadata exists."""
assert not fetcher.is_cache_valid("livewire")
def test_is_cache_valid_expired(self, fetcher, temp_dir):
"""Test cache validation with expired cache."""
import time
import os
# Create expired metadata
metadata_path = fetcher.get_cache_metadata_path("livewire")
metadata_path.parent.mkdir(parents=True, exist_ok=True)
metadata = {
"cache_time": time.time() - 86401 # 1 second past expiration
}
with open(metadata_path, 'w') as f:
json.dump(metadata, f)
# Set file modification time to be expired
old_time = time.time() - 86401 # 1 second past expiration
os.utime(metadata_path, (old_time, old_time))
assert not fetcher.is_cache_valid("livewire")
def test_is_cache_valid_fresh(self, fetcher, temp_dir):
"""Test cache validation with fresh cache."""
import time
# Create fresh metadata
metadata_path = fetcher.get_cache_metadata_path("livewire")
metadata_path.parent.mkdir(parents=True, exist_ok=True)
metadata = {
"cache_time": time.time() - 3600 # 1 hour ago
}
with open(metadata_path, 'w') as f:
json.dump(metadata, f)
assert fetcher.is_cache_valid("livewire")
def test_fetch_package_docs_unknown_package(self, fetcher):
"""Test fetching docs for unknown package."""
assert not fetcher.fetch_package_docs("unknown-package")
def test_fetch_package_docs_with_cache(self, fetcher, temp_dir):
"""Test fetching docs when cache is valid."""
# Create valid cache
import time
metadata_path = fetcher.get_cache_metadata_path("livewire")
metadata_path.parent.mkdir(parents=True, exist_ok=True)
metadata = {
"cache_time": time.time() - 3600 # 1 hour ago
}
with open(metadata_path, 'w') as f:
json.dump(metadata, f)
# Should return True without fetching
assert fetcher.fetch_package_docs("livewire")
@patch('urllib.request.urlopen')
def test_fetch_and_process_content(self, mock_urlopen, fetcher):
"""Test content fetching and processing."""
# Mock response
mock_response = MagicMock()
mock_response.read.return_value = b'''
<html>
<body>
<nav>Navigation content</nav>
<main>
<h1>Test Documentation</h1>
<p>This is test content.</p>
<pre><code class="language-php">
echo "Hello World";
</code></pre>
</main>
<footer>Footer content</footer>
</body>
</html>
'''
mock_response.__enter__.return_value = mock_response
mock_urlopen.return_value = mock_response
content = fetcher._fetch_and_process_content("https://example.com", "test", "section")
assert content is not None
assert "# Test - Section" in content
assert "Source: https://example.com" in content
assert "Test Documentation" in content
assert "This is test content." in content
assert "echo \"Hello World\";" in content
assert "Navigation content" not in content # nav removed
assert "Footer content" not in content # footer removed
def test_clean_markdown_content(self, fetcher):
"""Test markdown content cleaning."""
dirty_content = """Too many blank lines
```
Code block
```
Trailing spaces
More content"""
clean = fetcher._clean_markdown_content(dirty_content)
assert "\n\n\n" not in clean # No triple newlines
assert "Trailing spaces\nMore content" in clean # Trailing whitespace removed
assert "```\nCode block" in clean # Code block cleaned
def test_spatie_package_structure(self, fetcher):
"""Test Spatie package structure."""
spatie_config = fetcher.community_packages["spatie"]
assert spatie_config["name"] == "Spatie Packages"
assert spatie_config["type"] == DocumentationSourceType.COMMUNITY_PACKAGE
assert "packages" in spatie_config
# Check sub-packages
packages = spatie_config["packages"]
assert "laravel-permission" in packages
assert "laravel-medialibrary" in packages
assert "laravel-backup" in packages
# Check a sub-package structure
permission = packages["laravel-permission"]
assert permission["name"] == "Laravel Permission"
assert "docs_url" in permission
assert "sections" in permission
assert "introduction" in permission["sections"]
def test_fetch_all_packages(self, fetcher):
"""Test fetching all packages with mocked responses."""
with patch.object(fetcher, 'fetch_package_docs') as mock_fetch:
mock_fetch.return_value = True
results = fetcher.fetch_all_packages()
assert len(results) == 6
assert all(results.values())
assert mock_fetch.call_count == 6
def test_process_jsx_to_markdown(self, fetcher):
"""Test JSX content extraction to markdown."""
jsx_content = '''
import { H1, P, Code, Strong } from '@/Components'
export const meta = {
title: 'How it works',
}
export default function() {
return (
<>
<H1>How it works</H1>
<P>
With Inertia you build applications just like you've always done with your
server-side web framework of choice. You use your framework's existing
functionality for routing, controllers, middleware, authentication,
authorization, data fetching, and more.
</P>
<P>
However, Inertia replaces your application's view layer. Instead of using
server-side rendering via PHP or Ruby templates, the views returned by your
application are JavaScript page components.
</P>
<P>
At its core, Inertia is essentially a client-side routing library. It allows
you to make page visits without forcing a full page reload. This is done using
the <Code>Link</Code> component.
</P>
<P>
<Strong>The end result is a silky smooth single-page experience.</Strong>
</P>
</>
)
}
'''
result = fetcher._process_jsx_to_markdown(jsx_content, "how-it-works")
assert result is not None
assert "# How it works" in result
assert "With Inertia you build applications" in result
assert "However, Inertia replaces" in result
assert "At its core, Inertia is essentially" in result
assert "`Link`" in result # Code component
assert "**The end result is a silky smooth single-page experience.**" in result # Strong component
# Should not contain JSX syntax
assert "import {" not in result
assert "export default" not in result
assert "return (" not in result
def test_clean_jsx_text(self, fetcher):
"""Test JSX text cleaning functionality."""
# Test basic text cleaning
jsx_text = "This is {some.variable} text with <span>HTML</span> tags"
result = fetcher._clean_jsx_text(jsx_text)
assert result == "This is text with HTML tags"
# Test HTML entity decoding
jsx_text = "Text with & entities <and> more"
result = fetcher._clean_jsx_text(jsx_text)
assert result == "Text with & entities <and> more"
# Test whitespace cleanup
jsx_text = " Multiple spaces and \n newlines "
result = fetcher._clean_jsx_text(jsx_text)
assert result == "Multiple spaces and newlines"
@patch('urllib.request.urlopen')
def test_fetch_inertia_docs_github(self, mock_urlopen, fetcher):
"""Test fetching Inertia docs from GitHub repository."""
# Mock GitHub response with JSX content
mock_response = MagicMock()
mock_response.read.return_value = b'''
import { H1, P } from '@/Components'
export default function() {
return (
<>
<H1>Test Page</H1>
<P>This is test content from GitHub.</P>
</>
)
}
'''
mock_response.__enter__.return_value = mock_response
mock_urlopen.return_value = mock_response
# Mock the configuration for Inertia
config = {
"repo": "inertiajs/inertiajs.com",
"branch": "master",
"docs_path": "resources/js/Pages",
"name": "Inertia.js",
"sections": ["test-page"]
}
result = fetcher._fetch_inertia_docs(config)
assert result is True
# Check that file was created
file_path = fetcher.get_package_cache_path("inertia") / "test-page.md"
assert file_path.exists()
# Check file content
content = file_path.read_text()
assert "# Inertia - Test Page" in content
assert "Source: https://inertiajs.com/test-page" in content
assert "# Test Page" in content
assert "This is test content from GitHub." in content
def test_inertia_configuration_structure(self, fetcher):
"""Test Inertia configuration structure."""
inertia_config = fetcher.community_packages["inertia"]
assert inertia_config["name"] == "Inertia.js"
assert inertia_config["type"] == DocumentationSourceType.GITHUB_REPO
assert inertia_config["repo"] == "inertiajs/inertiajs.com"
assert inertia_config["branch"] == "master"
assert inertia_config["docs_path"] == "resources/js/Pages"
assert "sections" in inertia_config
# Check that problematic sections are removed
sections = inertia_config["sections"]
assert "installation" not in sections # Should be removed (404)
assert "server-side-setup" in sections # Should exist
assert "client-side-setup" in sections # Should exist
def test_livewire_configuration_fixed(self, fetcher):
"""Test that Livewire configuration has been fixed to remove 404 sections."""
livewire_config = fetcher.community_packages["livewire"]
sections = livewire_config["sections"]
# Check that problematic sections are removed
assert "file-uploads" not in sections # Should be "uploads"
assert "file-downloads" not in sections # Should be "downloads"
assert "alpine-js" not in sections # Should be "alpine"
assert "flash-messages" not in sections # Doesn't exist
assert "traits" not in sections # Doesn't exist
assert "javascript-hooks" not in sections # Consolidated into "javascript"
assert "defer" not in sections # Consolidated into "javascript"
assert "entangle" not in sections # Consolidated into "javascript"
assert "reactive" not in sections # Consolidated into "javascript"
assert "spa-mode" not in sections # Covered by "navigate"
# Check that correct sections exist
assert "uploads" in sections
assert "downloads" in sections
assert "alpine" in sections
assert "javascript" in sections
assert "navigate" in sections
def test_simplified_update_behavior(self, fetcher):
"""Test that the simplified --update parameter updates everything."""
# The --update parameter should update all packages
available_packages = fetcher.list_available_packages()
assert len(available_packages) == 6 # All packages should be updated
assert "spatie" in available_packages
assert "livewire" in available_packages
assert "inertia" in available_packages
assert "filament" in available_packages
assert "debugbar" in available_packages
assert "ide-helper" in available_packages