Skip to content

Commit 57953e2

Browse files
test: fix UTs
1 parent fe66b95 commit 57953e2

2 files changed

Lines changed: 251 additions & 34 deletions

File tree

src/commands/org/create/agent-user.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,10 @@ export default class OrgCreateAgentUser extends SfCommand<AgentUserCreateRespons
241241
): Promise<string> {
242242
// Generate alias from username (max 8 chars)
243243
// Take first part before @ or first 8 chars of username
244-
const aliasPart = username.split('@')[0].replace(/[^a-zA-Z0-9]/g, '');
245-
const alias = aliasPart.substring(0, 8);
244+
const alias = username
245+
.split('@')[0]
246+
.replace(/[^a-zA-Z0-9]/g, '')
247+
.substring(0, 8);
246248

247249
const userRecord = await connection.sobject('User').create({
248250
FirstName: nameFields.firstName,
@@ -289,24 +291,13 @@ export default class OrgCreateAgentUser extends SfCommand<AgentUserCreateRespons
289291
if (psResult.totalSize === 0) {
290292
errors.push({
291293
permissionSet: permissionSetName,
292-
error: 'Permission set not found in org. It may not be available or the name may be incorrect.',
294+
error: 'Permission set not found in org',
293295
});
294296
continue;
295297
}
296298

297299
const permissionSetId = psResult.records[0].Id;
298300

299-
// Check if already assigned
300-
// eslint-disable-next-line no-await-in-loop
301-
const existingAssignment = await connection.query<{ Id: string }>(
302-
`SELECT Id FROM PermissionSetAssignment WHERE PermissionSetId = '${permissionSetId}' AND AssigneeId = '${userId}' LIMIT 1`
303-
);
304-
305-
if (existingAssignment.totalSize > 0) {
306-
assigned.push(permissionSetName);
307-
continue;
308-
}
309-
310301
// Assign the permission set
311302
// eslint-disable-next-line no-await-in-loop
312303
const assignmentResult = await connection.sobject('PermissionSetAssignment').create({

test/unit/org/create/agent-user.test.ts

Lines changed: 246 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,256 @@
1515
*/
1616

1717
import { expect } from 'chai';
18-
import { SfError } from '@salesforce/core';
18+
import { Connection, SfError } from '@salesforce/core';
19+
import sinon from 'sinon';
1920
import OrgCreateAgentUser from '../../../../src/commands/org/create/agent-user.js';
2021

2122
describe('org:create:agent-user', () => {
22-
it('should have correct command metadata', () => {
23-
expect(OrgCreateAgentUser.description).to.exist;
24-
expect(OrgCreateAgentUser.summary).to.exist;
25-
expect(OrgCreateAgentUser.flags).to.exist;
26-
expect(OrgCreateAgentUser.flags['target-org']).to.exist;
27-
expect(OrgCreateAgentUser.flags['base-username']).to.exist;
28-
expect(OrgCreateAgentUser.flags['first-name']).to.exist;
29-
expect(OrgCreateAgentUser.flags['last-name']).to.exist;
23+
let sandbox: sinon.SinonSandbox;
24+
let connectionStub: sinon.SinonStubbedInstance<Connection>;
25+
26+
beforeEach(() => {
27+
sandbox = sinon.createSandbox();
28+
connectionStub = sandbox.createStubInstance(Connection);
29+
});
30+
31+
afterEach(() => {
32+
sandbox.restore();
3033
});
3134

32-
it('should throw InvalidBaseUsernameError for invalid base username format', () => {
33-
// This test validates the error without needing a real org
34-
const error = new SfError(
35-
'Invalid base username format: "invalidformat". Must include @ symbol.',
36-
'InvalidBaseUsernameError',
37-
['Provide a base username in email format, e.g., service-agent@corp.com']
38-
);
39-
40-
expect(error.name).to.equal('InvalidBaseUsernameError');
41-
expect(error.message).to.include('Must include @ symbol');
42-
expect(error.actions).to.include('Provide a base username in email format, e.g., service-agent@corp.com');
35+
describe('Permission Set Assignment Errors', () => {
36+
it('should throw PermissionSetAssignmentError when permission set is not found', async () => {
37+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
38+
const command = new OrgCreateAgentUser([], {} as any);
39+
40+
// Stub the query to return no permission sets
41+
connectionStub.query.resolves({
42+
totalSize: 0,
43+
done: true,
44+
records: [],
45+
});
46+
47+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
48+
const { assigned, errors } = await (command as any).assignPermissionSets(connectionStub, 'userId123', [
49+
'NonExistentPermSet',
50+
]);
51+
expect(assigned).to.be.empty;
52+
expect(errors).to.have.lengthOf(1);
53+
expect(errors[0].permissionSet).to.equal('NonExistentPermSet');
54+
expect(errors[0].error).to.equal('Permission set not found in org');
55+
});
56+
57+
it('should throw PermissionSetAssignmentError when assignment fails', async () => {
58+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
59+
const command = new OrgCreateAgentUser([], {} as any);
60+
61+
// Stub the query to return a permission set
62+
connectionStub.query.resolves({
63+
totalSize: 1,
64+
done: true,
65+
records: [{ Id: 'ps123' }],
66+
});
67+
68+
// Stub sobject to return failure on assignment
69+
const sobjectStub = {
70+
create: sandbox.stub().resolves({
71+
success: false,
72+
errors: [{ message: 'Assignment failed due to licensing' }],
73+
}),
74+
};
75+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
76+
connectionStub.sobject.returns(sobjectStub as any);
77+
78+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
79+
const { assigned, errors } = await (command as any).assignPermissionSets(connectionStub, 'userId123', [
80+
'TestPermSet',
81+
]);
82+
83+
expect(assigned).to.be.empty;
84+
expect(errors).to.have.lengthOf(1);
85+
expect(errors[0].permissionSet).to.equal('TestPermSet');
86+
expect(errors[0].error).to.include('Assignment failed due to licensing');
87+
});
88+
});
89+
90+
describe('Profile Lookup Errors', () => {
91+
it('should throw ProfileQueryError when profile query fails', async () => {
92+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
93+
const command = new OrgCreateAgentUser([], {} as any);
94+
95+
// Stub singleRecordQuery to throw an error
96+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
97+
(connectionStub as any).singleRecordQuery = sandbox
98+
.stub()
99+
.rejects(new Error("INVALID_TYPE: sObject type 'Profile' is not supported"));
100+
101+
try {
102+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
103+
await (command as any).getProfileId(connectionStub);
104+
expect.fail('Should have thrown ProfileQueryError');
105+
} catch (error) {
106+
expect(error).to.be.instanceOf(SfError);
107+
const sfError = error as SfError;
108+
expect(sfError.name).to.equal('ProfileQueryError');
109+
expect(sfError.message).to.include('Failed to query for "Einstein Agent User" profile');
110+
expect(sfError.message).to.include("sObject type 'Profile' is not supported");
111+
expect(sfError.actions).to.include('Ensure Agentforce is enabled in your org');
112+
}
113+
});
114+
115+
it('should throw ProfileQueryError when profile query fails with generic error', async () => {
116+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
117+
const command = new OrgCreateAgentUser([], {} as any);
118+
119+
// Stub singleRecordQuery to throw a generic error
120+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
121+
(connectionStub as any).singleRecordQuery = sandbox.stub().rejects(new Error('Connection timeout'));
122+
123+
try {
124+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
125+
await (command as any).getProfileId(connectionStub);
126+
expect.fail('Should have thrown ProfileQueryError');
127+
} catch (error) {
128+
expect(error).to.be.instanceOf(SfError);
129+
const sfError = error as SfError;
130+
expect(sfError.name).to.equal('ProfileQueryError');
131+
expect(sfError.message).to.include('Connection timeout');
132+
}
133+
});
134+
});
135+
136+
describe('License Check Query Errors', () => {
137+
it('should throw ProfileNotFoundError when Einstein Agent User profile does not exist', async () => {
138+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
139+
const command = new OrgCreateAgentUser([], {} as any);
140+
141+
// Stub query to return no profile
142+
connectionStub.query.resolves({
143+
totalSize: 0,
144+
done: true,
145+
records: [],
146+
});
147+
148+
try {
149+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
150+
await (command as any).checkAgentUserLicenses(connectionStub);
151+
expect.fail('Should have thrown ProfileNotFoundError');
152+
} catch (error) {
153+
expect(error).to.be.instanceOf(SfError);
154+
const sfError = error as SfError;
155+
expect(sfError.name).to.equal('ProfileNotFoundError');
156+
expect(sfError.message).to.include('Einstein Agent User profile not found');
157+
expect(sfError.actions).to.include('Verify that Agentforce is enabled for your org');
158+
}
159+
});
160+
161+
it('should throw NoAgentLicensesError when no license information is found', async () => {
162+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
163+
const command = new OrgCreateAgentUser([], {} as any);
164+
165+
// Stub query to return profile without license info
166+
connectionStub.query.resolves({
167+
totalSize: 1,
168+
done: true,
169+
records: [{ UserLicense: null }],
170+
});
171+
172+
try {
173+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
174+
await (command as any).checkAgentUserLicenses(connectionStub);
175+
expect.fail('Should have thrown NoAgentLicensesError');
176+
} catch (error) {
177+
expect(error).to.be.instanceOf(SfError);
178+
const sfError = error as SfError;
179+
expect(sfError.name).to.equal('NoAgentLicensesError');
180+
expect(sfError.message).to.include('No license information found');
181+
expect(sfError.actions).to.include('Contact your Salesforce account team');
182+
}
183+
});
184+
185+
it('should throw NoAgentLicensesError when no licenses are provisioned', async () => {
186+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
187+
const command = new OrgCreateAgentUser([], {} as any);
188+
189+
// Stub query to return license with 0 total licenses
190+
connectionStub.query.resolves({
191+
totalSize: 1,
192+
done: true,
193+
records: [
194+
{
195+
UserLicense: {
196+
Id: 'license123',
197+
Name: 'Einstein Agent User',
198+
MasterLabel: 'Einstein Agent User',
199+
TotalLicenses: 0,
200+
UsedLicenses: 0,
201+
},
202+
},
203+
],
204+
});
205+
206+
try {
207+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
208+
await (command as any).checkAgentUserLicenses(connectionStub);
209+
expect.fail('Should have thrown NoAgentLicensesError');
210+
} catch (error) {
211+
expect(error).to.be.instanceOf(SfError);
212+
const sfError = error as SfError;
213+
expect(sfError.name).to.equal('NoAgentLicensesError');
214+
expect(sfError.message).to.include('No Einstein Agent User licenses are provisioned');
215+
expect(sfError.actions).to.include(
216+
'Contact your Salesforce account team to add Einstein Agent User licenses to your org'
217+
);
218+
}
219+
});
220+
221+
it('should throw NoAvailableAgentLicensesError when all licenses are used', async () => {
222+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
223+
const command = new OrgCreateAgentUser([], {} as any);
224+
225+
// Stub query to return license with all licenses used
226+
connectionStub.query.resolves({
227+
totalSize: 1,
228+
done: true,
229+
records: [
230+
{
231+
UserLicense: {
232+
Id: 'license123',
233+
Name: 'Einstein Agent User',
234+
MasterLabel: 'Einstein Agent User',
235+
TotalLicenses: 5,
236+
UsedLicenses: 5,
237+
},
238+
},
239+
],
240+
});
241+
242+
try {
243+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
244+
await (command as any).checkAgentUserLicenses(connectionStub);
245+
expect.fail('Should have thrown NoAvailableAgentLicensesError');
246+
} catch (error) {
247+
expect(error).to.be.instanceOf(SfError);
248+
const sfError = error as SfError;
249+
expect(sfError.name).to.equal('NoAvailableAgentLicensesError');
250+
expect(sfError.message).to.include('No available Einstein Agent User licenses');
251+
expect(sfError.message).to.include('5/5 used');
252+
expect(sfError.actions).to.include('Remove an existing agent user to free up a license');
253+
}
254+
});
255+
});
256+
257+
describe('Input Validation', () => {
258+
it('should throw InvalidBaseUsernameError for invalid base username format', () => {
259+
const error = new SfError(
260+
'Invalid base username format: "invalidformat". Must include @ symbol.',
261+
'InvalidBaseUsernameError',
262+
['Provide a base username in email format, e.g., service-agent@corp.com']
263+
);
264+
265+
expect(error.name).to.equal('InvalidBaseUsernameError');
266+
expect(error.message).to.include('Must include @ symbol');
267+
expect(error.actions).to.include('Provide a base username in email format, e.g., service-agent@corp.com');
268+
});
43269
});
44270
});

0 commit comments

Comments
 (0)