2025-01-01 20:51:30 +00:00
< ? php
namespace Tests\E2E\Services\Sites ;
2025-03-08 22:33:37 +00:00
use Ahc\Jwt\JWT ;
use Appwrite\Platform\Modules\Compute\Specification ;
use Appwrite\Tests\Retry ;
2025-01-06 21:03:32 +00:00
use Tests\E2E\Client ;
2025-01-01 20:51:30 +00:00
use Tests\E2E\Scopes\ProjectCustom ;
use Tests\E2E\Scopes\Scope ;
use Tests\E2E\Scopes\SideServer ;
2026-02-10 05:04:24 +00:00
use Utopia\Console ;
2025-01-01 20:51:30 +00:00
use Utopia\Database\Document ;
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Query ;
2025-03-08 22:33:37 +00:00
use Utopia\Database\Validator\Datetime as DatetimeValidator ;
use Utopia\System\System ;
2025-01-01 20:51:30 +00:00
class SitesCustomServerTest extends Scope
{
use SitesBase ;
use ProjectCustom ;
use SideServer ;
2025-03-08 22:33:37 +00:00
public function testListSpecs () : void
{
$specifications = $this -> listSpecifications ();
$this -> assertEquals ( 200 , $specifications [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $specifications [ 'body' ][ 'total' ]);
$this -> assertArrayHasKey ( 0 , $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 ]);
$site = $this -> createSite ([
'buildRuntime' => 'node-22' ,
'framework' => 'other' ,
'name' => 'Specs site' ,
'siteId' => ID :: unique (),
'specification' => $specifications [ 'body' ][ 'specifications' ][ 0 ][ 'slug' ]
]);
$this -> assertEquals ( 201 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $specifications [ 'body' ][ 'specifications' ][ 0 ][ 'slug' ], $site [ 'body' ][ 'specification' ]);
$site = $this -> getSite ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $specifications [ 'body' ][ 'specifications' ][ 0 ][ 'slug' ], $site [ 'body' ][ 'specification' ]);
$this -> cleanupSite ( $site [ 'body' ][ '$id' ]);
$site = $this -> createSite ([
'buildRuntime' => 'node-22' ,
'framework' => 'other' ,
'name' => 'Specs site' ,
'siteId' => ID :: unique (),
'specification' => 'cheap-please'
]);
$this -> assertEquals ( 400 , $site [ 'headers' ][ 'status-code' ]);
}
public function testCreateSite () : void
{
/**
* Test for SUCCESS
*/
$site = $this -> createSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$siteId = $site [ 'body' ][ '$id' ] ? ? '' ;
$dateValidator = new DateTimeValidator ();
$this -> assertEquals ( 201 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Site' , $site [ 'body' ][ 'name' ]);
$this -> assertEquals ( 'other' , $site [ 'body' ][ 'framework' ]);
$this -> assertEquals ( true , $dateValidator -> isValid ( $site [ 'body' ][ '$createdAt' ]));
$this -> assertEquals ( true , $dateValidator -> isValid ( $site [ 'body' ][ '$updatedAt' ]));
$this -> assertEquals ( 'node-22' , $site [ 'body' ][ 'buildRuntime' ]);
$this -> assertEquals ( null , $site [ 'body' ][ 'fallbackFile' ]);
$this -> assertEquals ( './' , $site [ 'body' ][ 'outputDirectory' ]);
$variable = $this -> createVariable ( $siteId , [
'key' => 'siteKey1' ,
'value' => 'siteValue1' ,
]);
$variable2 = $this -> createVariable ( $siteId , [
'key' => 'siteKey2' ,
'value' => 'siteValue2' ,
]);
$variable3 = $this -> createVariable ( $siteId , [
'key' => 'siteKey3' ,
'value' => 'siteValue3' ,
]);
$this -> assertEquals ( 201 , $variable [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 201 , $variable2 [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 201 , $variable3 [ 'headers' ][ 'status-code' ]);
$this -> cleanupSite ( $siteId );
}
public function testConsoleAvailabilityEndpoint () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Test Site' ,
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$response = $this -> client -> call ( Client :: METHOD_GET , '/console/resources' , [
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => 'a_session_console=' . $this -> getRoot ()[ 'session' ],
'x-appwrite-project' => 'console' ,
], [
'type' => 'rules' ,
'value' => $domain ,
]);
$this -> assertEquals ( 409 , $response [ 'headers' ][ 'status-code' ]); // domain unavailable
$nonExistingDomain = " non-existent-subdomain.sites.localhost " ;
$response = $this -> client -> call ( Client :: METHOD_GET , '/console/resources' , [
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => 'a_session_console=' . $this -> getRoot ()[ 'session' ],
'x-appwrite-project' => 'console' ,
], [
'type' => 'rules' ,
'value' => $nonExistingDomain ,
]);
$this -> assertEquals ( 204 , $response [ 'headers' ][ 'status-code' ]); // domain available
$this -> cleanupSite ( $siteId );
$this -> assertEventually ( function () use ( $siteId ) {
$rule = $this -> client -> call ( Client :: METHOD_GET , '/proxy/rules' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'queries' => [
2026-02-12 11:13:05 +00:00
Query :: equal ( 'deploymentResourceId' , [ $siteId ]) -> toString ()
2025-03-08 22:33:37 +00:00
]
]);
$this -> assertEquals ( 200 , $rule [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 0 , $rule [ 'body' ][ 'total' ]);
}, 5000 , 500 );
$response = $this -> client -> call ( Client :: METHOD_GET , '/console/resources' , [
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => 'a_session_console=' . $this -> getRoot ()[ 'session' ],
'x-appwrite-project' => 'console' ,
], [
'type' => 'rules' ,
'value' => $domain ,
]);
$this -> assertEquals ( 204 , $response [ 'headers' ][ 'status-code' ]); // domain available as site is deleted
}
public function testVariables () : void
{
$site = $this -> createSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$siteId = $site [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEquals ( 201 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Site' , $site [ 'body' ][ 'name' ]);
$variable = $this -> createVariable ( $siteId , [
'key' => 'siteKey1' ,
'value' => 'siteValue1' ,
'secret' => false ,
]);
$this -> assertEquals ( 201 , $variable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $variable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey1' , $variable [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'siteValue1' , $variable [ 'body' ][ 'value' ]);
$this -> assertEquals ( false , $variable [ 'body' ][ 'secret' ]);
$variable2 = $this -> createVariable ( $siteId , [
'key' => 'siteKey2' ,
'value' => 'siteValue2' ,
'secret' => false ,
]);
$this -> assertEquals ( 201 , $variable2 [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $variable2 [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey2' , $variable2 [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'siteValue2' , $variable2 [ 'body' ][ 'value' ]);
$this -> assertEquals ( false , $variable2 [ 'body' ][ 'secret' ]);
$secretVariable = $this -> createVariable ( $siteId , [
'key' => 'siteKey3' ,
'value' => 'siteValue3' ,
'secret' => true ,
]);
$this -> assertEquals ( 201 , $secretVariable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey3' , $secretVariable [ 'body' ][ 'key' ]);
$this -> assertEquals ( '' , $secretVariable [ 'body' ][ 'value' ]);
$this -> assertEquals ( true , $secretVariable [ 'body' ][ 'secret' ]);
$variable = $this -> getVariable ( $siteId , $variable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $variable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $variable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey1' , $variable [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'siteValue1' , $variable [ 'body' ][ 'value' ]);
$this -> assertEquals ( false , $variable [ 'body' ][ 'secret' ]);
$secretVariable = $this -> getVariable ( $siteId , $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $secretVariable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey3' , $secretVariable [ 'body' ][ 'key' ]);
$this -> assertEquals ( '' , $secretVariable [ 'body' ][ 'value' ]);
$this -> assertEquals ( true , $secretVariable [ 'body' ][ 'secret' ]);
$variable = $this -> updateVariable ( $siteId , $variable [ 'body' ][ '$id' ], [
'key' => 'siteKey1Updated' ,
'value' => 'siteValue1Updated' ,
]);
$this -> assertEquals ( 200 , $variable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $variable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey1Updated' , $variable [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'siteValue1Updated' , $variable [ 'body' ][ 'value' ]);
$this -> assertEquals ( false , $variable [ 'body' ][ 'secret' ]);
$variable = $this -> updateVariable ( $siteId , $variable [ 'body' ][ '$id' ], [
'key' => 'siteKey1Updated' ,
'secret' => true ,
]);
$this -> assertEquals ( 200 , $variable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $variable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey1Updated' , $variable [ 'body' ][ 'key' ]);
$this -> assertEquals ( '' , $variable [ 'body' ][ 'value' ]);
$this -> assertEquals ( true , $variable [ 'body' ][ 'secret' ]);
$secretVariable = $this -> updateVariable ( $siteId , $secretVariable [ 'body' ][ '$id' ], [
'key' => 'siteKey3' ,
'value' => 'siteValue3Updated' ,
]);
$this -> assertEquals ( 200 , $secretVariable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey3' , $secretVariable [ 'body' ][ 'key' ]);
$this -> assertEquals ( '' , $secretVariable [ 'body' ][ 'value' ]);
$this -> assertEquals ( true , $secretVariable [ 'body' ][ 'secret' ]);
$response = $this -> updateVariable ( $siteId , $secretVariable [ 'body' ][ '$id' ], [
'key' => 'siteKey3' ,
'secret' => false ,
]);
$this -> assertEquals ( 400 , $response [ 'headers' ][ 'status-code' ]);
$secretVariable = $this -> getVariable ( $siteId , $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $secretVariable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'siteKey3' , $secretVariable [ 'body' ][ 'key' ]);
$this -> assertEquals ( '' , $secretVariable [ 'body' ][ 'value' ]);
$this -> assertEquals ( true , $secretVariable [ 'body' ][ 'secret' ]);
$variables = $this -> listVariables ( $siteId );
$this -> assertEquals ( 200 , $variables [ 'headers' ][ 'status-code' ]);
$this -> assertCount ( 3 , $variables [ 'body' ][ 'variables' ]);
$response = $this -> deleteVariable ( $siteId , $variable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 204 , $response [ 'headers' ][ 'status-code' ]);
$response = $this -> deleteVariable ( $siteId , $variable2 [ 'body' ][ '$id' ]);
$this -> assertEquals ( 204 , $response [ 'headers' ][ 'status-code' ]);
$response = $this -> deleteVariable ( $siteId , $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 204 , $response [ 'headers' ][ 'status-code' ]);
$variables = $this -> listVariables ( $siteId );
$this -> assertEquals ( 200 , $variables [ 'headers' ][ 'status-code' ]);
$this -> assertCount ( 0 , $variables [ 'body' ][ 'variables' ]);
$this -> cleanupSite ( $siteId );
}
// This is first Sites test with Proxy
// If this fails, it may not be related to variables; but Router flow failing
public function testVariablesE2E () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro site' ,
'framework' => 'astro' ,
'adapter' => 'ssr' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$secretVariable = $this -> createVariable ( $siteId , [
'key' => 'name' ,
'value' => 'Appwrite' ,
]);
$this -> assertEquals ( 201 , $secretVariable [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $secretVariable [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'name' , $secretVariable [ 'body' ][ 'key' ]);
$this -> assertEquals ( '' , $secretVariable [ 'body' ][ 'value' ]);
$this -> assertEquals ( true , $secretVariable [ 'body' ][ 'secret' ]);
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> getSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Env variable is Appwrite " , $response [ 'body' ]);
$this -> assertStringNotContainsString ( " Variable not found " , $response [ 'body' ]);
2025-03-17 16:35:03 +00:00
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$totalSize = $deployment [ 'body' ][ 'sourceSize' ] + $deployment [ 'body' ][ 'buildSize' ];
$this -> assertEquals ( $totalSize , $deployment [ 'body' ][ 'totalSize' ]);
2025-03-18 11:44:21 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ 'deploymentId' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ 'deploymentCreatedAt' ]);
$this -> assertEquals ( $deployment [ 'body' ][ '$id' ], $site [ 'body' ][ 'deploymentId' ]);
$this -> assertEquals ( $deployment [ 'body' ][ '$createdAt' ], $site [ 'body' ][ 'deploymentCreatedAt' ]);
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testAdapterDetectionAstroSSR () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro SSR site' ,
'framework' => 'astro' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'adapter' , $site [ 'body' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'adapter' ]);
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
2025-08-16 12:14:38 +00:00
$this -> assertEventually ( function () use ( $siteId , & $site ) {
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 'ssr' , $site [ 'body' ][ 'adapter' ]);
});
2025-03-08 22:33:37 +00:00
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> cleanupSite ( $siteId );
}
2025-08-16 17:03:21 +00:00
#[Retry(count: 3)]
2025-03-08 22:33:37 +00:00
public function testAdapterDetectionAstroStatic () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro static site' ,
'framework' => 'astro' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'adapter' , $site [ 'body' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'adapter' ]);
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro-static' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'static' , $site [ 'body' ][ 'adapter' ]);
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> cleanupSite ( $siteId );
}
public function testAdapterDetectionStatic () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Static site' ,
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => '' ,
'buildCommand' => '' ,
'installCommand' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'adapter' , $site [ 'body' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'adapter' ]);
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deploymentId = $this -> setupDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'static' , $site [ 'body' ][ 'adapter' ]);
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> cleanupSite ( $siteId );
}
2025-03-08 15:50:39 +00:00
public function testAdapterDetectionStaticSPA () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Static site' ,
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => '' ,
'buildCommand' => '' ,
'installCommand' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'adapter' , $site [ 'body' ]);
$this -> assertArrayHasKey ( 'fallbackFile' , $site [ 'body' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'adapter' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'fallbackFile' ]);
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static-single-file' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'static' , $site [ 'body' ][ 'adapter' ]);
$this -> assertEquals ( 'main.html' , $site [ 'body' ][ 'fallbackFile' ]);
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'Main page' , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/something' );
2025-03-05 17:05:49 +00:00
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-03-08 15:50:39 +00:00
$this -> assertStringContainsString ( 'Main page' , $response [ 'body' ]);
2025-03-05 17:05:49 +00:00
$this -> cleanupSite ( $siteId );
}
2025-03-24 14:34:02 +00:00
public function testSettingsForRollback () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Static site' ,
2025-03-25 13:01:14 +00:00
'framework' => 'astro' ,
2025-03-24 14:34:02 +00:00
'buildRuntime' => 'node-22' ,
2025-03-25 13:01:14 +00:00
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
2025-03-24 14:34:02 +00:00
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'adapter' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'fallbackFile' ]);
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deploymentId1 = $this -> setupDeployment ( $siteId , [
2025-03-25 13:01:14 +00:00
'code' => $this -> packageSite ( 'astro-static' ),
2025-03-24 14:34:02 +00:00
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId1 );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'static' , $site [ 'body' ][ 'adapter' ]);
2025-03-25 13:01:14 +00:00
$this -> assertEquals ( 'index.html' , $site [ 'body' ][ 'fallbackFile' ]);
2025-03-24 14:34:02 +00:00
$site = $this -> updateSite ([
2025-03-25 13:01:14 +00:00
'name' => 'SSR site' ,
'framework' => 'astro' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
2025-03-24 14:34:02 +00:00
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
2025-03-25 13:01:14 +00:00
'adapter' => 'ssr' ,
'fallbackFile' => '' ,
'$id' => $siteId ,
2025-03-24 14:34:02 +00:00
]);
2025-03-25 13:01:14 +00:00
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'ssr' , $site [ 'body' ][ 'adapter' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'fallbackFile' ]);
2025-03-24 14:34:02 +00:00
$deploymentId2 = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId2 );
2025-03-25 13:01:14 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'ssr' , $site [ 'body' ][ 'adapter' ]);
2025-03-24 14:34:02 +00:00
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
2025-03-25 13:01:14 +00:00
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro SSR " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/not-found' );
2025-03-24 14:34:02 +00:00
$this -> assertEquals ( 404 , $response [ 'headers' ][ 'status-code' ]);
$response = $this -> updateSiteDeployment ( $siteId , $deploymentId1 );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-03-25 13:01:14 +00:00
$this -> assertStringContainsString ( " Astro static " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/not-found' );
2025-03-05 17:05:49 +00:00
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-03-25 13:01:14 +00:00
$this -> assertStringContainsString ( " Astro static " , $response [ 'body' ]);
2025-03-05 17:05:49 +00:00
$this -> cleanupSite ( $siteId );
}
2025-01-01 20:51:30 +00:00
public function testListSites () : void
{
/**
* Test for SUCCESS
*/
$siteId = $this -> setupSite ([
2025-03-07 14:46:49 +00:00
'buildRuntime' => 'node-22' ,
2025-03-08 17:39:13 +00:00
'fallbackFile' => '' ,
2025-01-06 21:03:32 +00:00
'framework' => 'other' ,
2025-10-29 09:43:33 +00:00
'name' => 'Test List Sites' ,
2025-01-06 21:03:32 +00:00
'outputDirectory' => './' ,
2025-01-01 20:51:30 +00:00
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
2025-01-06 21:03:32 +00:00
'siteId' => ID :: unique ()
2025-01-01 20:51:30 +00:00
]);
2025-10-29 09:41:42 +00:00
$sites = $this -> listSites ([
2025-10-29 09:43:33 +00:00
'search' => 'Test List Sites' ,
2025-10-29 09:41:42 +00:00
]);
2025-01-01 20:51:30 +00:00
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites [ 'body' ][ 'sites' ]);
2025-10-29 09:49:16 +00:00
$this -> assertEquals ( $sites [ 'body' ][ 'sites' ][ 0 ][ 'name' ], 'Test List Sites' );
2025-01-01 20:51:30 +00:00
// Test pagination limit
$sites = $this -> listSites ([
'queries' => [
Query :: limit ( 1 ) -> toString (),
],
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites [ 'body' ][ 'sites' ]);
// Test pagination offset
$sites = $this -> listSites ([
'queries' => [
Query :: offset ( 1 ) -> toString (),
],
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 0 , $sites [ 'body' ][ 'sites' ]);
// Test filter enabled
$sites = $this -> listSites ([
'queries' => [
Query :: equal ( 'enabled' , [ true ]) -> toString (),
],
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites [ 'body' ][ 'sites' ]);
// Test filter disabled
$sites = $this -> listSites ([
'queries' => [
Query :: equal ( 'enabled' , [ false ]) -> toString (),
],
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 0 , $sites [ 'body' ][ 'sites' ]);
// Test search name
$sites = $this -> listSites ([
2025-10-29 09:49:16 +00:00
'search' => 'Test List Sites'
2025-01-01 20:51:30 +00:00
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites [ 'body' ][ 'sites' ]);
$this -> assertEquals ( $sites [ 'body' ][ 'sites' ][ 0 ][ '$id' ], $siteId );
// Test search framework
$sites = $this -> listSites ([
2025-01-06 21:03:32 +00:00
'search' => 'other'
2025-01-01 20:51:30 +00:00
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites [ 'body' ][ 'sites' ]);
$this -> assertEquals ( $sites [ 'body' ][ 'sites' ][ 0 ][ '$id' ], $siteId );
/**
* Test pagination
*/
$siteId2 = $this -> setupSite ([
2025-03-07 14:46:49 +00:00
'buildRuntime' => 'node-22' ,
2025-03-08 17:39:13 +00:00
'fallbackFile' => '' ,
2025-01-06 21:03:32 +00:00
'framework' => 'other' ,
2025-10-29 09:49:16 +00:00
'name' => 'Test List Sites 2' ,
2025-01-06 21:03:32 +00:00
'outputDirectory' => './' ,
2025-01-01 20:51:30 +00:00
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
2025-01-06 21:03:32 +00:00
'siteId' => ID :: unique ()
2025-01-01 20:51:30 +00:00
]);
2025-10-29 09:49:16 +00:00
$sites = $this -> listSites ([
'search' => 'Test List Sites' ,
]);
2025-01-01 20:51:30 +00:00
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( $sites [ 'body' ][ 'total' ], 2 );
$this -> assertIsArray ( $sites [ 'body' ][ 'sites' ]);
$this -> assertCount ( 2 , $sites [ 'body' ][ 'sites' ]);
2025-10-29 09:49:16 +00:00
$this -> assertEquals ( $sites [ 'body' ][ 'sites' ][ 0 ][ 'name' ], 'Test List Sites' );
$this -> assertEquals ( $sites [ 'body' ][ 'sites' ][ 1 ][ 'name' ], 'Test List Sites 2' );
2025-01-01 20:51:30 +00:00
$sites1 = $this -> listSites ([
'queries' => [
Query :: cursorAfter ( new Document ([ '$id' => $sites [ 'body' ][ 'sites' ][ 0 ][ '$id' ]])) -> toString (),
],
]);
$this -> assertEquals ( $sites1 [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites1 [ 'body' ][ 'sites' ]);
2025-10-29 09:49:16 +00:00
$this -> assertEquals ( $sites1 [ 'body' ][ 'sites' ][ 0 ][ 'name' ], 'Test List Sites 2' );
2025-01-01 20:51:30 +00:00
$sites2 = $this -> listSites ([
'queries' => [
Query :: cursorBefore ( new Document ([ '$id' => $sites [ 'body' ][ 'sites' ][ 1 ][ '$id' ]])) -> toString (),
],
]);
$this -> assertEquals ( $sites2 [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $sites2 [ 'body' ][ 'sites' ]);
2025-10-29 09:49:16 +00:00
$this -> assertEquals ( $sites2 [ 'body' ][ 'sites' ][ 0 ][ 'name' ], 'Test List Sites' );
2025-01-01 20:51:30 +00:00
/**
* Test for FAILURE
*/
$sites = $this -> listSites ([
'queries' => [
Query :: cursorAfter ( new Document ([ '$id' => 'unknown' ])) -> toString (),
],
]);
$this -> assertEquals ( $sites [ 'headers' ][ 'status-code' ], 400 );
$this -> cleanupSite ( $siteId );
$this -> cleanupSite ( $siteId2 );
}
2025-03-08 22:33:37 +00:00
public function testGetSite () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
/**
* Test for SUCCESS
*/
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( $site [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( $site [ 'body' ][ 'name' ], 'Test Site' );
/**
* Test for FAILURE
*/
$site = $this -> getSite ( 'x' );
$this -> assertEquals ( $site [ 'headers' ][ 'status-code' ], 404 );
$this -> cleanupSite ( $siteId );
}
public function testUpdateSite () : void
{
$site = $this -> createSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$siteId = $site [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEquals ( 201 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Site' , $site [ 'body' ][ 'name' ]);
$site = $this -> updateSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site Updated' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'$id' => $siteId ,
'installCommand' => 'npm install'
]);
$dateValidator = new DatetimeValidator ();
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Site Updated' , $site [ 'body' ][ 'name' ]);
$this -> assertEquals ( true , $dateValidator -> isValid ( $site [ 'body' ][ '$createdAt' ]));
$this -> assertEquals ( true , $dateValidator -> isValid ( $site [ 'body' ][ '$updatedAt' ]));
$this -> assertEquals ( 'npm install' , $site [ 'body' ][ 'installCommand' ]);
$this -> cleanupSite ( $siteId );
}
// public function testCreateDeploymentFromCLI() {
// // TODO: Implement testCreateDeploymentFromCLI() later
// }
public function testCreateDeployment ()
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
'siteId' => $siteId ,
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => true ,
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
2025-03-13 10:19:55 +00:00
$this -> assertEquals ( 'waiting' , $deployment [ 'body' ][ 'status' ]);
2025-03-08 22:33:37 +00:00
$this -> assertEquals ( true , ( new DatetimeValidator ()) -> isValid ( $deployment [ 'body' ][ '$createdAt' ]));
$deploymentIdActive = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEventually ( function () use ( $siteId , $deploymentIdActive ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentIdActive );
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ]);
}, 50000 , 500 );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
$deploymentIdInactive = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEventually ( function () use ( $siteId , $deploymentIdInactive ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentIdInactive );
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ]);
}, 50000 , 500 );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentIdActive , $site [ 'body' ][ 'deploymentId' ]);
$this -> assertNotEquals ( $deploymentIdInactive , $site [ 'body' ][ 'deploymentId' ]);
$this -> cleanupDeployment ( $siteId , $deploymentIdActive );
$this -> cleanupDeployment ( $siteId , $deploymentIdInactive );
$this -> cleanupSite ( $siteId );
}
#[Retry(count: 3)]
public function testCancelDeploymentBuild () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( true , ( new DatetimeValidator ()) -> isValid ( $deployment [ 'body' ][ '$createdAt' ]));
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'building' , $deployment [ 'body' ][ 'status' ]);
}, 100000 , 250 );
$deployment = $this -> cancelDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'canceled' , $deployment [ 'body' ][ 'status' ]);
2025-07-10 13:52:29 +00:00
// Ensures worker got eventually aware of cancellation and reacted properly
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'Build has been canceled.' , $deployment [ 'body' ][ 'buildLogs' ]);
});
2025-03-08 22:33:37 +00:00
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'canceled' , $deployment [ 'body' ][ 'status' ]);
$this -> cleanupDeployment ( $siteId , $deploymentId );
$this -> cleanupSite ( $siteId );
}
public function testUpdateDeployment () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ]);
}, 50000 , 500 );
/**
* Test for SUCCESS
*/
$dateValidator = new DatetimeValidator ();
$response = $this -> updateSiteDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( true , $dateValidator -> isValid ( $response [ 'body' ][ '$createdAt' ]));
$this -> assertEquals ( true , $dateValidator -> isValid ( $response [ 'body' ][ '$updatedAt' ]));
$this -> assertEquals ( $deploymentId , $response [ 'body' ][ 'deploymentId' ]);
$this -> cleanupDeployment ( $siteId , $deploymentId );
$this -> cleanupSite ( $siteId );
}
public function testListDeployments () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$deploymentIdActive = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
$deploymentIdInactive = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$deployments = $this -> listDeployments ( $siteId );
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( $deployments [ 'body' ][ 'total' ], 2 );
$this -> assertIsArray ( $deployments [ 'body' ][ 'deployments' ]);
$this -> assertCount ( 2 , $deployments [ 'body' ][ 'deployments' ]);
$this -> assertArrayHasKey ( 'sourceSize' , $deployments [ 'body' ][ 'deployments' ][ 0 ]);
$this -> assertArrayHasKey ( 'buildSize' , $deployments [ 'body' ][ 'deployments' ][ 0 ]);
$deployments = $this -> listDeployments ( $siteId , [
'queries' => [
Query :: limit ( 1 ) -> toString (),
],
]);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $deployments [ 'body' ][ 'deployments' ]);
2025-08-26 13:29:11 +00:00
$deployments = $this -> listDeployments ( $siteId , [
'queries' => [
Query :: select ([ 'status' ]) -> toString (),
],
]);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertArrayHasKey ( 'status' , $deployments [ 'body' ][ 'deployments' ][ 0 ]);
$this -> assertArrayHasKey ( 'status' , $deployments [ 'body' ][ 'deployments' ][ 1 ]);
$this -> assertArrayNotHasKey ( 'sourceSize' , $deployments [ 'body' ][ 'deployments' ][ 0 ]);
$this -> assertArrayNotHasKey ( 'sourceSize' , $deployments [ 'body' ][ 'deployments' ][ 1 ]);
2025-08-26 13:51:24 +00:00
// Extra select query check, for attribute not allowed by filter queries
$deployments = $this -> listDeployments ( $siteId , [
'queries' => [
Query :: select ([ 'buildLogs' ]) -> toString (),
],
]);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertArrayHasKey ( 'buildLogs' , $deployments [ 'body' ][ 'deployments' ][ 0 ]);
$this -> assertArrayHasKey ( 'buildLogs' , $deployments [ 'body' ][ 'deployments' ][ 1 ]);
$this -> assertArrayNotHasKey ( 'sourceSize' , $deployments [ 'body' ][ 'deployments' ][ 0 ]);
$this -> assertArrayNotHasKey ( 'sourceSize' , $deployments [ 'body' ][ 'deployments' ][ 1 ]);
2025-03-08 22:33:37 +00:00
$deployments = $this -> listDeployments ( $siteId , [
'queries' => [
Query :: offset ( 1 ) -> toString (),
],
]);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertCount ( 1 , $deployments [ 'body' ][ 'deployments' ]);
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: equal ( 'type' , [ 'manual' ]) -> toString (),
],
]
);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( 2 , $deployments [ 'body' ][ 'total' ]);
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: equal ( 'type' , [ 'vcs' ]) -> toString (),
],
]
);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( 0 , $deployments [ 'body' ][ 'total' ]);
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: equal ( 'type' , [ 'invalid-string' ]) -> toString (),
],
]
);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( 0 , $deployments [ 'body' ][ 'total' ]);
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: greaterThan ( 'sourceSize' , 10000 ) -> toString (),
],
]
);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( 0 , $deployments [ 'body' ][ 'total' ]);
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: greaterThan ( 'sourceSize' , 0 ) -> toString (),
],
]
);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( 2 , $deployments [ 'body' ][ 'total' ]);
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: greaterThan ( 'sourceSize' , - 100 ) -> toString (),
],
]
);
$this -> assertEquals ( $deployments [ 'headers' ][ 'status-code' ], 200 );
$this -> assertEquals ( 2 , $deployments [ 'body' ][ 'total' ]);
/**
* Ensure size output and size filters work exactly .
* Prevents buildSize being counted towards deployment size
*/
$deployments = $this -> listDeployments (
$siteId ,
[
Query :: limit ( 1 ) -> toString (),
]
);
$this -> assertEquals ( 200 , $deployments [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThanOrEqual ( 1 , $deployments [ 'body' ][ 'total' ]);
$this -> assertNotEmpty ( $deployments [ 'body' ][ 'deployments' ][ 0 ][ '$id' ]);
$this -> assertNotEmpty ( $deployments [ 'body' ][ 'deployments' ][ 0 ][ 'sourceSize' ]);
$deploymentId = $deployments [ 'body' ][ 'deployments' ][ 0 ][ '$id' ];
$deploymentSize = $deployments [ 'body' ][ 'deployments' ][ 0 ][ 'sourceSize' ];
$deployments = $this -> listDeployments (
$siteId ,
[
'queries' => [
Query :: equal ( 'sourceSize' , [ $deploymentSize ]) -> toString (),
],
]
);
$this -> assertEquals ( 200 , $deployments [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployments [ 'body' ][ 'total' ]);
$matchingDeployment = array_filter (
$deployments [ 'body' ][ 'deployments' ],
fn ( $deployment ) => $deployment [ '$id' ] === $deploymentId
);
$this -> assertNotEmpty ( $matchingDeployment , " Deployment with ID { $deploymentId } not found " );
if ( ! empty ( $matchingDeployment )) {
$deployment = reset ( $matchingDeployment );
$this -> assertEquals ( $deploymentSize , $deployment [ 'sourceSize' ]);
}
$this -> cleanupDeployment ( $siteId , $deploymentIdActive );
$this -> cleanupDeployment ( $siteId , $deploymentIdInactive );
$this -> cleanupSite ( $siteId );
}
public function testGetDeployment () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ]);
}, 50000 , 500 );
/**
* Test for SUCCESS
*/
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'buildDuration' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ 'status' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ 'buildLogs' ]);
$this -> assertArrayHasKey ( 'sourceSize' , $deployment [ 'body' ]);
$this -> assertArrayHasKey ( 'buildSize' , $deployment [ 'body' ]);
/**
* Test for FAILURE
*/
$deployment = $this -> getDeployment ( $siteId , 'x' );
$this -> assertEquals ( $deployment [ 'headers' ][ 'status-code' ], 404 );
$this -> cleanupDeployment ( $siteId , $deploymentId );
$this -> cleanupSite ( $siteId );
}
public function testUpdateSpecs () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
/**
* Test for SUCCESS
*/
// Change the function specs
$site = $this -> updateSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'$id' => $siteId ,
'specification' => Specification :: S_1VCPU_1GB ,
]);
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( Specification :: S_1VCPU_1GB , $site [ 'body' ][ 'specification' ]);
// Change the specs to 1vcpu 512mb
$site = $this -> updateSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'$id' => $siteId ,
'specification' => Specification :: S_1VCPU_512MB ,
]);
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$this -> assertEquals ( Specification :: S_1VCPU_512MB , $site [ 'body' ][ 'specification' ]);
/**
* Test for FAILURE
*/
$site = $this -> updateSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'$id' => $siteId ,
'specification' => 's-2vcpu-512mb' , // Invalid specification
]);
$this -> assertEquals ( 400 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertStringStartsWith ( 'Invalid `specification` param: Specification must be one of:' , $site [ 'body' ][ 'message' ]);
$this -> cleanupSite ( $siteId );
}
public function testDeleteDeployment () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => 'false'
]);
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ]);
}, 50000 , 500 );
/**
* Test for SUCCESS
*/
$deployment = $this -> client -> call ( Client :: METHOD_DELETE , '/sites/' . $siteId . '/deployments/' . $deploymentId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$this -> assertEquals ( 204 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEmpty ( $deployment [ 'body' ]);
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 404 , $deployment [ 'headers' ][ 'status-code' ]);
}
public function testDeleteSite () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotNull ( $siteId );
$site = $this -> deleteSite ( $siteId );
$this -> assertEquals ( 204 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEmpty ( $site [ 'body' ]);
$function = $this -> getSite ( $siteId );
$this -> assertEquals ( 404 , $function [ 'headers' ][ 'status-code' ]);
}
public function testGetFrameworks () : void
{
$frameworks = $this -> client -> call ( Client :: METHOD_GET , '/sites/frameworks' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2025-04-01 13:29:07 +00:00
]));
2025-03-08 22:33:37 +00:00
$this -> assertEquals ( 200 , $frameworks [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $frameworks [ 'body' ][ 'total' ]);
$framework = $frameworks [ 'body' ][ 'frameworks' ][ 0 ];
$this -> assertArrayHasKey ( 'name' , $framework );
$this -> assertArrayHasKey ( 'key' , $framework );
$this -> assertArrayHasKey ( 'buildRuntime' , $framework );
$this -> assertArrayHasKey ( 'runtimes' , $framework );
$this -> assertArrayHasKey ( 'adapters' , $framework );
2025-03-17 15:02:16 +00:00
$this -> assertIsArray ( $framework [ 'adapters' ]);
$this -> assertArrayHasKey ( 'key' , $framework [ 'adapters' ][ 0 ]);
$this -> assertArrayHasKey ( 'installCommand' , $framework [ 'adapters' ][ 0 ]);
$this -> assertArrayHasKey ( 'buildCommand' , $framework [ 'adapters' ][ 0 ]);
$this -> assertArrayHasKey ( 'outputDirectory' , $framework [ 'adapters' ][ 0 ]);
2025-03-08 22:33:37 +00:00
}
public function testSiteStatic () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Non-SPA site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static-spa' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> setupSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Index page " , $response [ 'body' ]);
2025-08-26 13:04:43 +00:00
$this -> assertArrayHasKey ( 'x-appwrite-log-id' , $response [ 'headers' ]);
$this -> assertNotEmpty ( $response [ 'headers' ][ 'x-appwrite-log-id' ]);
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Contact page " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/non-existing' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
]));
$this -> assertEquals ( 404 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Page not found " , $response [ 'body' ]); // Title
$this -> assertStringContainsString ( " Go to homepage " , $response [ 'body' ]); // Button
2025-05-30 05:56:05 +00:00
$this -> assertStringNotContainsString ( " Powered by " , $response [ 'body' ]); // Brand
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testSiteStaticSPA () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'SPA site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '404.html' ,
]);
$this -> assertNotEmpty ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static-spa' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> setupSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Index page " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Contact page " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/non-existing' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Customized 404 page " , $response [ 'body' ]);
2025-05-29 09:16:25 +00:00
$this -> assertStringNotContainsString ( " Powered by " , $response [ 'body' ]); //brand
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
2025-08-16 17:08:07 +00:00
2025-11-17 10:21:36 +00:00
#[Retry(count: 3)]
2025-03-08 22:33:37 +00:00
public function testSiteTemplate () : void
{
2025-04-25 12:06:09 +00:00
$template = $this -> getTemplate ( 'playground-for-astro' );
2025-03-08 22:33:37 +00:00
$this -> assertEquals ( 200 , $template [ 'headers' ][ 'status-code' ]);
$template = $template [ 'body' ];
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Template site' ,
'framework' => $template [ 'frameworks' ][ 0 ][ 'key' ],
'adapter' => $template [ 'frameworks' ][ 0 ][ 'adapter' ],
'buildRuntime' => $template [ 'frameworks' ][ 0 ][ 'buildRuntime' ],
'outputDirectory' => $template [ 'frameworks' ][ 0 ][ 'outputDirectory' ],
'buildCommand' => $template [ 'frameworks' ][ 0 ][ 'buildCommand' ],
'installCommand' => $template [ 'frameworks' ][ 0 ][ 'installCommand' ],
'fallbackFile' => $template [ 'frameworks' ][ 0 ][ 'fallbackFile' ],
]);
$this -> assertNotEmpty ( $siteId );
$deployment = $this -> createTemplateDeployment ( $siteId , [
'repository' => $template [ 'providerRepositoryId' ],
'owner' => $template [ 'providerOwner' ],
'rootDirectory' => $template [ 'frameworks' ][ 0 ][ 'providerRootDirectory' ],
2025-09-18 07:42:46 +00:00
'type' => 'tag' ,
'reference' => $template [ 'providerVersion' ],
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
$deployment = $this -> getDeployment ( $siteId , $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'totalSize' ]);
$this -> assertEventually ( function () use ( $siteId ) {
$site = $this -> getSite ( $siteId );
$this -> assertNotEmpty ( $site [ 'body' ][ 'deploymentId' ]);
}, 50000 , 500 );
2025-11-27 11:01:14 +00:00
$domain = $this -> setupSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
2025-09-18 07:42:46 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro Blog " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Hello, Astronaut! " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/about' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro Blog " , $response [ 'body' ]);
$this -> assertStringContainsString ( " About Me " , $response [ 'body' ]);
$deployment = $this -> getDeployment ( $siteId , $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$totalSize = $deployment [ 'body' ][ 'sourceSize' ] + $deployment [ 'body' ][ 'buildSize' ];
$this -> assertEquals ( $totalSize , $deployment [ 'body' ][ 'totalSize' ]);
$this -> cleanupSite ( $siteId );
}
public function testCreateSiteFromTemplateBranch ()
{
$template = $this -> getTemplate ( 'playground-for-astro' );
$this -> assertEquals ( 200 , $template [ 'headers' ][ 'status-code' ]);
$template = $template [ 'body' ];
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro Blog - Branch Test' ,
'framework' => $template [ 'frameworks' ][ 0 ][ 'key' ],
'adapter' => $template [ 'frameworks' ][ 0 ][ 'adapter' ],
'buildRuntime' => $template [ 'frameworks' ][ 0 ][ 'buildRuntime' ],
'outputDirectory' => $template [ 'frameworks' ][ 0 ][ 'outputDirectory' ],
'buildCommand' => $template [ 'frameworks' ][ 0 ][ 'buildCommand' ],
'installCommand' => $template [ 'frameworks' ][ 0 ][ 'installCommand' ],
'fallbackFile' => $template [ 'frameworks' ][ 0 ][ 'fallbackFile' ],
]);
$this -> assertNotEmpty ( $siteId );
// Deploy using branch
$deployment = $this -> createTemplateDeployment ( $siteId , [
'repository' => $template [ 'providerRepositoryId' ],
'owner' => $template [ 'providerOwner' ],
'rootDirectory' => $template [ 'frameworks' ][ 0 ][ 'providerRootDirectory' ],
'type' => 'branch' ,
'reference' => 'main' ,
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
$deployment = $this -> getDeployment ( $siteId , $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'totalSize' ]);
$this -> assertEventually ( function () use ( $siteId ) {
$site = $this -> getSite ( $siteId );
$this -> assertNotEmpty ( $site [ 'body' ][ 'deploymentId' ]);
}, 50000 , 500 );
$domain = $this -> setupSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro Blog " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Hello, Astronaut! " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/about' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro Blog " , $response [ 'body' ]);
$this -> assertStringContainsString ( " About Me " , $response [ 'body' ]);
$deployment = $this -> getDeployment ( $siteId , $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$totalSize = $deployment [ 'body' ][ 'sourceSize' ] + $deployment [ 'body' ][ 'buildSize' ];
$this -> assertEquals ( $totalSize , $deployment [ 'body' ][ 'totalSize' ]);
$this -> cleanupSite ( $siteId );
}
public function testCreateSiteFromTemplateCommit ()
{
$template = $this -> getTemplate ( 'playground-for-astro' );
$this -> assertEquals ( 200 , $template [ 'headers' ][ 'status-code' ]);
2025-09-19 12:48:44 +00:00
// Get latest commit using helper function
$latestCommit = $this -> helperGetLatestCommit (
$template [ 'body' ][ 'providerOwner' ],
$template [ 'body' ][ 'providerRepositoryId' ]
);
$this -> assertNotNull ( $latestCommit );
2025-09-18 07:42:46 +00:00
$template = $template [ 'body' ];
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro Blog - Commit Test' ,
'framework' => $template [ 'frameworks' ][ 0 ][ 'key' ],
'adapter' => $template [ 'frameworks' ][ 0 ][ 'adapter' ],
'buildRuntime' => $template [ 'frameworks' ][ 0 ][ 'buildRuntime' ],
'outputDirectory' => $template [ 'frameworks' ][ 0 ][ 'outputDirectory' ],
'buildCommand' => $template [ 'frameworks' ][ 0 ][ 'buildCommand' ],
'installCommand' => $template [ 'frameworks' ][ 0 ][ 'installCommand' ],
'fallbackFile' => $template [ 'frameworks' ][ 0 ][ 'fallbackFile' ],
]);
$this -> assertNotEmpty ( $siteId );
// Deploy using commit
$deployment = $this -> createTemplateDeployment ( $siteId , [
'repository' => $template [ 'providerRepositoryId' ],
'owner' => $template [ 'providerOwner' ],
'rootDirectory' => $template [ 'frameworks' ][ 0 ][ 'providerRootDirectory' ],
'type' => 'commit' ,
'reference' => $latestCommit ,
2025-03-08 22:33:37 +00:00
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
2025-03-17 16:35:03 +00:00
$deployment = $this -> getDeployment ( $siteId , $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'totalSize' ]);
2025-03-08 22:33:37 +00:00
$this -> assertEventually ( function () use ( $siteId ) {
$site = $this -> getSite ( $siteId );
$this -> assertNotEmpty ( $site [ 'body' ][ 'deploymentId' ]);
}, 50000 , 500 );
2025-11-17 10:21:36 +00:00
$domain = $this -> setupSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro Blog " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Hello, Astronaut! " , $response [ 'body' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/about' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Astro Blog " , $response [ 'body' ]);
$this -> assertStringContainsString ( " About Me " , $response [ 'body' ]);
2025-03-17 16:35:03 +00:00
$deployment = $this -> getDeployment ( $siteId , $deployment [ 'body' ][ '$id' ]);
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$totalSize = $deployment [ 'body' ][ 'sourceSize' ] + $deployment [ 'body' ][ 'buildSize' ];
$this -> assertEquals ( $totalSize , $deployment [ 'body' ][ 'totalSize' ]);
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testSiteDomainReclaiming () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Startup site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$subdomain = 'startup' . \uniqid ();
$domain = $this -> setupSiteDomain ( $siteId , $subdomain );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> getSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringNotContainsString ( " This domain is not connected to any Appwrite resource yet " , $response [ 'body' ]);
$site2 = $this -> createSite ([
'siteId' => ID :: unique (),
'name' => 'Startup 2 site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '' ,
]);
$siteId2 = $site2 [ 'body' ][ '$id' ];
2026-01-30 15:20:46 +00:00
$sitesDomain = \explode ( ',' , System :: getEnv ( '_APP_DOMAIN_SITES' , '' ))[ 0 ];
2025-03-08 22:33:37 +00:00
$rule = $this -> client -> call ( Client :: METHOD_POST , '/proxy/rules/site' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2026-01-30 15:20:46 +00:00
'domain' => $subdomain . '.' . $sitesDomain ,
2025-03-08 22:33:37 +00:00
'siteId' => $siteId2 ,
]);
$this -> assertEquals ( 409 , $rule [ 'headers' ][ 'status-code' ]);
$this -> cleanupSite ( $siteId );
$this -> assertEventually ( function () use ( $domain ) {
$rules = $this -> client -> call ( Client :: METHOD_GET , '/proxy/rules' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'queries' => [
Query :: equal ( 'domain' , [ $domain ]) -> toString (),
],
]);
$this -> assertEquals ( 200 , $rules [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 0 , $rules [ 'body' ][ 'total' ]);
}, 50000 , 500 );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
2025-04-09 06:28:54 +00:00
$this -> assertEquals ( 404 , $response [ 'headers' ][ 'status-code' ]);
2025-04-09 17:50:06 +00:00
$this -> assertStringContainsString ( " This page is empty, but you can make it yours. " , $response [ 'body' ]);
2025-03-08 22:33:37 +00:00
$site = $this -> createSite ([
'siteId' => ID :: unique (),
'name' => 'Startup 2 site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '' ,
]);
$this -> assertEquals ( 201 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $site [ 'body' ][ '$id' ]);
$siteId = $site [ 'body' ][ '$id' ];
$domain = $this -> setupSiteDomain ( $siteId , $subdomain );
$this -> assertNotEmpty ( $domain );
$this -> cleanupSite ( $site [ 'body' ][ '$id' ]);
}
public function testSitePreviewBranding () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'A site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
2025-03-13 09:33:28 +00:00
$siteDomain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $siteDomain );
2025-03-08 22:33:37 +00:00
2025-04-07 09:08:06 +00:00
$deploymentDomain = $this -> getDeploymentDomain ( $deploymentId );
$this -> assertNotEmpty ( $deploymentDomain );
2025-03-08 22:33:37 +00:00
$proxyClient = new Client ();
2025-03-13 09:33:28 +00:00
$proxyClient -> setEndpoint ( 'http://' . $siteDomain );
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Hello Appwrite " , $response [ 'body' ]);
$this -> assertStringNotContainsString ( " Preview by " , $response [ 'body' ]);
$contentLength = $response [ 'headers' ][ 'content-length' ];
$proxyClient = new Client ();
2025-04-07 09:08:06 +00:00
$proxyClient -> setEndpoint ( 'http://' . $deploymentDomain );
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( '/console/auth/preview' , $response [ 'headers' ][ 'location' ]);
$jwtObj = new JWT ( System :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , 900 , 0 );
$apiKey = $jwtObj -> encode ([
'projectCheckDisabled' => true ,
'previewAuthDisabled' => true ,
]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false , headers : [
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey ,
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Hello Appwrite " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Preview by " , $response [ 'body' ]);
$this -> assertGreaterThan ( $contentLength , $response [ 'headers' ][ 'content-length' ]);
2025-03-31 16:51:33 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/non-existing-path' , followRedirects : false , headers : [
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey ,
]);
$this -> assertEquals ( 404 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Page not found " , $response [ 'body' ]);
$this -> assertStringNotContainsString ( " Preview by " , $response [ 'body' ]);
$this -> assertGreaterThan ( $contentLength , $response [ 'headers' ][ 'content-length' ]);
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testSiteCors () : void
{
// Create rule together with site
$subdomain = 'startup' . \uniqid ();
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Startup site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => '' ,
'installCommand' => '' ,
'fallbackFile' => '' ,
'subdomain' => $subdomain
]);
$this -> assertNotEmpty ( $siteId );
$this -> setupSiteDomain ( $siteId , $subdomain );
$domain = $this -> getSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$url = 'http://' . $domain ;
$response = $this -> client -> call ( Client :: METHOD_GET , '/account' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'referer' => $url ,
2025-12-07 20:29:45 +00:00
'origin' => $url ,
2025-03-08 22:33:37 +00:00
]));
$this -> assertEquals ( $url , $response [ 'headers' ][ 'access-control-allow-origin' ]);
$response = $this -> client -> call ( Client :: METHOD_GET , '/account' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => 'unknown' ,
'referer' => $url ,
2025-12-07 20:29:45 +00:00
'origin' => $url ,
2025-03-08 22:33:37 +00:00
]));
2025-12-07 20:29:45 +00:00
$this -> assertArrayNotHasKey ( 'access-control-allow-origin' , $response [ 'headers' ]);
2025-03-08 22:33:37 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/account' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'referer' => 'http://unknown.com' ,
'origin' => 'http://unknown.com'
]));
2025-12-07 20:29:45 +00:00
$this -> assertArrayNotHasKey ( 'access-control-allow-origin' , $response [ 'headers' ]);
2025-03-08 22:33:37 +00:00
}
public function testSiteDownload () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'fallbackFile' => '' ,
'framework' => 'other' ,
'name' => 'Test Site' ,
'adapter' => 'static' ,
'outputDirectory' => './' ,
'providerBranch' => 'main' ,
'providerRootDirectory' => './' ,
'siteId' => ID :: unique ()
]);
$deploymentId = $this -> setupDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-08 22:33:37 +00:00
'activate' => true
]);
$this -> assertNotEmpty ( $deploymentId );
$response = $this -> getDeploymentDownload ( $siteId , $deploymentId , 'source' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'application/gzip' , $response [ 'headers' ][ 'content-type' ]);
$this -> assertGreaterThan ( 0 , $response [ 'headers' ][ 'content-length' ]);
$this -> assertGreaterThan ( 0 , \strlen ( $response [ 'body' ]));
$deploymentMd5 = \md5 ( $response [ 'body' ]);
$response = $this -> getDeploymentDownload ( $siteId , $deploymentId , 'output' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'application/gzip' , $response [ 'headers' ][ 'content-type' ]);
$this -> assertGreaterThan ( 0 , $response [ 'headers' ][ 'content-length' ]);
$this -> assertGreaterThan ( 0 , \strlen ( $response [ 'body' ]));
$buildMd5 = \md5 ( $response [ 'body' ]);
$this -> assertNotEquals ( $deploymentMd5 , $buildMd5 );
$this -> cleanupSite ( $siteId );
}
public function testSSRLogs () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'SSR site' ,
'framework' => 'astro' ,
'adapter' => 'ssr' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> getSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/logs-inline' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Inline logs printed. " , $response [ 'body' ]);
$logs = $this -> listLogs ( $siteId , [
Query :: orderDesc ( '$createdAt' ) -> toString (),
Query :: limit ( 1 ) -> toString (),
]);
$this -> assertEquals ( 200 , $logs [ 'headers' ][ 'status-code' ]);
2025-08-22 09:11:37 +00:00
$this -> assertStringContainsString ( $deploymentId , $logs [ 'body' ][ 'executions' ][ 0 ][ 'deploymentId' ]);
2025-03-08 22:33:37 +00:00
$this -> assertStringContainsString ( " GET " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestMethod' ]);
$this -> assertStringContainsString ( " /logs-inline " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestPath' ]);
$this -> assertStringContainsString ( " Log1 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertStringContainsString ( " Log2 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertStringContainsString ( " Error1 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
$this -> assertStringContainsString ( " Error2 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
$log1Id = $logs [ 'body' ][ 'executions' ][ 0 ][ '$id' ];
$this -> assertNotEmpty ( $log1Id );
2025-08-22 09:16:18 +00:00
2025-08-22 09:11:37 +00:00
$logs = $this -> listLogs ( $siteId , [
Query :: orderDesc ( '$createdAt' ) -> toString (),
Query :: limit ( 1 ) -> toString (),
Query :: equal ( 'deploymentId' , [ $deploymentId ]) -> toString ()
]);
$this -> assertEquals ( 200 , $logs [ 'headers' ][ 'status-code' ]);
2025-08-22 09:31:14 +00:00
$this -> assertGreaterThanOrEqual ( 1 , $logs [ 'body' ][ 'total' ]);
2025-08-22 09:11:37 +00:00
$this -> assertCount ( 1 , $logs [ 'body' ][ 'executions' ]);
2025-08-22 09:16:18 +00:00
2025-08-22 09:11:37 +00:00
$logs = $this -> listLogs ( $siteId , [
Query :: orderDesc ( '$createdAt' ) -> toString (),
Query :: limit ( 1 ) -> toString (),
Query :: equal ( 'deploymentId' , [ 'some-random-id' ]) -> toString ()
]);
$this -> assertEquals ( 200 , $logs [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 0 , $logs [ 'body' ][ 'total' ]);
$this -> assertCount ( 0 , $logs [ 'body' ][ 'executions' ]);
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/logs-action' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Action logs printed. " , $response [ 'body' ]);
$logs = $this -> listLogs ( $siteId , [
Query :: orderDesc ( '$createdAt' ) -> toString (),
Query :: limit ( 1 ) -> toString (),
]);
$this -> assertEquals ( 200 , $logs [ 'headers' ][ 'status-code' ]);
2025-08-22 09:11:37 +00:00
$this -> assertStringContainsString ( $deploymentId , $logs [ 'body' ][ 'executions' ][ 0 ][ 'deploymentId' ]);
2025-03-08 22:33:37 +00:00
$this -> assertStringContainsString ( " GET " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestMethod' ]);
$this -> assertStringContainsString ( " /logs-action " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestPath' ]);
$this -> assertStringContainsString ( " Log1 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertStringContainsString ( " Log2 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertStringContainsString ( " Error1 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
$this -> assertStringContainsString ( " Error2 " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
$log2Id = $logs [ 'body' ][ 'executions' ][ 0 ][ '$id' ];
$this -> assertNotEmpty ( $log2Id );
$this -> assertNotEquals ( $log1Id , $log2Id );
2025-03-25 13:44:14 +00:00
$site = $this -> updateSite (
[
'$id' => $siteId ,
'name' => 'SSR site' ,
'framework' => 'astro' ,
'adapter' => 'ssr' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
'fallbackFile' => '' ,
'logging' => false // set logging to false
]
);
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/logs-inline' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Inline logs printed. " , $response [ 'body' ]);
$logs = $this -> listLogs ( $siteId , [
Query :: orderDesc ( '$createdAt' ) -> toString (),
Query :: limit ( 1 ) -> toString (),
]);
$this -> assertEquals ( 200 , $logs [ 'headers' ][ 'status-code' ]);
2025-03-25 14:32:29 +00:00
$this -> assertEquals ( " GET " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestMethod' ]);
$this -> assertEquals ( " /logs-inline " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestPath' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
2025-03-25 13:44:14 +00:00
$log1Id = $logs [ 'body' ][ 'executions' ][ 0 ][ '$id' ];
$this -> assertNotEmpty ( $log1Id );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/logs-action' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Action logs printed. " , $response [ 'body' ]);
$logs = $this -> listLogs ( $siteId , [
Query :: orderDesc ( '$createdAt' ) -> toString (),
Query :: limit ( 1 ) -> toString (),
]);
$this -> assertEquals ( 200 , $logs [ 'headers' ][ 'status-code' ]);
2025-03-25 14:32:29 +00:00
$this -> assertEquals ( " GET " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestMethod' ]);
$this -> assertEquals ( " /logs-action " , $logs [ 'body' ][ 'executions' ][ 0 ][ 'requestPath' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'logs' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
$this -> assertEmpty ( $logs [ 'body' ][ 'executions' ][ 0 ][ 'errors' ]);
2025-03-25 13:44:14 +00:00
$log2Id = $logs [ 'body' ][ 'executions' ][ 0 ][ '$id' ];
$this -> assertNotEmpty ( $log2Id );
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testDuplicateDeployment () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'framework' => 'other' ,
'name' => 'Duplicate deployment Site' ,
'adapter' => 'static' ,
'fallbackFile' => '404.html' ,
'siteId' => ID :: unique ()
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$deploymentId1 = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static-spa' ),
'activate' => true
]);
$this -> assertNotEmpty ( $deploymentId1 );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/not-found' );
$this -> assertStringContainsString ( " Customized 404 page " , $response [ 'body' ]);
$site = $this -> updateSite ([
'$id' => $siteId ,
'buildRuntime' => 'node-22' ,
'framework' => 'other' ,
'name' => 'Duplicate deployment Site' ,
'adapter' => 'static' ,
'fallbackFile' => 'index.html' ,
]);
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'index.html' , $site [ 'body' ][ 'fallbackFile' ]);
2025-04-30 13:35:08 +00:00
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/sites/' . $siteId . '/deployments/duplicate' , array_merge ([
'content-type' => 'multipart/form-data' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-sdk-language' => 'cli'
], $this -> getHeaders ()), [
'deploymentId' => $deploymentId1 ,
]);
2025-03-17 16:35:03 +00:00
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId2 = $deployment [ 'body' ][ '$id' ];
2025-03-08 22:33:37 +00:00
$this -> assertNotEmpty ( $deploymentId2 );
2025-03-17 16:35:03 +00:00
$deployment = $this -> getDeployment ( $siteId , $deploymentId2 );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$this -> assertEquals ( $deployment [ 'body' ][ 'sourceSize' ], $deployment [ 'body' ][ 'totalSize' ]);
2025-04-30 13:35:08 +00:00
$this -> assertEquals ( 'cli' , $deployment [ 'body' ][ 'type' ]);
2025-03-17 16:35:03 +00:00
2025-05-06 10:01:22 +00:00
// create another duplicate deployment with manual trigger
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/sites/' . $siteId . '/deployments/duplicate' , array_merge ([
'content-type' => 'multipart/form-data' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'deploymentId' => $deploymentId1 ,
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId2 = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId2 );
$deployment = $this -> getDeployment ( $siteId , $deploymentId2 );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertEquals ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$this -> assertEquals ( $deployment [ 'body' ][ 'sourceSize' ], $deployment [ 'body' ][ 'totalSize' ]);
$this -> assertEquals ( 'manual' , $deployment [ 'body' ][ 'type' ]);
2025-03-17 16:35:03 +00:00
$this -> assertEventually ( function () use ( $siteId , $deploymentId2 ) {
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( $deploymentId2 , $site [ 'body' ][ 'deploymentId' ]);
}, 50000 , 500 );
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/not-found' );
$this -> assertStringContainsString ( " Index page " , $response [ 'body' ]);
2025-03-17 16:35:03 +00:00
$deployment = $this -> getDeployment ( $siteId , $deploymentId2 );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'sourceSize' ]);
$this -> assertGreaterThan ( 0 , $deployment [ 'body' ][ 'buildSize' ]);
$totalSize = $deployment [ 'body' ][ 'sourceSize' ] + $deployment [ 'body' ][ 'buildSize' ];
$this -> assertEquals ( $totalSize , $deployment [ 'body' ][ 'totalSize' ]);
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testUpdateDeploymentStatus () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'framework' => 'other' ,
'name' => 'Activate test Site' ,
'siteId' => ID :: unique (),
'adapter' => 'static' ,
]);
$this -> assertNotEmpty ( $siteId );
2025-03-20 14:22:35 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'latestDeploymentId' , $site [ 'body' ]);
$this -> assertArrayHasKey ( 'latestDeploymentCreatedAt' , $site [ 'body' ]);
$this -> assertArrayHasKey ( 'latestDeploymentStatus' , $site [ 'body' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'latestDeploymentId' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'latestDeploymentCreatedAt' ]);
$this -> assertEmpty ( $site [ 'body' ][ 'latestDeploymentStatus' ]);
2025-03-08 22:33:37 +00:00
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$deploymentId1 = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static' ),
'activate' => true
]);
$this -> assertNotEmpty ( $deploymentId1 );
2025-03-20 14:22:35 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentId1 , $site [ 'body' ][ 'latestDeploymentId' ]);
$this -> assertEquals ( 'ready' , $site [ 'body' ][ 'latestDeploymentStatus' ]);
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'Hello Appwrite' , $response [ 'body' ]);
$deploymentId2 = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static-spa' ),
'activate' => true
]);
$this -> assertNotEmpty ( $deploymentId2 );
2025-03-20 14:22:35 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentId2 , $site [ 'body' ][ 'latestDeploymentId' ]);
$this -> assertEquals ( 'ready' , $site [ 'body' ][ 'latestDeploymentStatus' ]);
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'Index page' , $response [ 'body' ]);
2025-03-20 14:22:35 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentId2 , $site [ 'body' ][ 'deploymentId' ]);
$this -> assertEquals ( $deploymentId2 , $site [ 'body' ][ 'latestDeploymentId' ]);
$this -> assertEquals ( 'ready' , $site [ 'body' ][ 'latestDeploymentStatus' ]);
2025-03-08 22:33:37 +00:00
2025-03-20 14:22:35 +00:00
$site = $this -> updateSiteDeployment ( $siteId , $deploymentId1 );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentId1 , $site [ 'body' ][ 'deploymentId' ]);
2025-03-08 22:33:37 +00:00
2025-03-20 14:22:35 +00:00
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentId1 , $site [ 'body' ][ 'deploymentId' ]);
$this -> assertEquals ( $deploymentId2 , $site [ 'body' ][ 'latestDeploymentId' ]);
$this -> assertEquals ( 'ready' , $site [ 'body' ][ 'latestDeploymentStatus' ]);
2025-03-08 22:33:37 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'Hello Appwrite' , $response [ 'body' ]);
2025-03-20 14:22:35 +00:00
$deployment = $this -> deleteDeployment ( $siteId , $deploymentId2 );
$this -> assertEquals ( 204 , $deployment [ 'headers' ][ 'status-code' ]);
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $deploymentId1 , $site [ 'body' ][ 'latestDeploymentId' ]);
$this -> assertEquals ( 'ready' , $site [ 'body' ][ 'latestDeploymentStatus' ]);
2025-03-08 22:33:37 +00:00
$this -> cleanupSite ( $siteId );
}
public function testPreviewDomain () : void
{
$siteId = $this -> setupSite ([
'buildRuntime' => 'node-22' ,
'framework' => 'other' ,
'name' => 'Authorized preview site' ,
'siteId' => ID :: unique (),
'adapter' => 'static' ,
]);
$this -> assertNotEmpty ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'static' ),
'activate' => true
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> getDeploymentDomain ( $deploymentId );
$this -> assertNotEmpty ( $domain );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( '/console/auth/preview' , $response [ 'headers' ][ 'location' ]);
$this -> assertStringContainsString ( 'projectId=' . $this -> getProject ()[ '$id' ], $response [ 'headers' ][ 'location' ]);
$this -> assertStringContainsString ( 'origin=' , $response [ 'headers' ][ 'location' ]);
$this -> assertStringContainsString ( 'path=%2Fcontact' , $response [ 'headers' ][ 'location' ]);
$session = $this -> client -> call ( Client :: METHOD_POST , '/account/sessions/email' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => 'console' ,
]), [
'email' => $this -> getRoot ()[ 'email' ],
'password' => 'password'
]);
$this -> assertEquals ( 201 , $session [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $session [ 'cookies' ][ 'a_session_console' ]);
$this -> assertNotEmpty ( $session [ 'body' ][ '$id' ]);
$cookie = 'a_session_console=' . $session [ 'cookies' ][ 'a_session_console' ];
$jwt = $this -> client -> call ( Client :: METHOD_POST , '/account/jwts' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => $cookie ,
'x-appwrite-project' => 'console' ,
]), []);
$this -> assertEquals ( 201 , $jwt [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $jwt [ 'body' ][ 'jwt' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/_appwrite/authorize' , params : [
'jwt' => $jwt [ 'body' ][ 'jwt' ],
'path' => '/contact'
], followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'set-cookie' , $response [ 'headers' ]);
$this -> assertStringContainsString ( 'a_jwt_console=' , $response [ 'headers' ][ 'set-cookie' ]);
2026-01-29 08:52:08 +00:00
// due to swoole update; no more httponly
$this -> assertStringContainsString ( 'HttpOnly' , $response [ 'headers' ][ 'set-cookie' ]);
2025-03-08 22:33:37 +00:00
$this -> assertStringContainsString ( 'domain=' . $domain , $response [ 'headers' ][ 'set-cookie' ]);
$this -> assertStringContainsString ( 'path=/' , $response [ 'headers' ][ 'set-cookie' ]);
$this -> assertNotEmpty ( $response [ 'cookies' ][ 'a_jwt_console' ]);
$this -> assertEquals ( $jwt [ 'body' ][ 'jwt' ], $response [ 'cookies' ][ 'a_jwt_console' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , headers : [
'cookie' => 'a_jwt_console=' . $jwt [ 'body' ][ 'jwt' ]
], followRedirects : false );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Contact page " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Preview by " , $response [ 'body' ]);
// Failure: Session missing (old bad, new ok)
$session = $this -> client -> call ( Client :: METHOD_DELETE , '/account/sessions/current' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => $cookie ,
'x-appwrite-project' => 'console' ,
]), []);
$this -> assertEquals ( 204 , $session [ 'headers' ][ 'status-code' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , headers : [
'cookie' => 'a_jwt_console=' . $jwt [ 'body' ][ 'jwt' ]
], followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( '/console/auth/preview' , $response [ 'headers' ][ 'location' ]);
// Failure: User missing
$cookie = 'a_session_console=' . $this -> getRoot ()[ 'session' ];
$jwt = $this -> client -> call ( Client :: METHOD_POST , '/account/jwts' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => $cookie ,
'x-appwrite-project' => 'console' ,
]), []);
$this -> assertEquals ( 201 , $jwt [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $jwt [ 'body' ][ 'jwt' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , headers : [
'cookie' => 'a_jwt_console=' . $jwt [ 'body' ][ 'jwt' ]
], followRedirects : false );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Contact page " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Preview by " , $response [ 'body' ]);
$user = $this -> client -> call ( Client :: METHOD_PATCH , '/account/status' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => $cookie ,
'x-appwrite-project' => 'console' ,
]), []);
$this -> assertEquals ( 200 , $user [ 'headers' ][ 'status-code' ]);
$this -> assertFalse ( $user [ 'body' ][ 'status' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , headers : [
'cookie' => 'a_jwt_console=' . $jwt [ 'body' ][ 'jwt' ]
], followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( '/console/auth/preview' , $response [ 'headers' ][ 'location' ]);
// Failure: Membership missing
2025-03-13 09:33:28 +00:00
$email = \uniqid () . 'newuser@appwrite.io' ;
2025-03-08 22:33:37 +00:00
$user = $this -> client -> call ( Client :: METHOD_POST , '/account' , [
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => 'console' ,
], [
'userId' => ID :: unique (),
2025-03-13 09:33:28 +00:00
'email' => $email ,
2025-03-08 22:33:37 +00:00
'password' => 'password'
]);
$this -> assertEquals ( 201 , $user [ 'headers' ][ 'status-code' ]);
$session = $this -> client -> call ( Client :: METHOD_POST , '/account/sessions/email' , [
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => 'console' ,
], [
2025-03-13 09:33:28 +00:00
'email' => $email ,
2025-03-08 22:33:37 +00:00
'password' => 'password' ,
]);
$this -> assertEquals ( 201 , $session [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $session [ 'cookies' ][ 'a_session_console' ]);
$cookie = 'a_session_console=' . $session [ 'cookies' ][ 'a_session_console' ];
$jwt = $this -> client -> call ( Client :: METHOD_POST , '/account/jwts' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'cookie' => $cookie ,
'x-appwrite-project' => 'console' ,
]), []);
$this -> assertEquals ( 201 , $jwt [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $jwt [ 'body' ][ 'jwt' ]);
$response = $proxyClient -> call ( Client :: METHOD_GET , '/contact' , headers : [
'cookie' => 'a_jwt_console=' . $jwt [ 'body' ][ 'jwt' ]
], followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( '/console/auth/preview' , $response [ 'headers' ][ 'location' ]);
$this -> cleanupSite ( $siteId );
}
2025-03-12 11:13:46 +00:00
public function testInvalidSSRSource () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro SSR Site' ,
'framework' => 'astro' ,
'adapter' => 'ssr' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( 200 , $site [ 'headers' ][ 'status-code' ]);
$this -> assertArrayHasKey ( 'adapter' , $site [ 'body' ]);
$this -> assertEquals ( 'ssr' , $site [ 'body' ][ 'adapter' ]);
$deployment = $this -> createDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro-static' ),
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ], 'Deployment status is failed, deployment: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
}, 100000 , 500 );
$this -> cleanupSite ( $siteId );
}
2025-03-21 12:23:22 +00:00
2025-11-21 04:58:02 +00:00
public function testDomainForFailedDeployment () : void
2025-03-26 11:22:32 +00:00
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Test Site' ,
'framework' => 'astro' ,
'buildRuntime' => 'node-22' ,
'buildCommand' => 'cd random'
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$deployment = $this -> createDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro' ),
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ], json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
}, 100000 , 500 );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
2025-11-27 11:01:14 +00:00
$this -> assertStringContainsString ( 'This page is empty, activate a deployment to make it live.' , $response [ 'body' ]);
2025-03-26 11:22:32 +00:00
$this -> cleanupSite ( $siteId );
}
2025-03-27 10:57:26 +00:00
2025-03-21 12:24:07 +00:00
public function testPermanentRedirect () : void
2025-03-21 12:23:22 +00:00
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Sub project site' ,
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './'
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deploymentId = $this -> setupDeployment ( $siteId , [
2025-03-27 11:06:37 +00:00
'code' => $this -> packageSite ( 'sub-directories' ),
2025-03-21 12:23:22 +00:00
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-03-27 11:06:37 +00:00
$this -> assertStringContainsString ( 'Sub-directory index' , $response [ 'body' ]);
2025-03-21 12:23:22 +00:00
$response1 = $proxyClient -> call ( Client :: METHOD_GET , '/project1' );
$this -> assertEquals ( 200 , $response1 [ 'headers' ][ 'status-code' ]);
2025-03-27 11:06:37 +00:00
$this -> assertStringContainsString ( 'Sub-directory project1' , $response1 [ 'body' ]);
2025-03-21 12:23:22 +00:00
$response2 = $proxyClient -> call ( Client :: METHOD_GET , '/project1/' );
2025-03-26 11:52:49 +00:00
$this -> assertEquals ( 200 , $response2 [ 'headers' ][ 'status-code' ]);
2025-03-27 11:06:37 +00:00
$this -> assertStringContainsString ( 'Sub-directory project1' , $response2 [ 'body' ]);
2025-03-21 12:23:22 +00:00
$this -> cleanupSite ( $siteId );
}
2025-03-31 16:51:33 +00:00
public function testDeploymentCommandEscaping () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'A site' ,
'framework' => 'other' ,
'adapter' => 'static' ,
'buildRuntime' => 'static-1' ,
'outputDirectory' => './' ,
'buildCommand' => " echo 'Hello two' " ,
'installCommand' => 'echo "Hello one"' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-03-31 16:51:33 +00:00
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'Hello one' , $deployment [ 'body' ][ 'buildLogs' ]);
$this -> assertStringContainsString ( 'Hello two' , $deployment [ 'body' ][ 'buildLogs' ]);
$this -> cleanupSite ( $siteId );
}
2025-04-03 07:05:05 +00:00
2025-04-16 13:39:08 +00:00
#[Retry(count: 3)]
2025-04-03 07:05:05 +00:00
public function testErrorPages () : void
{
// non-existent domain page
$domain = 'non-existent-page.sites.localhost' ;
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 404 , $response [ 'headers' ][ 'status-code' ]);
2025-04-15 12:16:04 +00:00
$this -> assertStringContainsString ( 'Nothing is here yet' , $response [ 'body' ]);
$this -> assertStringContainsString ( 'Start with this domain' , $response [ 'body' ]);
2025-04-03 07:05:05 +00:00
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
2025-04-15 12:16:04 +00:00
'name' => 'Static site' ,
'framework' => 'other' ,
2025-04-03 07:05:05 +00:00
'buildRuntime' => 'node-22' ,
2025-04-15 12:16:04 +00:00
'outputDirectory' => './' ,
'buildCommand' => 'sleep 5 && cd non-existing-directory' ,
2025-04-03 07:05:05 +00:00
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
2025-04-07 07:26:51 +00:00
// test canceled deployment error page
2025-04-03 07:05:05 +00:00
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-04-03 07:05:05 +00:00
'activate' => 'true'
]);
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
2025-04-07 07:26:51 +00:00
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
$deployment = $this -> cancelDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'canceled' , $deployment [ 'body' ][ 'status' ]);
2025-04-15 12:16:04 +00:00
$deploymentDomain = $this -> getDeploymentDomain ( $deploymentId );
$this -> assertNotEmpty ( $deploymentDomain );
2025-04-03 07:05:05 +00:00
$proxyClient = new Client ();
2025-04-07 09:08:06 +00:00
$proxyClient -> setEndpoint ( 'http://' . $deploymentDomain );
2025-04-03 07:05:05 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
$jwtObj = new JWT ( System :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , 900 , 0 );
$apiKey = $jwtObj -> encode ([
'projectCheckDisabled' => true ,
'previewAuthDisabled' => true ,
]);
2025-04-07 07:26:51 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false , headers : [
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey ,
]);
$this -> assertEquals ( 400 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Deployment build canceled " , $response [ 'body' ]);
2025-04-15 12:16:04 +00:00
$this -> assertStringContainsString ( " View deployments " , $response [ 'body' ]);
2025-11-27 11:01:14 +00:00
// check site domain for no active deployments
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' );
$this -> assertEquals ( 404 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'No active deployments' , $response [ 'body' ]);
$this -> assertStringContainsString ( 'View deployments' , $response [ 'body' ]);
2025-04-07 09:08:06 +00:00
2025-04-07 07:26:51 +00:00
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-04-07 07:26:51 +00:00
'activate' => 'true'
]);
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertNotEmpty ( $deploymentId );
2025-04-07 09:08:06 +00:00
$deploymentDomain = $this -> getDeploymentDomain ( $deploymentId );
$this -> assertNotEmpty ( $deploymentDomain );
2025-04-07 07:26:51 +00:00
2025-04-07 09:08:06 +00:00
$proxyClient -> setEndpoint ( 'http://' . $deploymentDomain );
2025-04-07 07:26:51 +00:00
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false );
$this -> assertEquals ( 301 , $response [ 'headers' ][ 'status-code' ]);
2025-04-03 07:05:05 +00:00
// deployment is still building error page
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false , headers : [
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey ,
]);
$this -> assertEquals ( 400 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Deployment is still building " , $response [ 'body' ]);
2025-04-15 12:16:04 +00:00
$this -> assertStringContainsString ( " View logs " , $response [ 'body' ]);
$this -> assertStringContainsString ( " Reload " , $response [ 'body' ]);
2025-04-03 07:05:05 +00:00
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ]);
}, 50000 , 500 );
// deployment failed error page
$response = $proxyClient -> call ( Client :: METHOD_GET , '/' , followRedirects : false , headers : [
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey ,
]);
$this -> assertEquals ( 400 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( " Deployment build failed " , $response [ 'body' ]);
2025-04-15 12:16:04 +00:00
$this -> assertStringContainsString ( " View logs " , $response [ 'body' ]);
2025-04-15 10:48:25 +00:00
2025-04-14 12:17:32 +00:00
$this -> cleanupSite ( $siteId );
}
2025-04-15 10:48:25 +00:00
2025-04-14 10:28:22 +00:00
public function testEmptySiteSource () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Empty source site' ,
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './' ,
]);
$this -> assertNotEmpty ( $siteId );
// Prepare empty site folder
// We cant use .gitkeep, because that would make deployment non-empty
$stdout = '' ;
$stderr = '' ;
$folderPath = realpath ( __DIR__ . '/../../../resources/sites' ) . '/empty' ;
Console :: execute ( " mkdir -p $folderPath " , '' , $stdout , $stderr );
$deployment = $this -> createDeployment ( $siteId , [
'code' => $this -> packageSite ( 'empty' ),
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ], 'Deployment status does not match: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
2025-04-14 10:54:56 +00:00
$this -> assertStringContainsString ( 'Error:' , $deployment [ 'body' ][ 'buildLogs' ], 'Deployment logs do not match: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
}, 100000 , 500 );
$this -> cleanupSite ( $siteId );
}
public function testOutputDirectoryEmpty () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Empty output directory' ,
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './empty-directory' ,
'buildCommand' => 'mkdir -p ./empty-directory'
]);
$this -> assertNotEmpty ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-04-14 10:54:56 +00:00
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ], 'Deployment status does not match: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
$this -> assertStringContainsString ( 'Error:' , $deployment [ 'body' ][ 'buildLogs' ], 'Deployment logs do not match: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
}, 100000 , 500 );
2025-04-03 07:05:05 +00:00
$this -> cleanupSite ( $siteId );
}
2025-04-14 10:54:56 +00:00
public function testOutputDirectoryMissing () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
2025-04-14 11:39:23 +00:00
'name' => 'Missing output directory' ,
2025-04-14 10:54:56 +00:00
'framework' => 'other' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './non-existing-directory' ,
]);
$this -> assertNotEmpty ( $siteId );
$deployment = $this -> createDeployment ( $siteId , [
2025-06-04 08:20:36 +00:00
'code' => $this -> packageSite ( 'static-single-file' ),
2025-04-14 10:54:56 +00:00
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ], 'Deployment status does not match: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
2025-05-18 10:51:07 +00:00
$this -> assertStringContainsString ( 'No such file or directory' , $deployment [ 'body' ][ 'buildLogs' ], 'Deployment logs do not match: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
2025-04-14 10:28:22 +00:00
}, 100000 , 500 );
2025-04-15 10:48:25 +00:00
2025-04-14 10:28:22 +00:00
$this -> cleanupSite ( $siteId );
}
2025-05-20 15:45:05 +00:00
public function testBuildErrorLogs () : void
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro SSR site' ,
2025-07-10 14:16:06 +00:00
'framework' => 'astro' ,
2025-05-20 15:45:05 +00:00
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'echo "custom error" && npm install' ,
'adapter' => 'ssr' ,
]);
$this -> assertNotEmpty ( $siteId );
$site = $this -> getSite ( $siteId );
$this -> assertEquals ( '200' , $site [ 'headers' ][ 'status-code' ]);
$domain = $this -> setupSiteDomain ( $siteId );
$this -> assertNotEmpty ( $domain );
$deployment = $this -> createDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro-static' ),
'activate' => true
]);
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $siteId , $deploymentId ) {
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 'failed' , $deployment [ 'body' ][ 'status' ], 'Deployment status is failed, deployment: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
}, 100000 , 500 );
$deployment = $this -> getDeployment ( $siteId , $deploymentId );
$this -> assertEquals ( 200 , $deployment [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'custom error' , $deployment [ 'body' ][ 'buildLogs' ]);
$this -> assertStringContainsString ( 'Adapter mismatch' , $deployment [ 'body' ][ 'buildLogs' ]);
$this -> cleanupSite ( $siteId );
}
2025-08-21 11:27:55 +00:00
public function testCookieHeader ()
{
$siteId = $this -> setupSite ([
'siteId' => ID :: unique (),
'name' => 'Astro site' ,
'framework' => 'astro' ,
'adapter' => 'ssr' ,
'buildRuntime' => 'node-22' ,
'outputDirectory' => './dist' ,
'buildCommand' => 'npm run build' ,
'installCommand' => 'npm install' ,
'fallbackFile' => '' ,
]);
$this -> assertNotEmpty ( $siteId );
$domain = $this -> setupSiteDomain ( $siteId );
$deploymentId = $this -> setupDeployment ( $siteId , [
'code' => $this -> packageSite ( 'astro' ),
'activate' => 'true'
]);
$this -> assertNotEmpty ( $deploymentId );
$domain = $this -> getSiteDomain ( $siteId );
$proxyClient = new Client ();
$proxyClient -> setEndpoint ( 'http://' . $domain );
$response = $proxyClient -> call ( Client :: METHOD_GET , '/cookies' , [
2025-09-03 15:49:10 +00:00
'cookie' => 'custom-session-id=abcd123; custom-user-id=efgh456'
2025-08-21 11:27:55 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-09-03 15:49:10 +00:00
$this -> assertEquals ( " abcd123;efgh456 " , $response [ 'body' ]);
$this -> assertEquals ( " value-one " , $response [ 'cookies' ][ 'my-cookie-one' ]);
$this -> assertEquals ( " value-two " , $response [ 'cookies' ][ 'my-cookie-two' ]);
2025-08-21 11:27:55 +00:00
$this -> cleanupSite ( $siteId );
}
2025-01-01 20:51:30 +00:00
}