'localhost', 'aud' => 'localhost', 'iat' => time(), 'nbf' => time(), 'exp' => time() + 3600, 'uid' => 1, 'email' => 'admin@admin.com', ]; $this->token = JWT::encode($payload, $key, 'HS256'); } private function authHeaders(): array { return ['Cookie' => 'token=' . $this->token]; } private function createContact(array $data = []): array { $payload = array_merge([ 'NameFirst' => 'Test', 'NameLast' => 'Contact ' . uniqid(), 'Specialty' => 'GP', ], $data); $response = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('post', $this->endpoint, $payload); $response->assertStatus(201); $decoded = json_decode($response->getJSON(), true); return $decoded['data']; } public function testPartialUpdateContactSuccess() { $contact = $this->createContact(); $id = $contact['ContactID']; $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$id}", ['NameFirst' => 'Updated']); $patch->assertStatus(200); $patchData = json_decode($patch->getJSON(), true); $this->assertEquals('success', $patchData['status']); $show = $this->withHeaders($this->authHeaders())->call('get', "{$this->endpoint}/{$id}"); $show->assertStatus(200); $showData = json_decode($show->getJSON(), true)['data']; $this->assertEquals('Updated', $showData['NameFirst']); } public function testPartialUpdateContactNotFound() { $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/999999", ['NameFirst' => 'Updated']); $patch->assertStatus(404); } public function testPartialUpdateContactInvalidId() { $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/invalid", ['NameFirst' => 'Updated']); $patch->assertStatus(400); } public function testPartialUpdateContactEmptyPayload() { $contact = $this->createContact(); $id = $contact['ContactID']; $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$id}", []); $patch->assertStatus(400); } public function testPartialUpdateContactSingleField() { $contact = $this->createContact(); $id = $contact['ContactID']; $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$id}", ['Specialty' => 'Pathology']); $patch->assertStatus(200); $showData = json_decode($this->withHeaders($this->authHeaders()) ->call('get', "{$this->endpoint}/{$id}") ->getJSON(), true)['data']; $this->assertEquals('Pathology', $showData['Specialty']); } public function testPartialUpdateContactDetailOperations() { $contact = $this->createContact([ 'Details' => [ [ 'SiteID' => '1', 'ContactCode' => 'CODE1', 'ContactEmail' => 'code1@example.com', 'OccupationID' => '1', 'JobTitle' => 'Doctor', 'Department' => 'General', ], [ 'SiteID' => '2', 'ContactCode' => 'CODE2', 'ContactEmail' => 'code2@example.com', 'OccupationID' => '2', 'JobTitle' => 'Specialist', 'Department' => 'Laboratory', ], ], ]); $showBefore = $this->withHeaders($this->authHeaders()) ->call('get', "{$this->endpoint}/{$contact['ContactID']}"); $showBefore->assertStatus(200); $beforeData = json_decode($showBefore->getJSON(), true)['data']; $keepDetail = $beforeData['Details'][0]; $deleteDetail = $beforeData['Details'][1]; $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$contact['ContactID']}", [ 'Details' => [ 'edited' => [ [ 'ContactDetID' => $keepDetail['ContactDetID'], 'JobTitle' => 'Senior Doctor', ], ], 'created' => [ [ 'SiteID' => '3', 'ContactCode' => 'CODE3', 'ContactEmail' => 'code3@example.com', 'OccupationID' => '3', 'JobTitle' => 'Consultant', 'Department' => 'Pathology', ], ], 'deleted' => [$deleteDetail['ContactDetID']], ], ]); $patch->assertStatus(200); $showAfter = $this->withHeaders($this->authHeaders()) ->call('get', "{$this->endpoint}/{$contact['ContactID']}"); $showAfter->assertStatus(200); $afterData = json_decode($showAfter->getJSON(), true)['data']; $this->assertCount(2, $afterData['Details']); $detailIds = array_column($afterData['Details'], 'ContactDetID'); $this->assertContains($keepDetail['ContactDetID'], $detailIds); $updatedDetails = array_values(array_filter($afterData['Details'], static fn ($row) => $row['ContactDetID'] === $keepDetail['ContactDetID'])); $this->assertNotEmpty($updatedDetails); } }