Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions src/Contracts/PaginatedResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Domain\Criteria\Contracts;

/**
* Interface PaginatedResult
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface PaginatedResult
{
/**
* Get the items for the current page
*
* @return array<int, mixed>
*/
public function items(): array;

/**
* Get total number of items across all pages
*
* @return int
*/
public function total(): int;

/**
* Get items per page
*
* @return int
*/
public function perPage(): int;

/**
* Get current page number (1-indexed)
*
* @return int
*/
public function currentPage(): int;

/**
* Get last page number
*
* @return int
*/
public function lastPage(): int;

/**
* Check if there are more pages after the current one
*
* @return bool
*/
public function hasMorePages(): bool;

/**
* Get the count of items on current page
*
* @return int
*/
public function count(): int;

/**
* Check if the current page is empty
*
* @return bool
*/
public function isEmpty(): bool;

/**
* Check if the current page is not empty
*
* @return bool
*/
public function isNotEmpty(): bool;
}
96 changes: 96 additions & 0 deletions tests/CriteriaSourceContractTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

declare(strict_types=1);

use ComplexHeart\Domain\Criteria\Contracts\CriteriaSource;

test('CriteriaSource interface exists and is accessible', function () {
expect(interface_exists(CriteriaSource::class))->toBeTrue();
});

test('CriteriaSource interface has all required methods', function () {
$reflection = new ReflectionClass(CriteriaSource::class);

$expectedMethods = [
'filterGroups',
'orderType',
'orderBy',
'pageLimit',
'pageOffset',
'pageNumber',
];

foreach ($expectedMethods as $method) {
expect($reflection->hasMethod($method))
->toBeTrue("Method '{$method}' should exist in CriteriaSource interface");
}
});

test('CriteriaSource methods have correct return types', function () {
$reflection = new ReflectionClass(CriteriaSource::class);

$methodReturnTypes = [
'filterGroups' => 'array',
'orderType' => 'string',
'orderBy' => 'string',
'pageLimit' => 'int',
'pageOffset' => 'int',
'pageNumber' => 'int',
];

foreach ($methodReturnTypes as $methodName => $expectedType) {
$method = $reflection->getMethod($methodName);
expect($method->hasReturnType())->toBeTrue("Method {$methodName} should have return type");

$returnType = $method->getReturnType();
expect($returnType)->not->toBeNull("Method {$methodName} return type should not be null");

if ($returnType instanceof ReflectionNamedType) {
expect($returnType->getName())->toBe($expectedType, "Method {$methodName} should return {$expectedType}");
}
}
});

test('CriteriaSource is an interface not a class', function () {
$reflection = new ReflectionClass(CriteriaSource::class);

expect($reflection->isInterface())->toBeTrue()
->and($reflection->isTrait())->toBeFalse();
});

test('CriteriaSource interface has correct namespace', function () {
expect(CriteriaSource::class)
->toBe('ComplexHeart\Domain\Criteria\Contracts\CriteriaSource');
});

test('CriteriaSource filterGroups method has correct docblock return type', function () {
$reflection = new ReflectionClass(CriteriaSource::class);
$method = $reflection->getMethod('filterGroups');

$docComment = $method->getDocComment();
expect($docComment)->toContain('@return array<array<array<string, mixed>>>');
});

test('CriteriaSource orderType method documents valid values', function () {
$reflection = new ReflectionClass(CriteriaSource::class);
$method = $reflection->getMethod('orderType');

$docComment = $method->getDocComment();
expect($docComment)->toContain('asc, desc, none or random');
});

test('CriteriaSource pageOffset method documents default behavior', function () {
$reflection = new ReflectionClass(CriteriaSource::class);
$method = $reflection->getMethod('pageOffset');

$docComment = $method->getDocComment();
expect($docComment)->toContain('default should be 0');
});

test('CriteriaSource pageNumber method documents offset computation', function () {
$reflection = new ReflectionClass(CriteriaSource::class);
$method = $reflection->getMethod('pageNumber');

$docComment = $method->getDocComment();
expect($docComment)->toContain('used to compute the offset');
});
70 changes: 70 additions & 0 deletions tests/PaginatedResultContractTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

use ComplexHeart\Domain\Criteria\Contracts\PaginatedResult;

test('PaginatedResult interface exists and is accessible', function () {
expect(interface_exists(PaginatedResult::class))->toBeTrue();
});

test('PaginatedResult interface has all required methods', function () {
$reflection = new ReflectionClass(PaginatedResult::class);

$expectedMethods = [
'items',
'total',
'perPage',
'currentPage',
'lastPage',
'hasMorePages',
'count',
'isEmpty',
'isNotEmpty',
];

foreach ($expectedMethods as $method) {
expect($reflection->hasMethod($method))
->toBeTrue("Method '{$method}' should exist in PaginatedResult interface");
}
});

test('PaginatedResult methods have correct return types', function () {
$reflection = new ReflectionClass(PaginatedResult::class);

$methodReturnTypes = [
'items' => 'array',
'total' => 'int',
'perPage' => 'int',
'currentPage' => 'int',
'lastPage' => 'int',
'hasMorePages' => 'bool',
'count' => 'int',
'isEmpty' => 'bool',
'isNotEmpty' => 'bool',
];

foreach ($methodReturnTypes as $methodName => $expectedType) {
$method = $reflection->getMethod($methodName);
expect($method->hasReturnType())->toBeTrue("Method {$methodName} should have return type");

$returnType = $method->getReturnType();
expect($returnType)->not->toBeNull("Method {$methodName} return type should not be null");

if ($returnType instanceof ReflectionNamedType) {
expect($returnType->getName())->toBe($expectedType, "Method {$methodName} should return {$expectedType}");
}
}
});

test('PaginatedResult is an interface not a class', function () {
$reflection = new ReflectionClass(PaginatedResult::class);

expect($reflection->isInterface())->toBeTrue()
->and($reflection->isTrait())->toBeFalse();
});

test('PaginatedResult interface has correct namespace', function () {
expect(PaginatedResult::class)
->toBe('ComplexHeart\Domain\Criteria\Contracts\PaginatedResult');
});