Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins3

# Conflicts:
#	composer.json
#	composer.lock
This commit is contained in:
fogelito
2026-03-18 10:35:55 +02:00
249 changed files with 33630 additions and 3480 deletions
@@ -196,25 +196,33 @@ class FunctionsCustomServerTest extends Scope
{
$specifications = $this->listSpecifications();
$this->assertEquals(200, $specifications['headers']['status-code']);
$this->assertGreaterThan(0, $specifications['body']['total']);
$this->assertGreaterThanOrEqual(2, $specifications['body']['total']);
$this->assertArrayHasKey(0, $specifications['body']['specifications']);
$this->assertArrayHasKey(1, $specifications['body']['specifications']);
$this->assertArrayHasKey('memory', $specifications['body']['specifications'][0]);
$this->assertArrayHasKey('cpus', $specifications['body']['specifications'][0]);
$this->assertArrayHasKey('enabled', $specifications['body']['specifications'][0]);
$this->assertArrayHasKey('slug', $specifications['body']['specifications'][0]);
$this->assertArrayHasKey('memory', $specifications['body']['specifications'][1]);
$this->assertArrayHasKey('cpus', $specifications['body']['specifications'][1]);
$this->assertArrayHasKey('enabled', $specifications['body']['specifications'][1]);
$this->assertArrayHasKey('slug', $specifications['body']['specifications'][1]);
$function = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Specs function',
'runtime' => 'node-22',
'specification' => $specifications['body']['specifications'][0]['slug']
'buildSpecification' => $specifications['body']['specifications'][0]['slug'],
'runtimeSpecification' => $specifications['body']['specifications'][1]['slug'],
]);
$this->assertEquals(201, $function['headers']['status-code']);
$this->assertEquals($specifications['body']['specifications'][0]['slug'], $function['body']['specification']);
$this->assertEquals($specifications['body']['specifications'][0]['slug'], $function['body']['buildSpecification']);
$this->assertEquals($specifications['body']['specifications'][1]['slug'], $function['body']['runtimeSpecification']);
$function = $this->getFunction($function['body']['$id']);
$this->assertEquals(200, $function['headers']['status-code']);
$this->assertEquals($specifications['body']['specifications'][0]['slug'], $function['body']['specification']);
$this->assertEquals($specifications['body']['specifications'][0]['slug'], $function['body']['buildSpecification']);
$this->assertEquals($specifications['body']['specifications'][1]['slug'], $function['body']['runtimeSpecification']);
$this->cleanupFunction($function['body']['$id']);
@@ -222,7 +230,15 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Specs function',
'runtime' => 'node-22',
'specification' => 'cheap-please'
'buildSpecification' => 'cheap-please'
]);
$this->assertEquals(400, $function['headers']['status-code']);
$function = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Specs function',
'runtime' => 'node-22',
'runtimeSpecification' => 'cheap-please'
]);
$this->assertEquals(400, $function['headers']['status-code']);
}
@@ -1552,12 +1568,12 @@ class FunctionsCustomServerTest extends Scope
'timeout' => 15,
'runtime' => 'node-22',
'entrypoint' => 'index.js',
'specification' => Specification::S_1VCPU_1GB,
'runtimeSpecification' => Specification::S_1VCPU_1GB,
]);
$this->assertEquals(200, $function['headers']['status-code']);
$this->assertNotEmpty($function['body']['$id']);
$this->assertEquals(Specification::S_1VCPU_1GB, $function['body']['specification']);
$this->assertEquals(Specification::S_1VCPU_1GB, $function['body']['runtimeSpecification']);
// Verify the updated specs
$execution = $this->createExecution($functionId);
@@ -1586,12 +1602,12 @@ class FunctionsCustomServerTest extends Scope
'timeout' => 15,
'runtime' => 'node-22',
'entrypoint' => 'index.js',
'specification' => Specification::S_1VCPU_512MB,
'runtimeSpecification' => Specification::S_1VCPU_512MB,
]);
$this->assertEquals(200, $function['headers']['status-code']);
$this->assertNotEmpty($function['body']['$id']);
$this->assertEquals(Specification::S_1VCPU_512MB, $function['body']['specification']);
$this->assertEquals(Specification::S_1VCPU_512MB, $function['body']['runtimeSpecification']);
// Verify the updated specs
$execution = $this->createExecution($functionId);
@@ -1616,11 +1632,29 @@ class FunctionsCustomServerTest extends Scope
'timeout' => 15,
'runtime' => 'node-22',
'entrypoint' => 'index.js',
'specification' => 's-2vcpu-512mb', // Invalid specification
'buildSpecification' => 's-2vcpu-512mb', // Invalid specification
]);
$this->assertEquals(400, $function['headers']['status-code']);
$this->assertStringStartsWith('Invalid `specification` param: Specification must be one of:', $function['body']['message']);
$this->assertStringStartsWith('Invalid `buildSpecification` param: Specification must be one of:', $function['body']['message']);
$function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Test1',
'events' => [
'users.*.update.name',
'users.*.update.email',
],
'timeout' => 15,
'runtime' => 'node-22',
'entrypoint' => 'index.js',
'runtimeSpecification' => 's-2vcpu-512mb', // Invalid specification
]);
$this->assertEquals(400, $function['headers']['status-code']);
$this->assertStringStartsWith('Invalid `runtimeSpecification` param: Specification must be one of:', $function['body']['message']);
}
public function testDeleteDeployment(): void
@@ -1984,7 +2018,7 @@ class FunctionsCustomServerTest extends Scope
$functionId = $this->setupFunction([
'functionId' => ID::unique(),
'name' => 'Test Scopes executions',
'commands' => 'bash setup.sh && npm install',
'commands' => 'bash setup.sh && npm ci',
'runtime' => 'node-22',
'entrypoint' => 'index.js',
'scopes' => ['users.read'],
@@ -2221,6 +2255,8 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertArrayNotHasKey('scopes', $response['body']);
$this->assertArrayNotHasKey('specification', $response['body']);
$this->assertArrayNotHasKey('buildSpecification', $response['body']);
$this->assertArrayNotHasKey('runtimeSpecification', $response['body']);
// get function with 1.5.0 response format header
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $response['body']['$id'], array_merge([
@@ -2231,13 +2267,31 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(200, $function['headers']['status-code']);
$this->assertArrayNotHasKey('scopes', $function['body']);
$this->assertArrayNotHasKey('buildSpecification', $function['body']);
$this->assertArrayNotHasKey('runtimeSpecification', $function['body']);
$this->assertArrayNotHasKey('specification', $function['body']);
$function = $this->getFunction($function['body']['$id']);
// get function with 1.8.0 response format header
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $response['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-response-format' => '1.8.0', // add response format header
], $this->getHeaders()));
$this->assertEquals(200, $function['headers']['status-code']);
$this->assertArrayHasKey('scopes', $function['body']);
$this->assertArrayHasKey('specification', $function['body']);
$this->assertArrayNotHasKey('buildSpecification', $function['body']);
$this->assertArrayNotHasKey('runtimeSpecification', $function['body']);
// get function with latest version
$function = $this->getFunction($function['body']['$id']);
$this->assertEquals(200, $function['headers']['status-code']);
$this->assertArrayHasKey('scopes', $function['body']);
$this->assertArrayNotHasKey('specification', $function['body']);
$this->assertArrayHasKey('buildSpecification', $function['body']);
$this->assertArrayHasKey('runtimeSpecification', $function['body']);
$functionId = $function['body']['$id'] ?? '';
$this->cleanupFunction($functionId);
@@ -2387,15 +2441,17 @@ class FunctionsCustomServerTest extends Scope
'entrypoint' => 'index.js',
'logging' => false,
'execute' => ['any'],
'specification' => Specification::S_2VCPU_2GB,
'buildSpecification' => Specification::S_2VCPU_2GB,
'runtimeSpecification' => Specification::S_1VCPU_1GB,
'commands' => 'echo $APPWRITE_FUNCTION_MEMORY:$APPWRITE_FUNCTION_CPUS',
]);
$this->assertEquals(201, $function['headers']['status-code']);
$this->assertEquals(Specification::S_2VCPU_2GB, $function['body']['specification']);
$this->assertEquals(Specification::S_2VCPU_2GB, $function['body']['buildSpecification']);
$this->assertEquals(Specification::S_1VCPU_1GB, $function['body']['runtimeSpecification']);
$this->assertNotEmpty($function['body']['$id']);
$functionId = $functionId = $function['body']['$id'] ?? '';
$functionId = $function['body']['$id'] ?? '';
$deploymentId = $this->setupDeployment($functionId, [
'code' => $this->packageFunction('basic'),
@@ -2414,8 +2470,8 @@ class FunctionsCustomServerTest extends Scope
$this->assertNotEmpty($execution['body']['$id']);
$executionResponse = json_decode($execution['body']['responseBody'], true);
$this->assertEquals('2048', $executionResponse['APPWRITE_FUNCTION_MEMORY']);
$this->assertEquals('2', $executionResponse['APPWRITE_FUNCTION_CPUS']);
$this->assertEquals('1024', $executionResponse['APPWRITE_FUNCTION_MEMORY']);
$this->assertEquals('1', $executionResponse['APPWRITE_FUNCTION_CPUS']);
$this->cleanupFunction($functionId);
}
@@ -2771,7 +2827,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertLessThanOrEqual(APP_FUNCTION_LOG_LENGTH_LIMIT, strlen($logs));
$this->assertStringStartsWith('[WARNING] Logs truncated', $logs);
$this->assertStringNotContainsString('z', $logs);
$this->assertStringContainsString('a', $logs);
// Verify errors are truncated and warning message is present at the beginning
@@ -2779,9 +2834,123 @@ class FunctionsCustomServerTest extends Scope
$this->assertLessThanOrEqual(APP_FUNCTION_ERROR_LENGTH_LIMIT, strlen($errors));
$this->assertStringStartsWith('[WARNING] Errors truncated', $errors);
$this->assertStringNotContainsString('z', $errors);
$this->assertStringContainsString('a', $errors);
$this->cleanupFunction($functionId);
}
public function testFunctionDeploymentRetention(): void
{
$functionIds = [];
// Default
$response = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test retention function',
'runtime' => 'node-22',
]);
$this->assertSame(201, $response['headers']['status-code']);
$this->assertSame(0, $response['body']['deploymentRetention']);
$functionIds[] = $response['body']['$id'];
$response = $this->getFunction($response['body']['$id']);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(0, $response['body']['deploymentRetention']);
// Success values
$response = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test retention function',
'runtime' => 'node-22',
'deploymentRetention' => 0
]);
$this->assertSame(201, $response['headers']['status-code']);
$this->assertSame(0, $response['body']['deploymentRetention']);
$functionIds[] = $response['body']['$id'];
$response = $this->getFunction($response['body']['$id']);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(0, $response['body']['deploymentRetention']);
$response = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test retention function',
'runtime' => 'node-22',
'deploymentRetention' => 180
]);
$this->assertSame(201, $response['headers']['status-code']);
$this->assertSame(180, $response['body']['deploymentRetention']);
$functionIds[] = $response['body']['$id'];
$response = $this->getFunction($response['body']['$id']);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(180, $response['body']['deploymentRetention']);
// Failure values
$response = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test retention function',
'runtime' => 'node-22',
'deploymentRetention' => 999999
]);
$this->assertSame(400, $response['headers']['status-code']);
$response = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test retention function',
'runtime' => 'node-22',
'deploymentRetention' => -1
]);
$this->assertSame(400, $response['headers']['status-code']);
// Update flow
$response = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test retention function',
'runtime' => 'node-22',
'deploymentRetention' => 180
]);
$this->assertSame(201, $response['headers']['status-code']);
$this->assertSame(180, $response['body']['deploymentRetention']);
$functionIds[] = $response['body']['$id'];
$functionIdForUpdate = $response['body']['$id'];
$response = $this->updateFunction($functionIdForUpdate, [
'name' => 'Test retention function',
'deploymentRetention' => 90
]);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(90, $response['body']['deploymentRetention']);
$response = $this->getFunction($functionIdForUpdate);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(90, $response['body']['deploymentRetention']);
$response = $this->updateFunction($functionIdForUpdate, [
'name' => 'Test retention function',
]);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(0, $response['body']['deploymentRetention']);
$response = $this->getFunction($functionIdForUpdate);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(0, $response['body']['deploymentRetention']);
// Failed update flow
$response = $this->updateFunction($functionIdForUpdate, [
'name' => 'Test retention function',
'deploymentRetention' => -1
]);
$this->assertSame(400, $response['headers']['status-code']);
$response = $this->updateFunction($functionIdForUpdate, [
'name' => 'Test retention function',
'deploymentRetention' => 999999
]);
$this->assertSame(400, $response['headers']['status-code']);
foreach ($functionIds as $functionId) {
$this->cleanupFunction($functionId);
}
}
}