'abc123'], JSON_THROW_ON_ERROR), [ 'response_headers' => ['content-type' => 'application/json'], ]); }); $taskId = $this->client($http)->createTaskFromTemplate('WIEN01 2026-08-20 Müller'); self::assertSame('abc123', $taskId); self::assertSame('POST', $requests[0][0]); self::assertSame('https://clickup.test/list/555/taskTemplate/tpl-7', $requests[0][1]); self::assertSame(['name' => 'WIEN01 2026-08-20 Müller'], json_decode((string) $requests[0][2]['body'], true, 512, JSON_THROW_ON_ERROR)); self::assertContains('Authorization: pk_secret', $requests[0][2]['headers']); } public function testUpdateTaskStatusPutsTheStatus(): void { $requests = []; $http = new MockHttpClient(function (string $method, string $url, array $options) use (&$requests): MockResponse { $requests[] = [$method, $url, $options]; return new MockResponse(json_encode(['id' => 'abc123'], JSON_THROW_ON_ERROR), [ 'response_headers' => ['content-type' => 'application/json'], ]); }); $this->client($http)->updateTaskStatus('abc123', 'GRO'); self::assertSame('PUT', $requests[0][0]); self::assertSame('https://clickup.test/task/abc123', $requests[0][1]); self::assertSame(['status' => 'GRO'], json_decode((string) $requests[0][2]['body'], true, 512, JSON_THROW_ON_ERROR)); } public function testAResponseWithoutATaskIdIsAnError(): void { $http = new MockHttpClient(new MockResponse(json_encode(['err' => 'nope'], JSON_THROW_ON_ERROR), [ 'response_headers' => ['content-type' => 'application/json'], ])); $this->expectException(ClickUpException::class); $this->client($http)->createTaskFromTemplate('WIEN01 2026-08-20 Müller'); } public function testAnHttpErrorIsWrappedInADomainException(): void { $http = new MockHttpClient(new MockResponse('rate limited', ['http_code' => 429])); $this->expectException(ClickUpException::class); $this->client($http)->createTaskFromTemplate('WIEN01 2026-08-20 Müller'); } public function testAnUnconfiguredClientReportsItselfAndRefusesToRequest(): void { $http = new MockHttpClient(function (): MockResponse { self::fail('No request must be made when the client is unconfigured.'); }); $client = new ApiClient($http, new NullLogger(), 'pk_secret', '', 'tpl-7', 'https://clickup.test'); self::assertFalse($client->isConfigured()); $this->expectException(ClickUpException::class); $client->createTaskFromTemplate('WIEN01 2026-08-20 Müller'); } public function testAFullyConfiguredClientReportsItself(): void { self::assertTrue($this->client(new MockHttpClient())->isConfigured()); self::assertFalse((new ApiClient(new MockHttpClient(), new NullLogger()))->isConfigured()); } private function client(MockHttpClient $http): ApiClient { return new ApiClient($http, new NullLogger(), 'pk_secret', '555', 'tpl-7', 'https://clickup.test'); } }