mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Reuse authenticated state in multi-tenant release E2E
This commit is contained in:
parent
5fe6bfde57
commit
1a05c715ac
5 changed files with 109 additions and 1 deletions
|
|
@ -3,6 +3,7 @@ import fs from 'node:fs/promises';
|
|||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import {
|
||||
buildManagedLocalBackendEnv,
|
||||
|
|
@ -11,6 +12,9 @@ import {
|
|||
shouldBuildManagedLocalBackendBinary,
|
||||
} from './managed-local-backend.mjs';
|
||||
|
||||
const scriptsDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const integrationRoot = path.resolve(scriptsDir, '..');
|
||||
|
||||
test('buildManagedLocalBackendState uses deterministic defaults', () => {
|
||||
const state = buildManagedLocalBackendState({});
|
||||
assert.equal(state.repoRoot.endsWith('/repos/pulse'), true);
|
||||
|
|
@ -143,6 +147,26 @@ test('buildManagedLocalBackendEnv seeds deterministic auth in demo mode', () =>
|
|||
assert.equal(env.PULSE_AUTH_PASS, 'adminadminadmin');
|
||||
});
|
||||
|
||||
test('multi-tenant release auth reuses storage state and classifies login rate limits', async () => {
|
||||
const helpers = await fs.readFile(path.join(integrationRoot, 'tests', 'helpers.ts'), 'utf8');
|
||||
const multiTenantSpec = await fs.readFile(
|
||||
path.join(integrationRoot, 'tests', '03-multi-tenant.spec.ts'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
assert.match(multiTenantSpec, /createAuthenticatedStorageState/);
|
||||
assert.match(multiTenantSpec, /storageState:\s*async\s*\(\{\s*authStorageStatePath\s*\},\s*use\)/);
|
||||
assert.match(multiTenantSpec, /authStorageStatePath:\s*\[/);
|
||||
assert.match(multiTenantSpec, /multi-tenant-\$\{workerInfo\.project\.name\}\.json/);
|
||||
assert.match(multiTenantSpec, /\{\s*scope:\s*'worker'\s*\}/);
|
||||
|
||||
assert.match(helpers, /new URL\(response\.url\(\)\)\.pathname === "\/api\/login"/);
|
||||
assert.match(helpers, /response\.request\(\)\.method\(\)\.toUpperCase\(\) === "POST"/);
|
||||
assert.match(helpers, /loginResponse\?\.status\(\) === 429/);
|
||||
assert.match(helpers, /return "error:Too many requests"/);
|
||||
assert.match(helpers, /\/too many\/i\.test\(lastOutcome\) \? 15_000/);
|
||||
});
|
||||
|
||||
test('shouldBuildManagedLocalBackendBinary returns true when binary is missing', async () => {
|
||||
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-'));
|
||||
const state = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { test as base, expect } from '@playwright/test';
|
||||
import {
|
||||
apiRequest,
|
||||
createAuthenticatedStorageState,
|
||||
createOrg,
|
||||
deleteOrg,
|
||||
ensureAuthenticated,
|
||||
|
|
@ -10,6 +14,38 @@ import {
|
|||
waitForAppShell,
|
||||
} from './helpers';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
type WorkerFixtures = {
|
||||
authStorageStatePath: string;
|
||||
};
|
||||
|
||||
const test = base.extend<{}, WorkerFixtures>({
|
||||
storageState: async ({ authStorageStatePath }, use) => {
|
||||
await use(authStorageStatePath);
|
||||
},
|
||||
authStorageStatePath: [
|
||||
async ({ browser }, use, workerInfo) => {
|
||||
const storageStatePath = path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'tmp',
|
||||
'playwright-auth',
|
||||
`multi-tenant-${workerInfo.project.name}.json`,
|
||||
);
|
||||
fs.mkdirSync(path.dirname(storageStatePath), { recursive: true });
|
||||
await createAuthenticatedStorageState(browser, storageStatePath);
|
||||
try {
|
||||
await use(storageStatePath);
|
||||
} finally {
|
||||
fs.rmSync(storageStatePath, { force: true });
|
||||
}
|
||||
},
|
||||
{ scope: 'worker' },
|
||||
],
|
||||
});
|
||||
|
||||
type Organization = {
|
||||
id: string;
|
||||
displayName?: string;
|
||||
|
|
|
|||
|
|
@ -925,6 +925,15 @@ export async function login(page: Page, credentials = E2E_CREDENTIALS) {
|
|||
.first();
|
||||
|
||||
const submitAndAwaitOutcome = async (): Promise<string> => {
|
||||
const loginResponsePromise = page
|
||||
.waitForResponse(
|
||||
(response) =>
|
||||
new URL(response.url()).pathname === "/api/login" &&
|
||||
response.request().method().toUpperCase() === "POST",
|
||||
{ timeout: 30_000 },
|
||||
)
|
||||
.catch(() => null);
|
||||
|
||||
await page.fill('input[name="username"]', credentials.username);
|
||||
await page.fill('input[name="password"]', credentials.password);
|
||||
await page.click('button[type="submit"]');
|
||||
|
|
@ -941,6 +950,13 @@ export async function login(page: Page, credentials = E2E_CREDENTIALS) {
|
|||
const message = (
|
||||
(await loginErrorText.textContent()) || "login_error"
|
||||
).trim();
|
||||
const loginResponse = await Promise.race([
|
||||
loginResponsePromise,
|
||||
page.waitForTimeout(100).then(() => null),
|
||||
]);
|
||||
if (loginResponse?.status() === 429) {
|
||||
return "error:Too many requests";
|
||||
}
|
||||
return `error:${message}`;
|
||||
}
|
||||
await page.waitForTimeout(250);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue