Skip to content

Commit bb1d386

Browse files
authored
@W-21274558 add and fix e2e tests for sandbox and mrt (#157)
1 parent 55c81c3 commit bb1d386

File tree

2 files changed

+212
-1
lines changed

2 files changed

+212
-1
lines changed

packages/b2c-cli/test/functional/e2e/mrt-lifecycle.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,21 @@ describe('MRT Lifecycle E2E Tests', function () {
479479
{timeout: TIMEOUTS.DEFAULT, env: MRT_TEST_ENV},
480480
);
481481

482+
// In some environments, the delete operation can be retried by Mocha.
483+
// If a previous attempt already deleted the variable, the backend will
484+
// return an error indicating that the variable does not exist. Treat
485+
// this specific case as acceptable instead of failing the test.
486+
if (result.exitCode !== 0) {
487+
const errorText = String(result.stderr || result.stdout || '');
488+
if (errorText.includes('does not exist')) {
489+
console.log(
490+
` ⚠ Environment variable ${testVarKey} was already deleted (does not exist), treating as success`,
491+
);
492+
varCreated = false;
493+
return;
494+
}
495+
}
496+
482497
expect(result.exitCode, `Env var delete command failed: ${result.stderr}`).to.equal(0);
483498

484499
const response = parseJSONOutput(result);

packages/b2c-cli/test/functional/e2e/sandbox-lifecycle.test.ts

Lines changed: 197 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,143 @@ describe('Sandbox Lifecycle E2E Tests', function () {
245245
});
246246
});
247247

248-
describe('Step 8: Delete Sandbox', function () {
248+
describe('Step 8: Sandbox Usage', function () {
249+
it('should retrieve sandbox usage in JSON format', async function () {
250+
// Skip if we don't have a valid sandbox ID
251+
if (!sandboxId) {
252+
this.skip();
253+
}
254+
255+
const result = await runCLIWithRetry(['sandbox', 'usage', sandboxId, '--json'], {verbose: true});
256+
257+
expect(result.exitCode, `Usage failed: ${toString(result.stderr)}`).to.equal(0);
258+
expect(result.stdout, 'Usage command should return JSON output').to.not.be.empty;
259+
260+
const response = parseJSONOutput(result);
261+
expect(response, 'Usage response should be a valid object').to.be.an('object');
262+
263+
// Response can be either a usage model or a wrapper with data property
264+
const usage: any = 'data' in response ? (response as any).data : response;
265+
if (usage && typeof usage === 'object') {
266+
if (usage.sandboxSeconds !== undefined) {
267+
expect(usage.sandboxSeconds, 'sandboxSeconds should be a number when present').to.be.a('number');
268+
}
269+
if (usage.minutesUp !== undefined) {
270+
expect(usage.minutesUp, 'minutesUp should be a number when present').to.be.a('number');
271+
}
272+
if (usage.minutesDown !== undefined) {
273+
expect(usage.minutesDown, 'minutesDown should be a number when present').to.be.a('number');
274+
}
275+
276+
// Some backends may also provide aggregate sandbox counters; validate types when available
277+
if (usage.activeSandboxes !== undefined) {
278+
expect(usage.activeSandboxes, 'activeSandboxes should be a number when present').to.be.a('number');
279+
}
280+
if (usage.createdSandboxes !== undefined) {
281+
expect(usage.createdSandboxes, 'createdSandboxes should be a number when present').to.be.a('number');
282+
}
283+
if (usage.deletedSandboxes !== undefined) {
284+
expect(usage.deletedSandboxes, 'deletedSandboxes should be a number when present').to.be.a('number');
285+
}
286+
}
287+
});
288+
});
289+
290+
describe('Step 9: Sandbox Aliases', function () {
291+
let createdAliasId: string | undefined;
292+
let createdAliasHostname: string | undefined;
293+
294+
it('should create an alias for the sandbox', async function () {
295+
// Skip if we don't have a valid sandbox ID
296+
if (!sandboxId) {
297+
this.skip();
298+
}
299+
300+
// Use a short, unique hostname per test run to avoid collisions and
301+
// stay well under Kubernetes label length limits (63 characters).
302+
const suffix = Date.now().toString(36);
303+
const aliasHostname = `e2e-${suffix}.example.com`;
304+
305+
const createResult = await runCLIWithRetry(['sandbox', 'alias', 'create', sandboxId, aliasHostname, '--json'], {
306+
verbose: true,
307+
});
308+
309+
expect(createResult.exitCode, `Alias create failed: ${toString(createResult.stderr)}`).to.equal(0);
310+
311+
const createResponse = parseJSONOutput(createResult) as any;
312+
expect(createResponse, 'Alias create response should be an object').to.be.an('object');
313+
expect(createResponse).to.have.property('id');
314+
expect(createResponse).to.have.property('name');
315+
316+
createdAliasId = createResponse.id as string;
317+
createdAliasHostname = createResponse.name as string;
318+
});
319+
320+
it('should list aliases for the sandbox including the created alias', async function () {
321+
// Skip if we don't have a valid sandbox ID or no alias was created
322+
if (!sandboxId || !createdAliasId) {
323+
this.skip();
324+
}
325+
326+
const result = await runCLIWithRetry(['sandbox', 'alias', 'list', sandboxId, '--json'], {verbose: true});
327+
328+
expect(result.exitCode, `Alias list failed: ${toString(result.stderr)}`).to.equal(0);
329+
330+
const response = parseJSONOutput(result);
331+
// sandbox alias list --json returns an array of alias objects (possibly empty)
332+
expect(response, 'Alias list response should be an array').to.be.an('array');
333+
334+
if (Array.isArray(response) && response.length > 0) {
335+
const found = (response as any[]).find(
336+
(alias) => alias.id === createdAliasId || alias.name === createdAliasHostname,
337+
);
338+
expect(found, 'Expected alias list to include the created alias').to.exist;
339+
}
340+
});
341+
342+
it('should delete the created alias for the sandbox', async function () {
343+
// Skip if we don't have a valid sandbox ID or no alias was created
344+
if (!sandboxId || !createdAliasId) {
345+
this.skip();
346+
}
347+
348+
const deleteResult = await runCLIWithRetry(
349+
['sandbox', 'alias', 'delete', sandboxId, createdAliasId, '--force', '--json'],
350+
{verbose: true},
351+
);
352+
353+
expect(deleteResult.exitCode, `Alias delete failed: ${toString(deleteResult.stderr)}`).to.equal(0);
354+
355+
const deleteResponse = parseJSONOutput(deleteResult) as any;
356+
expect(deleteResponse).to.have.property('success', true);
357+
});
358+
});
359+
360+
describe('Step 10: Reset Sandbox', function () {
361+
it('should trigger a sandbox reset operation in JSON mode', async function () {
362+
this.timeout(TIMEOUTS.ODS_OPERATION);
363+
364+
// Skip if we don't have a valid sandbox ID
365+
if (!sandboxId) {
366+
this.skip();
367+
}
368+
369+
const result = await runCLIWithRetry(
370+
['sandbox', 'reset', sandboxId, '--wait', '--poll-interval', '10', '--timeout', '600', '--force', '--json'],
371+
{timeout: TIMEOUTS.ODS_OPERATION, verbose: true},
372+
);
373+
374+
expect(result.exitCode, `Reset failed: ${toString(result.stderr)}`).to.equal(0);
375+
expect(result.stdout, 'Reset command should return JSON output').to.not.be.empty;
376+
377+
const response = parseJSONOutput(result);
378+
expect(response, 'Reset response should be a valid object').to.be.an('object');
379+
expect(response).to.have.property('operationState');
380+
expect(response).to.have.property('sandboxState');
381+
});
382+
});
383+
384+
describe('Step 11: Delete Sandbox', function () {
249385
it('should delete the sandbox', async function () {
250386
// Skip if we don't have a valid sandbox ID
251387
if (!sandboxId) {
@@ -299,6 +435,66 @@ describe('Sandbox Lifecycle E2E Tests', function () {
299435
);
300436
});
301437
});
438+
439+
describe('Realm Management', function () {
440+
const realmId = process.env.TEST_REALM;
441+
442+
before(function () {
443+
if (!realmId) {
444+
this.skip();
445+
}
446+
});
447+
448+
it('should get realm details', async function () {
449+
const result = await runCLIWithRetry(['sandbox', 'realm', 'get', realmId!, '--json'], {verbose: true});
450+
451+
expect(result.exitCode, `Realm get failed: ${toString(result.stderr)}`).to.equal(0);
452+
453+
const response = parseJSONOutput(result);
454+
expect(response, 'Realm get response should be a valid object').to.be.an('object');
455+
expect(response).to.have.property('realm');
456+
});
457+
458+
it('should fetch realm usage in JSON format', async function () {
459+
const result = await runCLIWithRetry(['sandbox', 'realm', 'usage', realmId!, '--json'], {verbose: true});
460+
461+
expect(result.exitCode, `Realm usage failed: ${toString(result.stderr)}`).to.equal(0);
462+
463+
const response = parseJSONOutput(result);
464+
expect(response, 'Realm usage response should be a valid object').to.be.an('object');
465+
466+
const usage: any = 'data' in response ? (response as any).data : response;
467+
if (usage && typeof usage === 'object') {
468+
if (usage.activeSandboxes !== undefined) {
469+
expect(usage.activeSandboxes, 'activeSandboxes should be a number when present').to.be.a('number');
470+
}
471+
if (usage.createdSandboxes !== undefined) {
472+
expect(usage.createdSandboxes, 'createdSandboxes should be a number when present').to.be.a('number');
473+
}
474+
if (usage.deletedSandboxes !== undefined) {
475+
expect(usage.deletedSandboxes, 'deletedSandboxes should be a number when present').to.be.a('number');
476+
}
477+
if (usage.sandboxSeconds !== undefined) {
478+
expect(usage.sandboxSeconds, 'sandboxSeconds should be a number when present').to.be.a('number');
479+
}
480+
if (usage.minutesUp !== undefined) {
481+
expect(usage.minutesUp, 'minutesUp should be a number when present').to.be.a('number');
482+
}
483+
if (usage.minutesDown !== undefined) {
484+
expect(usage.minutesDown, 'minutesDown should be a number when present').to.be.a('number');
485+
}
486+
}
487+
});
488+
489+
it('should fail realm update when no flags are provided', async function () {
490+
const result = await runCLI(['sandbox', 'realm', 'update', realmId!, '--json']);
491+
492+
expect(result.exitCode, 'Realm update without flags should fail').to.not.equal(0);
493+
494+
const errorText = String(result.stderr || result.stdout || '');
495+
expect(errorText).to.match(/No update flags specified/i);
496+
});
497+
});
302498
});
303499

304500
after(function () {});

0 commit comments

Comments
 (0)