bootstrapDatabase(); } protected function bootstrapDatabase(): void { if (defined('QDB_PATH') && is_file(QDB_PATH)) { @unlink(QDB_PATH); } if (defined('QDB_LOCK') && is_file(QDB_LOCK)) { @unlink(QDB_LOCK); } [$pdo, $tmpDb, $lockFp] = qdb_open(true); $this->fixtureIds = DatabaseSeeder::seed($pdo); qdb_save($tmpDb, $lockFp); } protected function api(): ApiClient { return self::$api; } protected function fixture(): FixtureIds { if ($this->fixtureIds === null) { $this->fail('Fixture not seeded'); } return $this->fixtureIds; } protected function adminToken(): string { return $this->api()->loginWebAndGetToken( DatabaseSeeder::ADMIN_USERNAME, DatabaseSeeder::PASSWORD )['token']; } protected function supervisorToken(): string { return $this->api()->loginWebAndGetToken( DatabaseSeeder::SUPERVISOR_USERNAME, DatabaseSeeder::PASSWORD )['token']; } /** * @return array{token: string, data: array} */ protected function coachMobileLogin(): array { return $this->api()->loginMobileAndGetToken( DatabaseSeeder::COACH_USERNAME, DatabaseSeeder::PASSWORD ); } protected function assertApiOk(array $response, string $message = ''): void { $this->assertTrue($response['ok'] ?? false, $message . ' ' . json_encode($response['error'] ?? [])); } protected function assertApiError(array $response, string $code, ?int $status = null): void { $this->assertFalse($response['ok'] ?? true); $this->assertSame($code, $response['error']['code'] ?? null); if ($status !== null) { $this->assertSame($status, $response['status']); } } protected function assertRawOk(array $response, string $contentTypePrefix): void { $this->assertTrue($response['ok'] ?? false); $this->assertTrue($response['raw'] ?? false); $this->assertStringStartsWith($contentTypePrefix, $response['headers']['Content-Type'] ?? ''); $this->assertNotSame('', $response['body'] ?? ''); } /** * @return array API response from app submit */ protected function submitFixtureQuestionnaire(): array { $login = $this->api()->loginMobileAndGetToken( DatabaseSeeder::COACH_USERNAME, DatabaseSeeder::PASSWORD ); $f = $this->fixture(); $now = time(); return $this->submitQuestionnaireAs( $login['token'], $f->questionnaireId, $f->clientCode, [ ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], ], $now - 60, $now ); } /** * @param list> $answers * @return array */ protected function submitQuestionnaireAs( string $token, string $questionnaireId, string $clientCode, array $answers, ?int $startedAt = null, ?int $completedAt = null, ?int $structureRevision = null, ): array { $now = time(); $startedAt ??= $now - 60; $completedAt ??= $now; $payload = [ 'questionnaireID' => $questionnaireId, 'clientCode' => $clientCode, 'answers' => $answers, 'startedAt' => $startedAt, 'completedAt' => $completedAt, ]; if ($structureRevision !== null) { $payload['structureRevision'] = $structureRevision; } return $this->api()->encryptedPost($token, 'app_questionnaires', $payload); } protected function submissionVersionCount(string $clientCode, string $questionnaireId): int { [$pdo, $tmpDb, $lockFp] = qdb_open(false); $stmt = $pdo->prepare( 'SELECT COUNT(*) FROM questionnaire_submission WHERE clientCode = :cc AND questionnaireID = :qn' ); $stmt->execute([':cc' => $clientCode, ':qn' => $questionnaireId]); $count = (int)$stmt->fetchColumn(); qdb_discard($tmpDb, $lockFp); return $count; } }