195 lines
7.7 KiB
PHP
195 lines
7.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Integration;
|
|
|
|
use Tests\Support\DatabaseSeeder;
|
|
use Tests\Support\QdbTestCase;
|
|
|
|
final class ProgramSessionsTest extends QdbTestCase
|
|
{
|
|
public function testAdminCrudAndMobileFetch(): void
|
|
{
|
|
$token = $this->adminToken();
|
|
$qnId = $this->fixture()->questionnaireId;
|
|
|
|
$create = $this->api()->request('POST', 'program_sessions', [
|
|
'name' => 'Baseline',
|
|
'description' => 'First visit',
|
|
'sessionNumber' => 1,
|
|
'orderIndex' => 0,
|
|
'isActive' => true,
|
|
'questionnaires' => [
|
|
['questionnaireID' => $qnId, 'orderIndex' => 0],
|
|
],
|
|
], ['Authorization' => 'Bearer ' . $token]);
|
|
|
|
$this->assertApiOk($create);
|
|
$sessionID = $create['data']['session']['sessionID'] ?? '';
|
|
$this->assertNotSame('', $sessionID);
|
|
$this->assertSame('Baseline', $create['data']['session']['name']);
|
|
$this->assertCount(1, $create['data']['session']['questionnaires']);
|
|
|
|
$list = $this->api()->request('GET', 'program_sessions', null, [
|
|
'Authorization' => 'Bearer ' . $token,
|
|
]);
|
|
$this->assertApiOk($list);
|
|
$this->assertCount(1, $list['data']['sessions']);
|
|
$this->assertFalse($list['data']['settings']['autoRepeatSessions'] ?? true);
|
|
|
|
$update = $this->api()->request('PUT', 'program_sessions', [
|
|
'sessionID' => $sessionID,
|
|
'name' => 'Baseline updated',
|
|
'sessionNumber' => 2,
|
|
'questionnaires' => [],
|
|
], ['Authorization' => 'Bearer ' . $token]);
|
|
$this->assertApiOk($update);
|
|
$this->assertSame('Baseline updated', $update['data']['session']['name']);
|
|
$this->assertSame([], $update['data']['session']['questionnaires']);
|
|
|
|
$mobile = $this->coachMobileLogin();
|
|
$appRes = $this->api()->request('GET', 'app_questionnaires', null, [
|
|
'Authorization' => 'Bearer ' . $mobile['token'],
|
|
], ['sessions' => '1']);
|
|
$this->assertApiOk($appRes);
|
|
$this->assertCount(1, $appRes['data']['sessions']);
|
|
$this->assertSame($sessionID, $appRes['data']['sessions'][0]['sessionID']);
|
|
$this->assertArrayHasKey('settings', $appRes['data']);
|
|
|
|
$delete = $this->api()->request('DELETE', 'program_sessions', [
|
|
'sessionID' => $sessionID,
|
|
], ['Authorization' => 'Bearer ' . $token]);
|
|
$this->assertApiOk($delete);
|
|
|
|
$empty = $this->api()->request('GET', 'program_sessions', null, [
|
|
'Authorization' => 'Bearer ' . $token,
|
|
]);
|
|
$this->assertApiOk($empty);
|
|
$this->assertSame([], $empty['data']['sessions']);
|
|
}
|
|
|
|
public function testAutoRepeatAdvancesCycleAfterLastSessionCompleted(): void
|
|
{
|
|
$adminToken = $this->adminToken();
|
|
$qnId = $this->fixture()->questionnaireId;
|
|
$clientCode = $this->fixture()->clientCode;
|
|
|
|
$this->assertApiOk($this->api()->request('PATCH', 'program_sessions', [
|
|
'autoRepeatSessions' => true,
|
|
], ['Authorization' => 'Bearer ' . $adminToken]));
|
|
|
|
$session1 = $this->api()->request('POST', 'program_sessions', [
|
|
'name' => 'Session 1',
|
|
'sessionNumber' => 1,
|
|
'orderIndex' => 0,
|
|
'isActive' => true,
|
|
'questionnaires' => [['questionnaireID' => $qnId, 'orderIndex' => 0]],
|
|
], ['Authorization' => 'Bearer ' . $adminToken]);
|
|
$this->assertApiOk($session1);
|
|
|
|
$session2 = $this->api()->request('POST', 'program_sessions', [
|
|
'name' => 'Session 2',
|
|
'sessionNumber' => 2,
|
|
'orderIndex' => 1,
|
|
'isActive' => true,
|
|
'questionnaires' => [['questionnaireID' => $qnId, 'orderIndex' => 0]],
|
|
], ['Authorization' => 'Bearer ' . $adminToken]);
|
|
$this->assertApiOk($session2);
|
|
|
|
$login = $this->coachMobileLogin();
|
|
$submit = $this->submitQuestionnaireAs(
|
|
$login['token'],
|
|
$qnId,
|
|
$clientCode,
|
|
[['questionID' => $this->fixture()->questionShortId, 'answerOptionKey' => 'yes']],
|
|
);
|
|
$this->assertApiOk($submit);
|
|
$this->assertTrue($submit['data']['cycleAdvanced'] ?? false);
|
|
$this->assertSame(2, $submit['data']['cycleNumber'] ?? 0);
|
|
|
|
$clientsRes = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires', null, [
|
|
'clients' => '1',
|
|
]);
|
|
$this->assertApiOk($clientsRes);
|
|
$plain = qdb_decrypt_sensitive_envelope($clientsRes['data'], $login['token']);
|
|
$payload = json_decode($plain, true, 512, JSON_THROW_ON_ERROR);
|
|
$client = null;
|
|
foreach ($payload['clients'] as $row) {
|
|
if (($row['clientCode'] ?? '') === $clientCode) {
|
|
$client = $row;
|
|
break;
|
|
}
|
|
}
|
|
$this->assertNotNull($client);
|
|
$progress = $client['programProgress'];
|
|
$this->assertSame(2, $progress['cycleNumber']);
|
|
$this->assertSame(2, $progress['totalSessions']);
|
|
$this->assertSame(0, $progress['sessionsCompletedInCycle']);
|
|
$this->assertTrue($progress['autoRepeatSessions']);
|
|
$this->assertFalse($progress['programComplete']);
|
|
}
|
|
|
|
public function testAutoRepeatDisabledKeepsProgramComplete(): void
|
|
{
|
|
$adminToken = $this->adminToken();
|
|
$qnId = $this->fixture()->questionnaireId;
|
|
$clientCode = $this->fixture()->clientCode;
|
|
|
|
$this->assertApiOk($this->api()->request('PATCH', 'program_sessions', [
|
|
'autoRepeatSessions' => false,
|
|
], ['Authorization' => 'Bearer ' . $adminToken]));
|
|
|
|
$this->assertApiOk($this->api()->request('POST', 'program_sessions', [
|
|
'name' => 'Only session',
|
|
'sessionNumber' => 1,
|
|
'orderIndex' => 0,
|
|
'isActive' => true,
|
|
'questionnaires' => [['questionnaireID' => $qnId, 'orderIndex' => 0]],
|
|
], ['Authorization' => 'Bearer ' . $adminToken]));
|
|
|
|
$login = $this->coachMobileLogin();
|
|
$submit = $this->submitQuestionnaireAs(
|
|
$login['token'],
|
|
$qnId,
|
|
$clientCode,
|
|
[['questionID' => $this->fixture()->questionShortId, 'answerOptionKey' => 'yes']],
|
|
);
|
|
$this->assertApiOk($submit);
|
|
$this->assertFalse($submit['data']['cycleAdvanced'] ?? true);
|
|
$this->assertSame(1, $submit['data']['cycleNumber'] ?? 0);
|
|
|
|
$clientsRes = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires', null, [
|
|
'clients' => '1',
|
|
]);
|
|
$this->assertApiOk($clientsRes);
|
|
$plain = qdb_decrypt_sensitive_envelope($clientsRes['data'], $login['token']);
|
|
$payload = json_decode($plain, true, 512, JSON_THROW_ON_ERROR);
|
|
$progress = $payload['clients'][0]['programProgress'];
|
|
$this->assertTrue($progress['cycleComplete']);
|
|
$this->assertTrue($progress['programComplete']);
|
|
$this->assertFalse($progress['autoRepeatSessions']);
|
|
}
|
|
|
|
public function testRejectUnknownQuestionnaire(): void
|
|
{
|
|
$token = $this->adminToken();
|
|
$res = $this->api()->request('POST', 'program_sessions', [
|
|
'name' => 'Invalid',
|
|
'questionnaires' => [
|
|
['questionnaireID' => 'does-not-exist'],
|
|
],
|
|
], ['Authorization' => 'Bearer ' . $token]);
|
|
$this->assertApiError($res, 'INVALID_FIELD', 400);
|
|
}
|
|
|
|
public function testCoachCannotCreateSession(): void
|
|
{
|
|
$mobile = $this->coachMobileLogin();
|
|
$res = $this->api()->request('POST', 'program_sessions', [
|
|
'name' => 'Blocked',
|
|
], ['Authorization' => 'Bearer ' . $mobile['token']]);
|
|
$this->assertApiError($res, 'FORBIDDEN', 403);
|
|
}
|
|
}
|