OfflineClientTest.php•8.49 kB
<?php
declare(strict_types=1);
namespace Tests\Unit;
use OpenFGA\ClientInterface;
use OpenFGA\MCP\OfflineClient;
use OpenFGA\Models\Collections\{Assertions, BatchCheckItems, TypeDefinitions, UserTypeFilters};
use OpenFGA\Models\TupleKey;
use OpenFGA\Results\{FailureInterface, SuccessInterface};
use ReflectionClass;
use RuntimeException;
beforeEach(function (): void {
$this->client = new OfflineClient;
});
describe('OfflineClient', function (): void {
describe('read operations', function (): void {
it('returns success for batchCheck', function (): void {
// Create an empty BatchCheckItems instance
$checks = new BatchCheckItems;
$result = $this->client->batchCheck('store-id', 'model-id', $checks);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for check', function (): void {
// Create a real TupleKey instance
$tuple = new TupleKey(
user: 'user:1',
relation: 'viewer',
object: 'document:1',
);
$result = $this->client->check(
store: 'store-id',
model: 'model-id',
tuple: $tuple,
);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for expand', function (): void {
// Create a real TupleKey instance
$tuple = new TupleKey(
user: 'user:1',
relation: 'viewer',
object: 'document:1',
);
$result = $this->client->expand(
store: 'store-id',
tuple: $tuple,
model: 'model-id',
);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for getAuthorizationModel', function (): void {
$result = $this->client->getAuthorizationModel('store-id', 'model-id');
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for getStore', function (): void {
$result = $this->client->getStore('store-id');
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for listAuthorizationModels', function (): void {
$result = $this->client->listAuthorizationModels('store-id');
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for listObjects', function (): void {
$result = $this->client->listObjects(
store: 'store-id',
model: 'model-id',
type: 'document',
relation: 'viewer',
user: 'user:1',
);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for listStores', function (): void {
$result = $this->client->listStores();
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for listTupleChanges', function (): void {
$result = $this->client->listTupleChanges('store-id', 'document');
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for listUsers', function (): void {
// Create an empty UserTypeFilters instance
$userFilters = new UserTypeFilters;
$result = $this->client->listUsers(
store: 'store-id',
model: 'model-id',
object: 'document:1',
relation: 'viewer',
userFilters: $userFilters,
);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for readAssertions', function (): void {
$result = $this->client->readAssertions('store-id', 'model-id');
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for readTuples', function (): void {
$result = $this->client->readTuples('store-id');
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for streamedListObjects', function (): void {
$result = $this->client->streamedListObjects(
store: 'store-id',
model: 'model-id',
type: 'document',
relation: 'viewer',
user: 'user:1',
);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
it('returns success for dsl parsing', function (): void {
$dsl = 'model
schema 1.1
type user
type document
relations
define viewer: [user]';
$result = $this->client->dsl($dsl);
// DSL parsing returns success in offline mode
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
});
describe('write operations', function (): void {
it('returns failure for createAuthorizationModel', function (): void {
// Create an empty TypeDefinitions instance
$typeDefinitions = new TypeDefinitions;
$result = $this->client->createAuthorizationModel('store-id', $typeDefinitions);
expect($result)->toBeInstanceOf(FailureInterface::class);
});
it('returns failure for createStore', function (): void {
$result = $this->client->createStore('Test Store');
expect($result)->toBeInstanceOf(FailureInterface::class);
});
it('returns failure for deleteStore', function (): void {
$result = $this->client->deleteStore('store-id');
expect($result)->toBeInstanceOf(FailureInterface::class);
});
it('returns failure for writeAssertions', function (): void {
// Create an empty Assertions instance
$assertions = new Assertions;
$result = $this->client->writeAssertions('store-id', 'model-id', $assertions);
expect($result)->toBeInstanceOf(FailureInterface::class);
});
it('returns failure for writeTuples', function (): void {
$result = $this->client->writeTuples('store-id', 'model-id');
expect($result)->toBeInstanceOf(FailureInterface::class);
});
});
describe('HTTP request/response methods', function (): void {
it('returns null for getLastRequest', function (): void {
$result = $this->client->getLastRequest();
expect($result)->toBeNull();
});
it('returns null for getLastResponse', function (): void {
$result = $this->client->getLastResponse();
expect($result)->toBeNull();
});
});
describe('interface implementation', function (): void {
it('implements ClientInterface', function (): void {
expect($this->client)->toBeInstanceOf(ClientInterface::class);
});
it('is a final class', function (): void {
$reflection = new ReflectionClass(OfflineClient::class);
expect($reflection->isFinal())->toBeTrue();
});
});
describe('error messages', function (): void {
it('provides helpful error message for write operations', function (): void {
$result = $this->client->createStore('Test Store');
expect($result)->toBeInstanceOf(FailureInterface::class);
$error = null;
$result->failure(function ($e) use (&$error): void {
$error = $e;
});
expect($error)->toBeInstanceOf(RuntimeException::class);
expect($error->getMessage())->toContain('OpenFGA instance');
expect($error->getMessage())->toContain('OPENFGA_MCP_API_URL');
});
});
describe('parameter handling', function (): void {
it('handles various parameter types correctly', function (): void {
// Test with string parameters
$result = $this->client->getStore('store-123');
expect($result)->toBeInstanceOf(SuccessInterface::class);
// Test with null parameters
$result = $this->client->listStores(null, null);
expect($result)->toBeInstanceOf(SuccessInterface::class);
});
});
});