Skip to content

Commit 6495138

Browse files
fix: tighten project env fallback
1 parent b16d53e commit 6495138

3 files changed

Lines changed: 65 additions & 12 deletions

File tree

src/commands/test.run.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,9 @@ describe('runTestRunAll — batch fresh run', () => {
25822582
await expect(test.parseAsync(['run', '--all'], { from: 'user' })).rejects.toMatchObject({
25832583
code: 'VALIDATION_ERROR',
25842584
exitCode: 5,
2585+
details: expect.objectContaining({
2586+
reason: expect.stringContaining('TESTSPRITE_PROJECT_ID'),
2587+
}),
25852588
});
25862589
});
25872590

@@ -2607,6 +2610,30 @@ describe('runTestRunAll — batch fresh run', () => {
26072610
const post = captured.find(c => c.method === 'POST' && c.url.includes('/tests/batch/run'))!;
26082611
expect(post.body).toMatchObject({ projectId: 'project_env', source: 'cli' });
26092612
});
2613+
2614+
it('run --all uses TESTSPRITE_PROJECT_ID when --project is blank', async () => {
2615+
const { createTestCommand } = await import('./test.js');
2616+
const { credentialsPath } = makeCreds();
2617+
type Captured = { url: string; method: string; body: unknown };
2618+
const captured: Captured[] = [];
2619+
const fetchImpl = makeFetch((url, init) => {
2620+
const method = init.method ?? 'GET';
2621+
captured.push({ url, method, body: init.body ? JSON.parse(init.body as string) : undefined });
2622+
return { body: BATCH_FRESH_RESP };
2623+
});
2624+
const test = createTestCommand({
2625+
credentialsPath,
2626+
env: { TESTSPRITE_PROJECT_ID: 'project_env' } as NodeJS.ProcessEnv,
2627+
fetchImpl,
2628+
stdout: () => undefined,
2629+
stderr: () => undefined,
2630+
sleep: instantSleep,
2631+
});
2632+
await test.parseAsync(['run', '--all', '--project', ' '], { from: 'user' });
2633+
const post = captured.find(c => c.method === 'POST' && c.url.includes('/tests/batch/run'))!;
2634+
expect(post.body).toMatchObject({ projectId: 'project_env', source: 'cli' });
2635+
});
2636+
26102637
it('--all --target-url → exit 5 (target-url has no effect on BE-only batch)', async () => {
26112638
const { createTestCommand } = await import('./test.js');
26122639
const test = createTestCommand();

src/commands/test.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,25 @@ describe('runList', () => {
384384
expect(seen[0]).toContain('projectId=project_env');
385385
});
386386

387+
it('uses TESTSPRITE_PROJECT_ID when --project is blank', async () => {
388+
const { credentialsPath } = makeCreds();
389+
const seen: string[] = [];
390+
const fetchImpl = makeFetch(url => {
391+
seen.push(url);
392+
return { body: { items: [FE_TEST], nextToken: null } };
393+
});
394+
await runList(
395+
{ profile: 'default', output: 'json', debug: false, projectId: ' ' },
396+
{
397+
credentialsPath,
398+
env: { TESTSPRITE_PROJECT_ID: 'project_env' } as NodeJS.ProcessEnv,
399+
fetchImpl,
400+
stdout: () => undefined,
401+
},
402+
);
403+
expect(seen[0]).toContain('projectId=project_env');
404+
});
405+
387406
it('prefers explicit --project over TESTSPRITE_PROJECT_ID', async () => {
388407
const { credentialsPath } = makeCreds();
389408
const seen: string[] = [];
@@ -822,10 +841,15 @@ describe('createTestCommand list — required flag', () => {
822841
await test.parseAsync(['list'], { from: 'user' });
823842
expect.unreachable('expected ApiError');
824843
} catch (err) {
825-
const apiErr = err as { code?: string; exitCode?: number; details?: { field?: string } };
844+
const apiErr = err as {
845+
code?: string;
846+
exitCode?: number;
847+
details?: { field?: string; reason?: string };
848+
};
826849
expect(apiErr.code).toBe('VALIDATION_ERROR');
827850
expect(apiErr.exitCode).toBe(5);
828851
expect(apiErr.details?.field).toBe('project');
852+
expect(apiErr.details?.reason).toContain('TESTSPRITE_PROJECT_ID');
829853
}
830854
});
831855

src/commands/test.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9161,18 +9161,16 @@ export function createTestCommand(deps: TestDeps = {}): Command {
91619161
if (isAll) {
91629162
// --all path: wave-ordered fresh batch run.
91639163
const projectId = resolveProjectId(cmdOpts.project, deps);
9164-
if (!projectId) {
9165-
throw localValidationError(
9166-
'project',
9167-
'--all requires a project id - pass --project <id> or set TESTSPRITE_PROJECT_ID',
9168-
);
9169-
}
9164+
requireProjectId(
9165+
projectId,
9166+
'--all requires a project id - pass --project <id> or set TESTSPRITE_PROJECT_ID',
9167+
);
91709168
// --target-url has no effect on the --all batch path: a BE test's base
91719169
// URL is baked into its code, and the unified engine resolves each
91729170
// project's configured environment server-side (per-run URL overrides
91739171
// are not applied to batch FE runs either). Silently dropping it could
9174-
// run the suite against an unintended environment in the caller's mind
9175-
// reject loudly instead.
9172+
// run the suite against an unintended environment in the caller's mind,
9173+
// so reject loudly.
91769174
if (cmdOpts.targetUrl !== undefined && cmdOpts.targetUrl !== '') {
91779175
throw localValidationError(
91789176
'target-url',
@@ -9792,14 +9790,18 @@ interface StepsFlagOpts {
97929790
}
97939791

97949792
function resolveProjectId(projectId: string | undefined, deps: TestDeps): string | undefined {
9795-
if (projectId !== undefined) return projectId;
9793+
const explicit = projectId?.trim();
9794+
if (explicit && explicit.length > 0) return explicit;
97969795
const envValue = (deps.env ?? process.env).TESTSPRITE_PROJECT_ID;
97979796
const trimmed = envValue?.trim();
97989797
return trimmed && trimmed.length > 0 ? trimmed : undefined;
97999798
}
9800-
function requireProjectId(projectId: string | undefined): asserts projectId is string {
9799+
function requireProjectId(
9800+
projectId: string | undefined,
9801+
message = 'is required; pass --project <id> or set TESTSPRITE_PROJECT_ID',
9802+
): asserts projectId is string {
98019803
if (typeof projectId !== 'string' || projectId.length === 0) {
9802-
throw localValidationError('project', 'is required');
9804+
throw localValidationError('project', message);
98039805
}
98049806
}
98059807

0 commit comments

Comments
 (0)