feat: import work hours from SaaS and exchange signed time sheet
This commit is contained in:
1 parent
7cf39850e6
commit
1a7c272414
73 files changed
+10488
-72
No files matched your search
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Saas;
|
||||
|
||||
use App\Saas\ApiClient;
|
||||
use App\Saas\ApiClientException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||
|
||||
class ApiClientTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* The whole point of the calendar-month argument: the app never has to know that the
|
||||
* API counts from zero.
|
||||
*
|
||||
* @dataProvider provideMonths
|
||||
*/
|
||||
public function testItTranslatesTheCalendarMonthIntoTheApiIndex(int $month, string $expectedPath): void
|
||||
{
|
||||
$requested = null;
|
||||
|
||||
$client = $this->createClient(function (string $method, string $url) use (&$requested) {
|
||||
$requested = $url;
|
||||
|
||||
return new MockResponse('{"ist": 0, "list": []}', ['response_headers' => ['content-type' => 'application/json']]);
|
||||
});
|
||||
|
||||
$client->getTimeAccount('[email protected]', 2026, $month);
|
||||
|
||||
$this->assertSame('https://saas.example'.$expectedPath, $requested);
|
||||
}
|
||||
|
||||
public static function provideMonths(): iterable
|
||||
{
|
||||
yield 'January is index 0' => [1, '/api/v2/time/account/secret%2Fkey/theo%40example.com/2026/0'];
|
||||
yield 'March is index 2' => [3, '/api/v2/time/account/secret%2Fkey/theo%40example.com/2026/2'];
|
||||
yield 'December is index 11' => [12, '/api/v2/time/account/secret%2Fkey/theo%40example.com/2026/11'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Both the key and the address sit in the path, so both have to survive a slash or an
|
||||
* "@" without changing the route.
|
||||
*/
|
||||
public function testItEscapesTheKeyAndTheAddress(): void
|
||||
{
|
||||
$requested = null;
|
||||
|
||||
$client = $this->createClient(function (string $method, string $url) use (&$requested) {
|
||||
$requested = $url;
|
||||
|
||||
return new MockResponse('{"ist": 0, "list": []}', ['response_headers' => ['content-type' => 'application/json']]);
|
||||
});
|
||||
|
||||
$client->getTimeAccount('a [email protected]', 2026, 3);
|
||||
|
||||
$this->assertStringContainsString('/secret%2Fkey/', $requested);
|
||||
$this->assertStringContainsString('/a%20b%2Bc%40example.com/', $requested);
|
||||
}
|
||||
|
||||
public function testItReturnsTheDecodedPayload(): void
|
||||
{
|
||||
$client = $this->createClient(fn () => new MockResponse(
|
||||
'{"ist": 480, "employee": "e-1", "list": []}',
|
||||
['response_headers' => ['content-type' => 'application/json']],
|
||||
));
|
||||
|
||||
$this->assertSame(
|
||||
['ist' => 480, 'employee' => 'e-1', 'list' => []],
|
||||
$client->getTimeAccount('[email protected]', 2026, 9),
|
||||
);
|
||||
}
|
||||
|
||||
public function testAnErrorStatusBecomesAnApiClientException(): void
|
||||
{
|
||||
$client = $this->createClient(fn () => new MockResponse('nope', ['http_code' => 500]));
|
||||
|
||||
$this->expectException(ApiClientException::class);
|
||||
$this->expectExceptionMessageMatches('/[email protected] \(2026-03\)/');
|
||||
|
||||
$client->getTimeAccount('[email protected]', 2026, 3);
|
||||
}
|
||||
|
||||
public function testUnreadableJsonBecomesAnApiClientException(): void
|
||||
{
|
||||
$client = $this->createClient(fn () => new MockResponse(
|
||||
'not json',
|
||||
['response_headers' => ['content-type' => 'application/json']],
|
||||
));
|
||||
|
||||
$this->expectException(ApiClientException::class);
|
||||
|
||||
$client->getTimeAccount('[email protected]', 2026, 3);
|
||||
}
|
||||
|
||||
public function testItRefusesAMonthThatIsNotACalendarMonth(): void
|
||||
{
|
||||
$client = $this->createClient(fn () => new MockResponse('{}'));
|
||||
|
||||
$this->expectException(ApiClientException::class);
|
||||
$this->expectExceptionMessage('0 is not a calendar month.');
|
||||
|
||||
$client->getTimeAccount('[email protected]', 2026, 0);
|
||||
}
|
||||
|
||||
public function testAnUnconfiguredBaseUrlFailsAtCallTimeNotAtBoot(): void
|
||||
{
|
||||
// The command has to stay usable - "--help" included - on a machine that has no
|
||||
// credentials, so this cannot be a container-time failure.
|
||||
$client = new ApiClient(
|
||||
new MockHttpClient(fn () => new MockResponse('{}')),
|
||||
new NullLogger(),
|
||||
['base_url' => '', 'api_key' => 'k'],
|
||||
);
|
||||
|
||||
$this->expectException(ApiClientException::class);
|
||||
$this->expectExceptionMessage('APP_SAAS_BASE_URL is not configured.');
|
||||
|
||||
$client->getTimeAccount('[email protected]', 2026, 3);
|
||||
}
|
||||
|
||||
private function createClient(callable $handler): ApiClient
|
||||
{
|
||||
return new ApiClient(
|
||||
new MockHttpClient($handler),
|
||||
new NullLogger(),
|
||||
['base_url' => 'https://saas.example', 'api_key' => 'secret/key'],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user