diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000000..2cb350d195 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,45 @@ +name: "Tests" + +on: [pull_request] +jobs: + tests: + name: Unit & E2E + runs-on: self-hosted + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + - name: Build Appwrite + # Upstream bug causes buildkit pulls to fail so prefetch base images + # https://github.com/moby/moby/issues/41864 + run: | + echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env + docker pull composer:2.0 + docker pull php:8.0-cli-alpine + docker compose build --progress=plain + docker compose up -d + sleep 10 + - name: Doctor + run: docker compose exec -T appwrite doctor + + - name: Environment Variables + run: docker compose exec -T appwrite vars + + - name: Run Tests + run: docker compose exec -T appwrite test --debug + + - name: Teardown + if: always() + run: | + docker compose down -v + docker ps -aq | xargs docker rm --force diff --git a/.travis.yml b/.travis.yml.tmp similarity index 100% rename from .travis.yml rename to .travis.yml.tmp diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 4269fa04dc..ee42b9effa 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -273,12 +273,6 @@ class Realtime extends Adapter $channels[] = 'teams.' . $payload->getId(); $roles = ['team:' . $payload->getId()]; - break; - case strpos($event, 'database.collections.') === 0: - $channels[] = 'collections'; - $channels[] = 'collections.' . $payload->getId(); - $roles = $payload->getRead(); - break; case strpos($event, 'database.documents.') === 0: $channels[] = 'documents'; @@ -287,8 +281,9 @@ class Realtime extends Adapter $roles = $payload->getRead(); break; - case strpos($event, 'storage.') === 0: + case strpos($event, 'storage.files') === 0: $channels[] = 'files'; + $channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files'; $channels[] = 'files.' . $payload->getId(); $roles = $payload->getRead(); diff --git a/src/Appwrite/Utopia/Response/Model/File.php b/src/Appwrite/Utopia/Response/Model/File.php index 50f4654ddd..19dde2a235 100644 --- a/src/Appwrite/Utopia/Response/Model/File.php +++ b/src/Appwrite/Utopia/Response/Model/File.php @@ -16,6 +16,12 @@ class File extends Model 'default' => '', 'example' => '5e5ea5c16897e', ]) + ->addRule('bucketId', [ + 'type' => self::TYPE_STRING, + 'description' => 'Bucket ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) ->addRule('$read', [ 'type' => self::TYPE_STRING, 'description' => 'File read permissions.', diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 82fd642a08..1dc7b71c10 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -156,7 +156,6 @@ class Client */ public function call(string $method, string $path = '', array $headers = [], array $params = []) { - usleep(50000); $headers = array_merge($this->headers, $headers); $ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : '')); $responseHeaders = []; diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index 01673ab7e7..267d155c65 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -33,8 +33,8 @@ abstract class Scope extends TestCase protected function getLastEmail():array { - sleep(10); - + sleep(5); + $emails = json_decode(file_get_contents('http://maildev:1080/email'), true); if ($emails && is_array($emails)) { @@ -46,7 +46,7 @@ abstract class Scope extends TestCase protected function getLastRequest():array { - sleep(5); + sleep(1); $resquest = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true); $resquest['data'] = json_decode($resquest['data'], true); diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 216b8c0831..1f5c633f1e 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -212,7 +212,6 @@ trait DatabaseBase $this->assertEquals(201, $string['headers']['status-code']); $this->assertEquals('string', $string['body']['key']); $this->assertEquals('string', $string['body']['type']); - $this->assertEquals('processing', $string['body']['status']); $this->assertEquals(false, $string['body']['required']); $this->assertEquals(false, $string['body']['array']); $this->assertEquals(16, $string['body']['size']); @@ -221,7 +220,6 @@ trait DatabaseBase $this->assertEquals(201, $email['headers']['status-code']); $this->assertEquals('email', $email['body']['key']); $this->assertEquals('string', $email['body']['type']); - $this->assertEquals('processing', $email['body']['status']); $this->assertEquals(false, $email['body']['required']); $this->assertEquals(false, $email['body']['array']); $this->assertEquals('email', $email['body']['format']); @@ -230,7 +228,6 @@ trait DatabaseBase $this->assertEquals(201, $enum['headers']['status-code']); $this->assertEquals('enum', $enum['body']['key']); $this->assertEquals('string', $enum['body']['type']); - $this->assertEquals('processing', $enum['body']['status']); $this->assertEquals(false, $enum['body']['required']); $this->assertEquals(false, $enum['body']['array']); $this->assertEquals('enum', $enum['body']['format']); @@ -241,7 +238,6 @@ trait DatabaseBase $this->assertEquals(201, $ip['headers']['status-code']); $this->assertEquals('ip', $ip['body']['key']); $this->assertEquals('string', $ip['body']['type']); - $this->assertEquals('processing', $ip['body']['status']); $this->assertEquals(false, $ip['body']['required']); $this->assertEquals(false, $ip['body']['array']); $this->assertEquals('ip', $ip['body']['format']); @@ -250,7 +246,6 @@ trait DatabaseBase $this->assertEquals(201, $url['headers']['status-code']); $this->assertEquals('url', $url['body']['key']); $this->assertEquals('string', $url['body']['type']); - $this->assertEquals('processing', $url['body']['status']); $this->assertEquals(false, $url['body']['required']); $this->assertEquals(false, $url['body']['array']); $this->assertEquals('url', $url['body']['format']); @@ -259,7 +254,6 @@ trait DatabaseBase $this->assertEquals(201, $integer['headers']['status-code']); $this->assertEquals('integer', $integer['body']['key']); $this->assertEquals('integer', $integer['body']['type']); - $this->assertEquals('processing', $integer['body']['status']); $this->assertEquals(false, $integer['body']['required']); $this->assertEquals(false, $integer['body']['array']); $this->assertEquals(1, $integer['body']['min']); @@ -269,7 +263,6 @@ trait DatabaseBase $this->assertEquals(201, $float['headers']['status-code']); $this->assertEquals('float', $float['body']['key']); $this->assertEquals('double', $float['body']['type']); - $this->assertEquals('processing', $float['body']['status']); $this->assertEquals(false, $float['body']['required']); $this->assertEquals(false, $float['body']['array']); $this->assertEquals(1.5, $float['body']['min']); @@ -279,13 +272,12 @@ trait DatabaseBase $this->assertEquals(201, $boolean['headers']['status-code']); $this->assertEquals('boolean', $boolean['body']['key']); $this->assertEquals('boolean', $boolean['body']['type']); - $this->assertEquals('processing', $boolean['body']['status']); $this->assertEquals(false, $boolean['body']['required']); $this->assertEquals(false, $boolean['body']['array']); $this->assertEquals(true, $boolean['body']['default']); // wait for database worker to create attributes - sleep(5); + sleep(30); $stringResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$string['body']['key']}",array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index f4e1d48041..d268cb492d 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -588,7 +588,7 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals(201, $attribute['headers']['status-code']); } - sleep(5); + sleep(30); $tooMany = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Realtime/RealtimeBase.php b/tests/e2e/Services/Realtime/RealtimeBase.php index 35e20d0a94..a47f36aba2 100644 --- a/tests/e2e/Services/Realtime/RealtimeBase.php +++ b/tests/e2e/Services/Realtime/RealtimeBase.php @@ -623,19 +623,6 @@ trait RealtimeBase 'permission' => 'collection' ]); - $response = json_decode($client->receive(), true); - - $this->assertArrayHasKey('type', $response); - $this->assertArrayHasKey('data', $response); - $this->assertEquals('event', $response['type']); - $this->assertNotEmpty($response['data']); - $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(2, $response['data']['channels']); - $this->assertContains('collections', $response['data']['channels']); - $this->assertContains('collections.' . $actors['body']['$id'], $response['data']['channels']); - $this->assertEquals('database.collections.create', $response['data']['event']); - $this->assertNotEmpty($response['data']['payload']); - $data = ['actorsId' => $actors['body']['$id']]; $name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([ @@ -819,9 +806,10 @@ trait RealtimeBase $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(2, $response['data']['channels']); + $this->assertCount(3, $response['data']['channels']); $this->assertContains('files', $response['data']['channels']); $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']); + $this->assertContains('buckets.' . $bucketId . '.files', $response['data']['channels']); $this->assertEquals('storage.files.create', $response['data']['event']); $this->assertNotEmpty($response['data']['payload']); @@ -830,7 +818,7 @@ trait RealtimeBase /** * Test File Update */ - $this->client->call(Client::METHOD_PUT, '/storage/buckets/'.$bucketId.'files/' . $data['fileId'], array_merge([ + $this->client->call(Client::METHOD_PUT, '/storage/buckets/'.$bucketId.'/files/' . $data['fileId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -845,8 +833,9 @@ trait RealtimeBase $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(2, $response['data']['channels']); + $this->assertCount(3, $response['data']['channels']); $this->assertContains('files', $response['data']['channels']); + $this->assertContains('buckets.'.$bucketId.'.files', $response['data']['channels']); $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']); $this->assertEquals('storage.files.update', $response['data']['event']); $this->assertNotEmpty($response['data']['payload']); @@ -854,7 +843,7 @@ trait RealtimeBase /** * Test File Delete */ - $this->client->call(Client::METHOD_DELETE, '/storage/buckets/'. $bucketId . 'files/' . $data['fileId'], array_merge([ + $this->client->call(Client::METHOD_DELETE, '/storage/buckets/'. $bucketId . '/files/' . $data['fileId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -866,8 +855,9 @@ trait RealtimeBase $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(2, $response['data']['channels']); + $this->assertCount(3, $response['data']['channels']); $this->assertContains('files', $response['data']['channels']); + $this->assertContains('buckets.'.$bucketId.'.files', $response['data']['channels']); $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']); $this->assertEquals('storage.files.delete', $response['data']['event']); $this->assertNotEmpty($response['data']['payload']);