Skip to content

Commit f00470b

Browse files
authored
@W-21319424 Increasing the unit test coverage for cli package (#197)
* @W-21319424 Increasing the unit test coverage for cli package * adding tests * remove incorrect --show-usage flag from realm list command documentation * adding tests
1 parent cf79e4d commit f00470b

File tree

89 files changed

+12632
-50
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+12632
-50
lines changed

packages/b2c-cli/test/commands/_test/index.test.ts

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,71 @@
33
* SPDX-License-Identifier: Apache-2
44
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
55
*/
6-
import {runCommand} from '@oclif/test';
6+
77
import {expect} from 'chai';
8+
import sinon from 'sinon';
9+
import Test from '../../../src/commands/_test/index.js';
10+
import {createIsolatedConfigHooks, createTestCommand} from '../../helpers/test-setup.js';
11+
12+
describe('commands/_test', () => {
13+
const hooks = createIsolatedConfigHooks();
14+
15+
beforeEach(async () => {
16+
await hooks.beforeEach();
17+
});
18+
19+
afterEach(() => {
20+
hooks.afterEach();
21+
});
22+
23+
async function createCommand(): Promise<any> {
24+
return createTestCommand(Test, hooks.getConfig(), {}, {});
25+
}
26+
27+
it('exercises logging functionality', async () => {
28+
const command = await createCommand();
29+
30+
const logStub = sinon.stub(command, 'log');
31+
const traceStub = sinon.stub(command.logger, 'trace');
32+
const infoStub = sinon.stub(command.logger, 'info');
33+
34+
await command.run();
35+
36+
expect(logStub.calledWith('Using this.log() - goes through pino')).to.be.true;
37+
expect(traceStub.calledWith('Trace level message')).to.be.true;
38+
expect(infoStub.called).to.be.true;
39+
});
40+
41+
it('logs with context objects', async () => {
42+
const command = await createCommand();
43+
44+
sinon.stub(command, 'log');
45+
const infoStub = sinon.stub(command.logger, 'info');
46+
47+
await command.run();
48+
49+
// Verify info was called with context objects
50+
const callWithContext = infoStub
51+
.getCalls()
52+
.find((call) => call.args[0] && typeof call.args[0] === 'object' && 'operation' in call.args[0]);
53+
expect(callWithContext).to.exist;
54+
expect(callWithContext?.args[0]).to.deep.include({operation: 'test', duration: 123});
55+
});
56+
57+
it('logs with redacted sensitive fields', async () => {
58+
const command = await createCommand();
59+
60+
sinon.stub(command, 'log');
61+
const infoStub = sinon.stub(command.logger, 'info');
62+
63+
await command.run();
864

9-
describe('_test', () => {
10-
// Skip in automated tests - this is a debug command that intentionally produces log output
11-
// Run manually with: ./cli _test
12-
it.skip('runs the smoke test command without errors', async () => {
13-
const {error} = await runCommand('_test');
14-
expect(error).to.be.undefined;
65+
// Verify info was called with object containing sensitive fields
66+
const callWithSensitive = infoStub
67+
.getCalls()
68+
.find((call) => call.args[0] && typeof call.args[0] === 'object' && 'password' in call.args[0]);
69+
expect(callWithSensitive).to.exist;
70+
expect(callWithSensitive?.args[0]).to.have.property('password');
71+
expect(callWithSensitive?.args[0]).to.have.property('client_secret');
1572
});
1673
});

packages/b2c-cli/test/commands/am/clients/get.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,88 @@ describe('am clients get', () => {
110110
expect(result.redirectUrls).to.deep.equal(['https://example.com/callback']);
111111
});
112112

113+
it('should display client details in non-JSON mode', async () => {
114+
const command = new ClientGet([], {} as any);
115+
(command as any).args = {'api-client-id': 'client-123'};
116+
(command as any).flags = {};
117+
stubCommandConfigAndLogger(command);
118+
stubJsonEnabled(command, false);
119+
stubImplicitOAuthStrategy(command);
120+
121+
const fullClient = {
122+
...mockClient,
123+
organizations: ['org-1', {id: 'org-2'}],
124+
roles: ['ADMIN', {roleEnumName: 'USER', id: 'user-role'}],
125+
roleTenantFilterMap: {ADMIN: 'f_ecom_zzxy_prd'},
126+
roleTenantFilter: '',
127+
defaultScopes: ['openid'],
128+
versionControl: ['v1'],
129+
disabledTimestamp: null,
130+
lastAuthenticatedDate: '2025-01-15',
131+
passwordModificationTimestamp: 1_706_200_000_000,
132+
};
133+
134+
server.use(
135+
http.get(`${BASE_URL}/apiclients/client-123`, () => {
136+
return HttpResponse.json(fullClient);
137+
}),
138+
);
139+
140+
const result = await command.run();
141+
expect(result.id).to.equal('client-123');
142+
});
143+
144+
it('should display client with minimal fields in non-JSON mode', async () => {
145+
const command = new ClientGet([], {} as any);
146+
(command as any).args = {'api-client-id': 'client-456'};
147+
(command as any).flags = {};
148+
stubCommandConfigAndLogger(command);
149+
stubJsonEnabled(command, false);
150+
stubImplicitOAuthStrategy(command);
151+
152+
const minimalClient = {
153+
id: 'client-456',
154+
name: 'Minimal Client',
155+
active: false,
156+
redirectUrls: [],
157+
scopes: [],
158+
defaultScopes: [],
159+
organizations: [],
160+
roles: [],
161+
roleTenantFilterMap: {},
162+
roleTenantFilter: 'ADMIN:zzxy_prd',
163+
};
164+
165+
server.use(
166+
http.get(`${BASE_URL}/apiclients/client-456`, () => {
167+
return HttpResponse.json(minimalClient);
168+
}),
169+
);
170+
171+
const result = await command.run();
172+
expect(result.id).to.equal('client-456');
173+
});
174+
175+
it('should handle valid expand flag', async () => {
176+
const command = new ClientGet([], {} as any);
177+
(command as any).args = {'api-client-id': 'client-123'};
178+
(command as any).flags = {expand: 'organizations,roles'};
179+
stubCommandConfigAndLogger(command);
180+
stubJsonEnabled(command, true);
181+
stubImplicitOAuthStrategy(command);
182+
183+
server.use(
184+
http.get(`${BASE_URL}/apiclients/client-123`, ({request}) => {
185+
const url = new URL(request.url);
186+
expect(url.searchParams.get('expand')).to.include('organizations');
187+
return HttpResponse.json(mockClient);
188+
}),
189+
);
190+
191+
const result = await command.run();
192+
expect(result.id).to.equal('client-123');
193+
});
194+
113195
it('should error when API client not found', async () => {
114196
const command = new ClientGet([], {} as any);
115197
(command as any).args = {'api-client-id': 'nonexistent'};

packages/b2c-cli/test/commands/am/clients/update.test.ts

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import {http, HttpResponse} from 'msw';
1010
import {setupServer} from 'msw/node';
1111
import {isolateConfig, restoreConfig} from '@salesforce/b2c-tooling-sdk/test-utils';
1212
import ClientUpdate from '../../../../src/commands/am/clients/update.js';
13-
import {stubCommandConfigAndLogger, stubJsonEnabled, stubImplicitOAuthStrategy} from '../../../helpers/test-setup.js';
13+
import {
14+
stubCommandConfigAndLogger,
15+
stubJsonEnabled,
16+
stubImplicitOAuthStrategy,
17+
makeCommandThrowOnError,
18+
} from '../../../helpers/test-setup.js';
1419

1520
const TEST_HOST = 'account.test.demandware.com';
1621
const BASE_URL = `https://${TEST_HOST}/dw/rest/v1`;
@@ -52,6 +57,68 @@ describe('am clients update', () => {
5257
});
5358
});
5459

60+
describe('validation', () => {
61+
it('should error when no flags provided', async () => {
62+
const command = new ClientUpdate([], {} as any);
63+
(command as any).args = {'api-client-id': 'client-123'};
64+
(command as any).flags = {};
65+
stubCommandConfigAndLogger(command);
66+
makeCommandThrowOnError(command);
67+
68+
try {
69+
await command.run();
70+
expect.fail('Should have thrown');
71+
} catch (error: unknown) {
72+
expect((error as Error).message).to.match(/No changes specified/);
73+
}
74+
});
75+
76+
it('should error when name exceeds 200 chars', async () => {
77+
const command = new ClientUpdate([], {} as any);
78+
(command as any).args = {'api-client-id': 'client-123'};
79+
(command as any).flags = {name: 'a'.repeat(201)};
80+
stubCommandConfigAndLogger(command);
81+
makeCommandThrowOnError(command);
82+
83+
try {
84+
await command.run();
85+
expect.fail('Should have thrown');
86+
} catch (error: unknown) {
87+
expect((error as Error).message).to.match(/at most 200 characters/);
88+
}
89+
});
90+
91+
it('should error when description exceeds 256 chars', async () => {
92+
const command = new ClientUpdate([], {} as any);
93+
(command as any).args = {'api-client-id': 'client-123'};
94+
(command as any).flags = {description: 'a'.repeat(257)};
95+
stubCommandConfigAndLogger(command);
96+
makeCommandThrowOnError(command);
97+
98+
try {
99+
await command.run();
100+
expect.fail('Should have thrown');
101+
} catch (error: unknown) {
102+
expect((error as Error).message).to.match(/at most 256 characters/);
103+
}
104+
});
105+
106+
it('should error on invalid role-tenant-filter', async () => {
107+
const command = new ClientUpdate([], {} as any);
108+
(command as any).args = {'api-client-id': 'client-123'};
109+
(command as any).flags = {'role-tenant-filter': 'INVALID FORMAT!!'};
110+
stubCommandConfigAndLogger(command);
111+
makeCommandThrowOnError(command);
112+
113+
try {
114+
await command.run();
115+
expect.fail('Should have thrown');
116+
} catch (error: unknown) {
117+
expect((error as Error).message).to.match(/Role tenant filter must match pattern/);
118+
}
119+
});
120+
});
121+
55122
describe('output formatting', () => {
56123
it('should return updated client in JSON mode', async () => {
57124
const command = new ClientUpdate([], {} as any);
@@ -83,5 +150,66 @@ describe('am clients update', () => {
83150
expect(result.id).to.equal('client-123');
84151
expect(result.name).to.equal('Updated Name');
85152
});
153+
154+
it('should handle all update flags', async () => {
155+
const command = new ClientUpdate([], {} as any);
156+
(command as any).args = {'api-client-id': 'client-123'};
157+
(command as any).flags = {
158+
name: 'New Name',
159+
description: 'New desc',
160+
organizations: 'org-1,org-2',
161+
roles: 'role-1,role-2',
162+
'role-tenant-filter': 'SALESFORCE_COMMERCE_API:abcd_prd',
163+
active: true,
164+
'redirect-urls': 'https://a.com,https://b.com',
165+
scopes: 'openid,mail',
166+
'default-scopes': 'openid',
167+
'version-control': 'v1',
168+
'token-endpoint-auth-method': 'client_secret_post',
169+
'jwt-public-key': ' some-key ',
170+
};
171+
stubCommandConfigAndLogger(command);
172+
stubJsonEnabled(command, true);
173+
stubImplicitOAuthStrategy(command);
174+
175+
server.use(
176+
http.put(`${BASE_URL}/apiclients/client-123`, async ({request}) => {
177+
const body = (await request.json()) as Record<string, unknown>;
178+
expect(body.name).to.equal('New Name');
179+
expect(body.active).to.equal(true);
180+
expect(body.organizations).to.deep.equal(['org-1', 'org-2']);
181+
expect(body.roles).to.deep.equal(['role-1', 'role-2']);
182+
expect(body.scopes).to.deep.equal(['openid', 'mail']);
183+
expect(body.defaultScopes).to.deep.equal(['openid']);
184+
expect(body.redirectUrls).to.deep.equal(['https://a.com', 'https://b.com']);
185+
expect(body.tokenEndpointAuthMethod).to.equal('client_secret_post');
186+
expect(body.jwtPublicKey).to.equal('some-key');
187+
return HttpResponse.json({id: 'client-123', ...body});
188+
}),
189+
);
190+
191+
const result = await command.run();
192+
expect(result.id).to.equal('client-123');
193+
});
194+
195+
it('should handle empty jwt-public-key as null', async () => {
196+
const command = new ClientUpdate([], {} as any);
197+
(command as any).args = {'api-client-id': 'client-123'};
198+
(command as any).flags = {'jwt-public-key': ''};
199+
stubCommandConfigAndLogger(command);
200+
stubJsonEnabled(command, true);
201+
stubImplicitOAuthStrategy(command);
202+
203+
server.use(
204+
http.put(`${BASE_URL}/apiclients/client-123`, async ({request}) => {
205+
const body = (await request.json()) as Record<string, unknown>;
206+
expect(body.jwtPublicKey).to.equal(null);
207+
return HttpResponse.json({id: 'client-123'});
208+
}),
209+
);
210+
211+
const result = await command.run();
212+
expect(result.id).to.equal('client-123');
213+
});
86214
});
87215
});

0 commit comments

Comments
 (0)