fix: make Projects tests parallel-safe

- Use getLastEmailByAddress for SMTP tests instead of getLastEmail(2) to
  avoid shared mail server state issues under parallel execution
- Add retry logic to setupProject, setupProjectData, and
  setupScheduleProjectData for intermittent 401 errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jake Barnby
2026-03-06 01:20:09 +13:00
co-authored by Claude Opus 4.6
parent 499ca71c28
commit ee107d30b3
3 changed files with 109 additions and 72 deletions
+62 -34
View File
@@ -26,26 +26,39 @@ trait ProjectsBase
return self::$cachedProjectData;
}
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => ID::unique(),
'name' => 'Project Test',
]);
$this->assertEquals(201, $team['headers']['status-code']);
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Project Test',
'teamId' => $team['body']['$id'],
'region' => System::getEnv('_APP_REGION', 'default')
]);
$teamId = ID::unique();
$team = null;
for ($i = 0; $i < 3; $i++) {
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => $teamId,
'name' => 'Project Test',
]);
if (\in_array($team['headers']['status-code'], [201, 409])) {
break;
}
\usleep(500000);
}
$this->assertContains($team['headers']['status-code'], [201, 409]);
$project = null;
for ($i = 0; $i < 3; $i++) {
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Project Test',
'teamId' => $team['body']['$id'] ?? $teamId,
'region' => System::getEnv('_APP_REGION', 'default')
]);
if ($project['headers']['status-code'] === 201) {
break;
}
\usleep(500000);
}
$this->assertEquals(201, $project['headers']['status-code']);
self::$cachedProjectData = [
@@ -396,27 +409,42 @@ trait ProjectsBase
protected function setupProject(mixed $params, ?string $teamId = null, bool $newTeam = true): string
{
if ($newTeam) {
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
$generatedTeamId = $teamId ?? ID::unique();
$team = null;
for ($i = 0; $i < 3; $i++) {
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => $generatedTeamId,
'name' => 'Project Test',
]);
if (\in_array($team['headers']['status-code'], [201, 409])) {
break;
}
\usleep(500000);
}
$this->assertContains($team['headers']['status-code'], [201, 409], 'Setup team failed with status code: ' . $team['headers']['status-code'] . ' and response: ' . json_encode($team['body'], JSON_PRETTY_PRINT));
$teamId = $team['body']['$id'] ?? $generatedTeamId;
}
$project = null;
for ($i = 0; $i < 3; $i++) {
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => $teamId ?? ID::unique(),
'name' => 'Project Test',
...$params,
'teamId' => $teamId,
]);
$this->assertEquals(201, $team['headers']['status-code'], 'Setup team failed with status code: ' . $team['headers']['status-code'] . ' and response: ' . json_encode($team['body'], JSON_PRETTY_PRINT));
$teamId = $team['body']['$id'];
if ($project['headers']['status-code'] === 201) {
break;
}
\usleep(500000);
}
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
...$params,
'teamId' => $teamId,
]);
$this->assertEquals(201, $project['headers']['status-code'], 'Setup project failed with status code: ' . $project['headers']['status-code'] . ' and response: ' . json_encode($project['body'], JSON_PRETTY_PRINT));
return $project['body']['$id'];
@@ -951,26 +951,22 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
$emails = $this->getLastEmail(2);
$this->assertCount(2, $emails);
$this->assertEquals('custommailer@appwrite.io', $emails[0]['from'][0]['address']);
$this->assertEquals('Custom Mailer', $emails[0]['from'][0]['name']);
$this->assertEquals('reply@appwrite.io', $emails[0]['replyTo'][0]['address']);
$this->assertEquals('Custom Mailer', $emails[0]['replyTo'][0]['name']);
$this->assertEquals('Custom SMTP email sample', $emails[0]['subject']);
$this->assertStringContainsStringIgnoringCase('working correctly', $emails[0]['text']);
$this->assertStringContainsStringIgnoringCase('working correctly', $emails[0]['html']);
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $emails[0]['text']);
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $emails[0]['html']);
$smtpProbe = function ($email) {
$this->assertEquals('Custom SMTP email sample', $email['subject']);
};
$email1 = $this->getLastEmailByAddress('testuser@appwrite.io', $smtpProbe);
$email2 = $this->getLastEmailByAddress('testusertwo@appwrite.io', $smtpProbe);
$to = [
$emails[0]['to'][0]['address'],
$emails[1]['to'][0]['address']
];
\sort($to);
$this->assertEquals('testuser@appwrite.io', $to[0]);
$this->assertEquals('testusertwo@appwrite.io', $to[1]);
$this->assertEquals('custommailer@appwrite.io', $email1['from'][0]['address']);
$this->assertEquals('Custom Mailer', $email1['from'][0]['name']);
$this->assertEquals('reply@appwrite.io', $email1['replyTo'][0]['address']);
$this->assertEquals('Custom Mailer', $email1['replyTo'][0]['name']);
$this->assertEquals('Custom SMTP email sample', $email1['subject']);
$this->assertStringContainsStringIgnoringCase('working correctly', $email1['text']);
$this->assertStringContainsStringIgnoringCase('working correctly', $email1['html']);
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $email1['text']);
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $email1['html']);
$this->assertEquals('custommailer@appwrite.io', $email2['from'][0]['address']);
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/smtp/tests', array_merge([
'content-type' => 'application/json',
@@ -16,26 +16,39 @@ trait SchedulesBase
return self::$cachedScheduleProjectData;
}
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => ID::unique(),
'name' => 'Schedule Test Team',
]);
$this->assertEquals(201, $team['headers']['status-code']);
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Schedule Test Project',
'teamId' => $team['body']['$id'],
'region' => System::getEnv('_APP_REGION', 'default'),
]);
$teamId = ID::unique();
$team = null;
for ($i = 0; $i < 3; $i++) {
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => $teamId,
'name' => 'Schedule Test Team',
]);
if (\in_array($team['headers']['status-code'], [201, 409])) {
break;
}
\usleep(500000);
}
$this->assertContains($team['headers']['status-code'], [201, 409]);
$project = null;
for ($i = 0; $i < 3; $i++) {
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Schedule Test Project',
'teamId' => $team['body']['$id'] ?? $teamId,
'region' => System::getEnv('_APP_REGION', 'default'),
]);
if ($project['headers']['status-code'] === 201) {
break;
}
\usleep(500000);
}
$this->assertEquals(201, $project['headers']['status-code']);
$projectId = $project['body']['$id'];