Skip to main content
Glama

OpenFGA MCP

DocumentationToolsTest.php20 kB
<?php declare(strict_types=1); use OpenFGA\ClientInterface; use OpenFGA\MCP\Tools\DocumentationTools; describe('DocumentationTools', function (): void { beforeEach(function (): void { $this->mockClient = Mockery::mock(ClientInterface::class); $this->tools = new DocumentationTools($this->mockClient); }); afterEach(function (): void { Mockery::close(); }); describe('searchDocumentation', function (): void { it('returns error for empty query', function (): void { $result = $this->tools->searchDocumentation(''); expect($result)->toContain('❌ Search query cannot be empty'); }); it('returns error for invalid limit', function (): void { $result = $this->tools->searchDocumentation('test', null, 'content', 0); expect($result)->toContain('❌ Limit must be between 1 and 50'); $result = $this->tools->searchDocumentation('test', null, 'content', 51); expect($result)->toContain('❌ Limit must be between 1 and 50'); }); it('returns error for negative offset', function (): void { $result = $this->tools->searchDocumentation('test', null, 'content', 10, -1); expect($result)->toContain('❌ Offset cannot be negative'); }); it('returns error for invalid search type', function (): void { $result = $this->tools->searchDocumentation('test', null, 'invalid_type'); expect($result)->toContain('❌ Invalid search_type'); expect($result)->toContain('content, class, method, section'); }); it('returns error for invalid SDK', function (): void { $result = $this->tools->searchDocumentation('test', 'invalid_sdk'); expect($result)->toContain('❌ Invalid SDK'); expect($result)->toContain('php, go, python, java, dotnet, js, laravel'); }); it('performs basic content search', function (): void { $result = $this->tools->searchDocumentation('openfga'); expect($result)->toBeString(); expect($result)->toContain('## Documentation Search Results'); expect($result)->toContain('**Query:** `openfga`'); }); it('performs search with SDK filter', function (): void { $result = $this->tools->searchDocumentation('check', 'php'); expect($result)->toBeString(); expect($result)->toContain('## Documentation Search Results'); expect($result)->toContain('**SDK Filter:** php'); }); it('performs class search', function (): void { $result = $this->tools->searchDocumentation('Client', null, 'class'); expect($result)->toBeString(); expect($result)->toContain('**Search Type:** class'); }); it('performs method search', function (): void { $result = $this->tools->searchDocumentation('check', null, 'method'); expect($result)->toBeString(); expect($result)->toContain('**Search Type:** method'); }); it('performs section search', function (): void { $result = $this->tools->searchDocumentation('Authentication', null, 'section'); expect($result)->toBeString(); expect($result)->toContain('**Search Type:** section'); }); it('handles pagination properly', function (): void { $result = $this->tools->searchDocumentation('test', null, 'content', 5, 10); expect($result)->toBeString(); expect($result)->toContain('## Documentation Search Results'); // Should show results starting from offset 10 if (str_contains($result, 'Results:')) { expect($result)->toMatch('/\*\*Results:\*\* Showing \d+/'); } }); it('formats no results message properly', function (): void { $result = $this->tools->searchDocumentation('xyznonexistentquery123'); expect($result)->toBeString(); if (str_contains($result, 'No results found')) { expect($result)->toContain('No results found for query'); expect($result)->toContain('Try:'); expect($result)->toContain('Using different keywords'); } }); }); describe('searchCodeExamples', function (): void { it('returns error for empty query', function (): void { $result = $this->tools->searchCodeExamples(''); expect($result)->toContain('❌ Search query cannot be empty'); }); it('returns error for invalid limit', function (): void { $result = $this->tools->searchCodeExamples('test', null, true, 0); expect($result)->toContain('❌ Limit must be between 1 and 20'); $result = $this->tools->searchCodeExamples('test', null, true, 21); expect($result)->toContain('❌ Limit must be between 1 and 20'); }); it('returns error for negative offset', function (): void { $result = $this->tools->searchCodeExamples('test', null, true, 5, -1); expect($result)->toContain('❌ Offset cannot be negative'); }); it('returns error for invalid language', function (): void { $result = $this->tools->searchCodeExamples('test', 'invalid_lang'); expect($result)->toContain('❌ Invalid language'); expect($result)->toContain('php, go, python, java, csharp, javascript, typescript'); }); it('performs basic code search', function (): void { $result = $this->tools->searchCodeExamples('check'); expect($result)->toBeString(); expect($result)->toContain('## Code Examples'); expect($result)->toContain('**Search:** `check`'); }); it('performs search with language filter', function (): void { $result = $this->tools->searchCodeExamples('client', 'php'); expect($result)->toBeString(); expect($result)->toContain('## Code Examples'); expect($result)->toContain('**Language:** php'); }); it('includes context when requested', function (): void { $result = $this->tools->searchCodeExamples('new Client', null, true); expect($result)->toBeString(); // If examples are found, they might include context if (! str_contains($result, 'No code examples found')) { // Context section would be included if examples exist expect($result)->toContain('## Code Examples'); } }); it('excludes context when not requested', function (): void { $result = $this->tools->searchCodeExamples('new Client', null, false); expect($result)->toBeString(); expect($result)->toContain('## Code Examples'); }); it('handles pagination properly', function (): void { $result = $this->tools->searchCodeExamples('function', null, true, 3, 5); expect($result)->toBeString(); expect($result)->toContain('## Code Examples'); // Should show pagination info if there are results if (! str_contains($result, 'No code examples found')) { expect($result)->toMatch('/\*\*Results:\*\* Showing \d+/'); } }); it('formats no results message properly', function (): void { $result = $this->tools->searchCodeExamples('xyznonexistentcode123'); expect($result)->toBeString(); if (str_contains($result, 'No code examples found')) { expect($result)->toContain('No code examples found for'); expect($result)->toContain('Try:'); expect($result)->toContain('Searching for specific method or class names'); } }); it('maps language to SDK correctly', function (): void { // Test PHP mapping $result = $this->tools->searchCodeExamples('client', 'php'); expect($result)->toBeString(); expect($result)->toContain('**Language:** php'); // Test JavaScript mapping $result = $this->tools->searchCodeExamples('client', 'javascript'); expect($result)->toBeString(); expect($result)->toContain('**Language:** javascript'); // Test TypeScript mapping $result = $this->tools->searchCodeExamples('client', 'typescript'); expect($result)->toBeString(); expect($result)->toContain('**Language:** typescript'); }); }); describe('findSimilarDocumentation', function (): void { it('returns error for empty content', function (): void { $result = $this->tools->findSimilarDocumentation(''); expect($result)->toContain('❌ Content cannot be empty'); }); it('returns error for invalid similarity threshold', function (): void { $result = $this->tools->findSimilarDocumentation('test content', null, -0.1); expect($result)->toContain('❌ Similarity threshold must be between 0.0 and 1.0'); $result = $this->tools->findSimilarDocumentation('test content', null, 1.1); expect($result)->toContain('❌ Similarity threshold must be between 0.0 and 1.0'); }); it('returns error for invalid limit', function (): void { $result = $this->tools->findSimilarDocumentation('test content', null, 0.5, 0); expect($result)->toContain('❌ Limit must be between 1 and 20'); $result = $this->tools->findSimilarDocumentation('test content', null, 0.5, 21); expect($result)->toContain('❌ Limit must be between 1 and 20'); }); it('returns error for invalid SDK', function (): void { $result = $this->tools->findSimilarDocumentation('test content', 'invalid_sdk'); expect($result)->toContain('❌ Invalid SDK'); expect($result)->toContain('php, go, python, java, dotnet, js, laravel'); }); it('finds similar documentation', function (): void { $content = 'I need to understand how to check permissions in OpenFGA using the check method'; $result = $this->tools->findSimilarDocumentation($content); expect($result)->toBeString(); expect($result)->toContain('## Similar Documentation'); // The test should handle both cases - finding results or not finding results if (str_contains($result, 'No similar documentation found')) { expect($result)->toContain('Try:'); expect($result)->toContain('Lowering the similarity threshold'); } else { expect($result)->toContain('**Similarity Threshold:** 0.5'); } }); it('finds similar documentation with SDK filter', function (): void { $content = 'How do I create a new client instance and connect to OpenFGA?'; $result = $this->tools->findSimilarDocumentation($content, 'php'); expect($result)->toBeString(); expect($result)->toContain('## Similar Documentation'); // The test should handle both cases - finding results or not finding results if (str_contains($result, 'No similar documentation found')) { expect($result)->toContain('in SDK: php'); } else { expect($result)->toContain('**SDK Filter:** php'); } }); it('respects similarity threshold', function (): void { $content = 'OpenFGA authorization model with user groups'; // Lower threshold should find more results $result1 = $this->tools->findSimilarDocumentation($content, null, 0.3); expect($result1)->toBeString(); if (str_contains($result1, 'No similar documentation found')) { expect($result1)->toContain('(threshold: 0.3)'); } else { expect($result1)->toContain('**Similarity Threshold:** 0.3'); } // Higher threshold should be more restrictive $result2 = $this->tools->findSimilarDocumentation($content, null, 0.9); expect($result2)->toBeString(); if (str_contains($result2, 'No similar documentation found')) { expect($result2)->toContain('(threshold: 0.9)'); } else { expect($result2)->toContain('**Similarity Threshold:** 0.9'); } }); it('limits results properly', function (): void { $content = 'OpenFGA client methods and API calls'; $result = $this->tools->findSimilarDocumentation($content, null, 0.3, 3); expect($result)->toBeString(); // If similar docs are found, should respect limit if (! str_contains($result, 'No similar documentation found')) { // Count the number of result headers (### 1., ### 2., etc.) preg_match_all('/### \d+\./', $result, $matches); expect(count($matches[0]))->toBeLessThanOrEqual(3); } }); it('formats no results message properly', function (): void { $content = 'xyzabc123 nonexistent random gibberish content that will not match anything'; $result = $this->tools->findSimilarDocumentation($content, null, 0.95); expect($result)->toBeString(); if (str_contains($result, 'No similar documentation found')) { expect($result)->toContain('No similar documentation found'); expect($result)->toContain('Try:'); expect($result)->toContain('Lowering the similarity threshold'); } }); it('handles content with code blocks', function (): void { $content = <<<'CONTENT' I'm trying to use the check method like this: ```php $client->check($request); ``` But I'm getting an error. How should I properly call the check method? CONTENT; $result = $this->tools->findSimilarDocumentation($content); expect($result)->toBeString(); expect($result)->toContain('## Similar Documentation'); }); it('extracts key terms properly', function (): void { $content = 'OpenFGA authorization tuples and relationships with user groups and permissions'; $result = $this->tools->findSimilarDocumentation($content); expect($result)->toBeString(); // Should find documentation related to these OpenFGA concepts expect($result)->toContain('## Similar Documentation'); }); }); describe('edge cases', function (): void { it('handles whitespace-only query gracefully', function (): void { $result = $this->tools->searchDocumentation(' '); expect($result)->toContain('❌ Search query cannot be empty'); $result = $this->tools->searchCodeExamples("\t\n"); expect($result)->toContain('❌ Search query cannot be empty'); $result = $this->tools->findSimilarDocumentation(' '); expect($result)->toContain('❌ Content cannot be empty'); }); it('handles very long queries gracefully', function (): void { $longQuery = str_repeat('test ', 100); $result = $this->tools->searchDocumentation($longQuery); expect($result)->toBeString(); expect($result)->toContain('## Documentation Search Results'); $result = $this->tools->searchCodeExamples($longQuery); expect($result)->toBeString(); expect($result)->toContain('## Code Examples'); }); it('handles special characters in queries', function (): void { $specialQuery = 'check() && expand()'; $result = $this->tools->searchDocumentation($specialQuery); expect($result)->toBeString(); expect($result)->not->toContain('❌'); $result = $this->tools->searchCodeExamples($specialQuery); expect($result)->toBeString(); expect($result)->not->toContain('❌'); }); it('handles Unicode characters in content', function (): void { $unicodeContent = 'OpenFGA 授权 权限 检查 用户组 🔐'; $result = $this->tools->findSimilarDocumentation($unicodeContent); expect($result)->toBeString(); expect($result)->toContain('## Similar Documentation'); }); it('handles null SDK parameter correctly', function (): void { $result = $this->tools->searchDocumentation('test', null); expect($result)->toBeString(); expect($result)->not->toContain('**SDK Filter:**'); $result = $this->tools->searchCodeExamples('test', null); expect($result)->toBeString(); // When no language filter is provided, the filter should not be shown in the header // But individual code examples can still show their language $lines = explode(' ', $result); $headerSection = implode(' ', array_slice($lines, 0, 10)); // Check only the header section expect($headerSection)->not->toContain('**Language:**'); $result = $this->tools->findSimilarDocumentation('test content', null); expect($result)->toBeString(); expect($result)->not->toContain('**SDK Filter:**'); }); }); describe('markdown formatting', function (): void { it('generates valid markdown headers', function (): void { $result = $this->tools->searchDocumentation('test'); // Check for proper markdown headers expect($result)->toMatch('/^## /m'); expect($result)->toContain('**Query:**'); }); it('formats code blocks properly', function (): void { $result = $this->tools->searchCodeExamples('client'); expect($result)->toBeString(); // If code examples are found, they should be in code blocks if (! str_contains($result, 'No code examples found')) { // Code blocks should be properly formatted expect($result)->toContain('## Code Examples'); } }); it('includes pagination navigation when needed', function (): void { $result = $this->tools->searchDocumentation('test', null, 'content', 5, 0); expect($result)->toBeString(); // If there are more results than the limit, pagination should be shown if (str_contains($result, 'Page:') && str_contains($result, ' of ')) { if (preg_match('/Page: \d+ of (\d+)/', $result, $matches)) { $totalPages = (int) $matches[1]; if (1 < $totalPages) { expect($result)->toContain('### Pagination'); } } } }); it('formats similarity scores as percentages', function (): void { $result = $this->tools->findSimilarDocumentation('OpenFGA check method'); expect($result)->toBeString(); // If similar docs are found, similarity should be shown as percentage if (! str_contains($result, 'No similar documentation found')) { // Should contain percentage formatting if results exist if (str_contains($result, '**Similarity Score:**')) { expect($result)->toMatch('/\*\*Similarity Score:\*\* \d+%/'); } } }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evansims/openfga-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server