ConfigurationParserTest.php•9.37 kB
<?php
declare(strict_types=1);
use OpenFGA\MCP\ConfigurationParser;
use Psr\Log\NullLogger;
beforeEach(function (): void {
    // Clear $_ENV before each test
    foreach (array_keys($_ENV) as $key) {
        if (str_starts_with($key, 'OPENFGA_MCP_')) {
            unset($_ENV[$key]);
        }
    }
});
describe('ConfigurationParser', function (): void {
    describe('parseAndApply', function (): void {
        it('parses valid JSON configuration', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_URL' => 'https://api.example.com',
                'OPENFGA_MCP_API_TOKEN' => 'test-token',
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($result->getErrors())->toBeEmpty();
            expect($result->getAppliedKeys())->toContain('OPENFGA_MCP_API_URL');
            expect($result->getAppliedKeys())->toContain('OPENFGA_MCP_API_TOKEN');
            expect($_ENV['OPENFGA_MCP_API_URL'])->toBe('https://api.example.com');
            expect($_ENV['OPENFGA_MCP_API_TOKEN'])->toBe('test-token');
        });
        it('handles invalid JSON gracefully', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $result = $parser->parseAndApply('invalid json {');
            expect($result->isSuccessful())->toBeFalse();
            expect($result->hasErrors())->toBeTrue();
            expect($result->getErrors())->toContain('Invalid JSON: Syntax error');
            expect($result->getAppliedKeys())->toBeEmpty();
        });
        it('converts boolean values correctly', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_WRITEABLE' => true,
                'OPENFGA_MCP_DEBUG' => false,
                'OPENFGA_MCP_TRANSPORT_SSE' => 1,
                'OPENFGA_MCP_TRANSPORT_STATELESS' => '0',
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($_ENV['OPENFGA_MCP_API_WRITEABLE'])->toBe('true');
            expect($_ENV['OPENFGA_MCP_DEBUG'])->toBe('false');
            expect($_ENV['OPENFGA_MCP_TRANSPORT_SSE'])->toBe('true');
            expect($_ENV['OPENFGA_MCP_TRANSPORT_STATELESS'])->toBe('false');
        });
        it('converts integer values correctly', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_TRANSPORT_PORT' => 8080,
                'OPENFGA_MCP_TRANSPORT_HOST' => '127.0.0.1', // string should stay string
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($_ENV['OPENFGA_MCP_TRANSPORT_PORT'])->toBe('8080');
            expect($_ENV['OPENFGA_MCP_TRANSPORT_HOST'])->toBe('127.0.0.1');
        });
        it('ignores unsupported configuration keys silently', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_URL' => 'https://api.example.com',
                'UNSUPPORTED_KEY' => 'value',
                'ANOTHER_INVALID' => 123,
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($result->getErrors())->toBeEmpty();
            expect($result->getAppliedKeys())->toContain('OPENFGA_MCP_API_URL');
            expect($result->getAppliedKeys())->not->toContain('UNSUPPORTED_KEY');
            expect($result->getAppliedKeys())->not->toContain('ANOTHER_INVALID');
            expect($_ENV)->toHaveKey('OPENFGA_MCP_API_URL');
            expect($_ENV)->not->toHaveKey('UNSUPPORTED_KEY');
        });
        it('validates OAuth2 configuration combinations', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_CLIENT_ID' => 'client-id',
                'OPENFGA_MCP_API_CLIENT_SECRET' => 'client-secret',
                // Missing ISSUER and AUDIENCE
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeFalse();
            expect($result->hasErrors())->toBeTrue();
            expect($result->getErrors())->toContain('OAuth2 client credentials require all of: OPENFGA_MCP_API_CLIENT_ID, OPENFGA_MCP_API_CLIENT_SECRET, OPENFGA_MCP_API_ISSUER, OPENFGA_MCP_API_AUDIENCE');
        });
        it('validates restricted mode configuration', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_RESTRICT' => true,
                'OPENFGA_MCP_API_STORE' => 'store-id',
                // Missing MODEL
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeFalse();
            expect($result->hasErrors())->toBeTrue();
            expect($result->getErrors())->toContain('Restricted mode requires both OPENFGA_MCP_API_STORE and OPENFGA_MCP_API_MODEL to be set');
        });
        it('accepts valid OAuth2 configuration', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_CLIENT_ID' => 'client-id',
                'OPENFGA_MCP_API_CLIENT_SECRET' => 'client-secret',
                'OPENFGA_MCP_API_ISSUER' => 'https://issuer.example.com',
                'OPENFGA_MCP_API_AUDIENCE' => 'https://api.example.com',
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($result->getErrors())->toBeEmpty();
            expect($_ENV['OPENFGA_MCP_API_CLIENT_ID'])->toBe('client-id');
            expect($_ENV['OPENFGA_MCP_API_CLIENT_SECRET'])->toBe('client-secret');
            expect($_ENV['OPENFGA_MCP_API_ISSUER'])->toBe('https://issuer.example.com');
            expect($_ENV['OPENFGA_MCP_API_AUDIENCE'])->toBe('https://api.example.com');
        });
        it('accepts valid restricted mode configuration', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_RESTRICT' => true,
                'OPENFGA_MCP_API_STORE' => 'store-id',
                'OPENFGA_MCP_API_MODEL' => 'model-id',
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($result->getErrors())->toBeEmpty();
            expect($_ENV['OPENFGA_MCP_API_RESTRICT'])->toBe('true');
            expect($_ENV['OPENFGA_MCP_API_STORE'])->toBe('store-id');
            expect($_ENV['OPENFGA_MCP_API_MODEL'])->toBe('model-id');
        });
        it('reports type validation errors', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_WRITEABLE' => ['not', 'a', 'boolean'],
                'OPENFGA_MCP_TRANSPORT_PORT' => 'not-a-number',
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeFalse();
            expect($result->hasErrors())->toBeTrue();
            expect($result->getErrors())->toContain('OPENFGA_MCP_API_WRITEABLE must be a boolean, array given');
            expect($result->getErrors())->toContain('OPENFGA_MCP_TRANSPORT_PORT must be numeric, string given');
        });
        it('handles empty configuration object', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            expect($result->getErrors())->toBeEmpty();
            expect($result->getAppliedKeys())->toBeEmpty();
        });
        it('rejects non-object JSON', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode('string value');
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeFalse();
            expect($result->hasErrors())->toBeTrue();
            expect($result->getErrors())->toContain('Configuration must be a JSON object');
        });
        it('masks sensitive values in applied values', function (): void {
            $parser = new ConfigurationParser(new NullLogger);
            $json = json_encode([
                'OPENFGA_MCP_API_TOKEN' => 'very-secret-token-12345',
                'OPENFGA_MCP_API_URL' => 'https://api.example.com', // not sensitive
            ]);
            $result = $parser->parseAndApply($json);
            expect($result->isSuccessful())->toBeTrue();
            // The actual values in $_ENV should be unmasked
            expect($_ENV['OPENFGA_MCP_API_TOKEN'])->toBe('very-secret-token-12345');
            expect($_ENV['OPENFGA_MCP_API_URL'])->toBe('https://api.example.com');
        });
    });
});