From 22cbe11ff163f48b3a63ddef7f3e47185dfc9b62 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 13 Feb 2026 15:41:35 +0000 Subject: [PATCH] Add E2E tests for schedules API --- phpunit.xml | 1 + tests/e2e/Scopes/ProjectCustom.php | 2 + .../e2e/Services/Schedules/SchedulesBase.php | 32 +++ .../Schedules/SchedulesCustomServerTest.php | 256 ++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 tests/e2e/Services/Schedules/SchedulesBase.php create mode 100644 tests/e2e/Services/Schedules/SchedulesCustomServerTest.php diff --git a/phpunit.xml b/phpunit.xml index a8578995c1..8f7323b35b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -35,6 +35,7 @@ ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Messaging ./tests/e2e/Services/Migrations + ./tests/e2e/Services/Schedules ./tests/e2e/Services/Functions/FunctionsBase.php ./tests/e2e/Services/Functions/FunctionsCustomServerTest.php ./tests/e2e/Services/Functions/FunctionsCustomClientTest.php diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 1859d551a4..0bbaa13082 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -109,6 +109,8 @@ trait ProjectCustom 'subscribers.read', 'migrations.write', 'migrations.read', + 'schedules.read', + 'schedules.write', 'tokens.read', 'tokens.write', ], diff --git a/tests/e2e/Services/Schedules/SchedulesBase.php b/tests/e2e/Services/Schedules/SchedulesBase.php new file mode 100644 index 0000000000..01a7860de4 --- /dev/null +++ b/tests/e2e/Services/Schedules/SchedulesBase.php @@ -0,0 +1,32 @@ +client->call(Client::METHOD_POST, '/schedules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); + } + + protected function getSchedule(string $scheduleId): array + { + return $this->client->call(Client::METHOD_GET, '/schedules/' . $scheduleId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + } + + protected function listSchedules(array $params = []): array + { + return $this->client->call(Client::METHOD_GET, '/schedules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); + } +} diff --git a/tests/e2e/Services/Schedules/SchedulesCustomServerTest.php b/tests/e2e/Services/Schedules/SchedulesCustomServerTest.php new file mode 100644 index 0000000000..000f0d9306 --- /dev/null +++ b/tests/e2e/Services/Schedules/SchedulesCustomServerTest.php @@ -0,0 +1,256 @@ +createSchedule([ + 'resourceType' => 'function', + 'resourceId' => ID::unique(), + 'schedule' => '0 0 * * *', + 'active' => true, + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertNotEmpty($response['body']['$createdAt']); + $this->assertNotEmpty($response['body']['$updatedAt']); + $this->assertEquals('function', $response['body']['resourceType']); + $this->assertNotEmpty($response['body']['resourceId']); + $this->assertNotEmpty($response['body']['resourceUpdatedAt']); + $this->assertNotEmpty($response['body']['projectId']); + $this->assertEquals('0 0 * * *', $response['body']['schedule']); + $this->assertTrue($response['body']['active']); + $this->assertNotEmpty($response['body']['region']); + + return ['scheduleId' => $response['body']['$id']]; + } + + public function testCreateScheduleExecutionType(): void + { + $response = $this->createSchedule([ + 'resourceType' => 'execution', + 'resourceId' => ID::unique(), + 'schedule' => '*/10 * * * *', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals('execution', $response['body']['resourceType']); + $this->assertFalse($response['body']['active']); + } + + public function testCreateScheduleMessageType(): void + { + $response = $this->createSchedule([ + 'resourceType' => 'message', + 'resourceId' => ID::unique(), + 'schedule' => '0 9 * * 1', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals('message', $response['body']['resourceType']); + } + + public function testCreateScheduleInvalidResourceType(): void + { + $response = $this->createSchedule([ + 'resourceType' => 'invalid', + 'resourceId' => ID::unique(), + 'schedule' => '0 0 * * *', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testCreateScheduleInvalidCron(): void + { + $response = $this->createSchedule([ + 'resourceType' => 'function', + 'resourceId' => ID::unique(), + 'schedule' => 'not-a-cron', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testCreateScheduleMissingRequired(): void + { + // Missing resourceType + $response = $this->createSchedule([ + 'resourceId' => ID::unique(), + 'schedule' => '0 0 * * *', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Missing resourceId + $response = $this->createSchedule([ + 'resourceType' => 'function', + 'schedule' => '0 0 * * *', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Missing schedule + $response = $this->createSchedule([ + 'resourceType' => 'function', + 'resourceId' => ID::unique(), + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * @depends testCreateSchedule + */ + public function testGetSchedule(array $data): void + { + $scheduleId = $data['scheduleId']; + + /** + * Test for SUCCESS + */ + $response = $this->getSchedule($scheduleId); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($scheduleId, $response['body']['$id']); + $this->assertEquals('function', $response['body']['resourceType']); + $this->assertEquals('0 0 * * *', $response['body']['schedule']); + $this->assertTrue($response['body']['active']); + } + + public function testGetScheduleNotFound(): void + { + $response = $this->getSchedule('nonexistent'); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * @depends testCreateSchedule + */ + public function testListSchedules(array $data): void + { + /** + * Test for SUCCESS + */ + $response = $this->listSchedules(); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsArray($response['body']['schedules']); + $this->assertGreaterThanOrEqual(1, $response['body']['total']); + $this->assertGreaterThanOrEqual(1, \count($response['body']['schedules'])); + + // Verify schedule structure + $schedule = $response['body']['schedules'][0]; + $this->assertArrayHasKey('$id', $schedule); + $this->assertArrayHasKey('$createdAt', $schedule); + $this->assertArrayHasKey('$updatedAt', $schedule); + $this->assertArrayHasKey('resourceType', $schedule); + $this->assertArrayHasKey('resourceId', $schedule); + $this->assertArrayHasKey('projectId', $schedule); + $this->assertArrayHasKey('schedule', $schedule); + $this->assertArrayHasKey('active', $schedule); + $this->assertArrayHasKey('region', $schedule); + } + + /** + * @depends testCreateSchedule + */ + public function testListSchedulesWithQuery(array $data): void + { + // Filter by resourceType + $response = $this->listSchedules([ + 'queries' => ['equal("resourceType", "function")'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertGreaterThanOrEqual(1, $response['body']['total']); + + foreach ($response['body']['schedules'] as $schedule) { + $this->assertEquals('function', $schedule['resourceType']); + } + + // Filter by active status + $response = $this->listSchedules([ + 'queries' => ['equal("active", true)'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + foreach ($response['body']['schedules'] as $schedule) { + $this->assertTrue($schedule['active']); + } + } + + public function testListSchedulesWithTotalDisabled(): void + { + $response = $this->listSchedules([ + 'total' => false, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(0, $response['body']['total']); + $this->assertIsArray($response['body']['schedules']); + } + + public function testListSchedulesInvalidQuery(): void + { + $response = $this->listSchedules([ + 'queries' => ['equal("nonexistent", "value")'], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testScheduleProjectIsolation(): void + { + // Create a schedule in the current project + $response = $this->createSchedule([ + 'resourceType' => 'function', + 'resourceId' => ID::unique(), + 'schedule' => '0 12 * * *', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $scheduleId = $response['body']['$id']; + + // Create a fresh project with its own key + $freshProject = $this->getProject(true); + + // Try to get the schedule from the other project + $response = $this->client->call(\Tests\E2E\Client::METHOD_GET, '/schedules/' . $scheduleId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $freshProject['$id'], + 'x-appwrite-key' => $freshProject['apiKey'], + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // List should not include schedules from other projects + $response = $this->client->call(\Tests\E2E\Client::METHOD_GET, '/schedules', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $freshProject['$id'], + 'x-appwrite-key' => $freshProject['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $scheduleIds = array_column($response['body']['schedules'], '$id'); + $this->assertNotContains($scheduleId, $scheduleIds); + } +}