Skip to content

Commit

Permalink
fix: add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
WillieRuemmele committed Nov 11, 2020
1 parent 57772dd commit 22f2252
Show file tree
Hide file tree
Showing 12 changed files with 673 additions and 63 deletions.
2 changes: 1 addition & 1 deletion messages/password.generate.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"flags": {
"onBehalfOf": "comma-separated list of usernames or aliases to assign the password to"
},
"noSelfSetAction": "Create a scratch org with the enableSetPasswordInApi org security setting set to TRUE and try again.",
"noSelfSetError": "Create a scratch org with the enableSetPasswordInApi org security setting set to TRUE and try again.",
"success": "Successfully set the password \"%s\" for user %s.",
"successMultiple": "Successfully set passwords:%s",
"viewWithCommand": "You can see the password again by running \"sfdx force:user:display -u %s\"."
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"bugs": "https://github.com/forcedotcom/cli/issues",
"dependencies": {
"@oclif/config": "^1.17.0",
"@salesforce/command": "^3.0.3",
"@salesforce/core": "^2.14.0",
"@salesforce/command": "^3.0.4",
"@salesforce/core": "^2.14.2",
"tslib": "^1"
},
"devDependencies": {
Expand Down
22 changes: 12 additions & 10 deletions src/commands/user/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
User,
UserFields,
} from '@salesforce/core';
import { get } from '@salesforce/ts-types';
import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command';
import get = Reflect.get;

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-user', 'create');
Expand Down Expand Up @@ -55,10 +55,11 @@ export class UserCreateCommand extends SfdxCommand {
private successes: SuccessMsg[];
private failures: FailureMsg[];

// todo: typing
public async run(): Promise<unknown> {
public async run(): Promise<UserFields> {
this.logger = await Logger.child(this.constructor.name);
const defaultUserFields = await DefaultUserFields.create({ templateUser: this.org.getUsername() });
const defaultUserFields: DefaultUserFields = await DefaultUserFields.create({
templateUser: this.org.getUsername(),
});
const user: User = await User.create({ org: this.org });

// merge defaults with provided values from cli -> file -> defaults
Expand All @@ -70,8 +71,8 @@ export class UserCreateCommand extends SfdxCommand {
await this.catchCreateUser(e, fields);
}

const permsets = this.varargs.permsets as string;
const generatepassword = this.varargs.generatepassword;
const permsets: string = fields['permsets'];
const generatepassword: string = fields['varargs'];

// Assign permission sets to the created user
if (permsets) {
Expand All @@ -90,7 +91,7 @@ export class UserCreateCommand extends SfdxCommand {
}

// Generate and set a password if specified
if (generatepassword) {
if (generatepassword === 'true') {
try {
const password = User.generatePasswordUtf8();
// await this.user.assignPassword(await AuthInfo.create({ username: fields.username }), password);
Expand All @@ -116,13 +117,13 @@ export class UserCreateCommand extends SfdxCommand {

this.print(fields);

return Promise.resolve(Object.assign({ orgId: this.org.getOrgId() }, this.user));
return Promise.resolve(Object.assign({ orgId: this.org.getOrgId() }, fields));
}

private async catchCreateUser(respBody: Error, fields: UserFields): Promise<void> {
// For Gacks, the error message is on response.body[0].message but for handled errors
// the error message is on response.body.Errors[0].description.
const errMessage = get(respBody, 'message') || 'Unknown Error';
const errMessage = (get(respBody, 'message') as string) || 'Unknown Error';
const conn: Connection = this.org.getConnection();

// Provide a more user friendly error message for certain server errors.
Expand All @@ -138,10 +139,11 @@ export class UserCreateCommand extends SfdxCommand {
}

private async aggregateFields(defaultFields: UserFields): Promise<UserFields> {
// take from cli params, then file, then default
// start with the default fields, then add the fields from the file, then (possibly overwritting) add the fields from the cli varargs param
if (this.flags.definitionfile) {
const content = await fs.readJson(this.flags.definitionfile);
Object.keys(content).forEach((key) => {
// we overload the UserField type by doing this
defaultFields[key] = content[key];
});
}
Expand Down
1 change: 0 additions & 1 deletion src/commands/user/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export class UserListCommand extends SfdxCommand {
});

const columns = ['Default', 'Alias', 'Username', 'Profile Name', 'User ID'];
// TODO: this used to print in blue, are we still doing that?
this.ux.styledHeader(`Users in org ${this.org.getOrgId()}`);
this.ux.table(trimmedList, columns);

Expand Down
10 changes: 8 additions & 2 deletions src/commands/user/password/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ export class UserPasswordGenerateCommand extends SfdxCommand {
private passwordData: PasswordData[] = [];

public async run(): Promise<PasswordData[]> {
this.usernames = (this.flags.onbehalfof || this.org.getUsername()).trim().split(',');
// split the usernames, trim them down, and then join them back
if (this.flags.onbehalfof) {
this.usernames = this.flags.onbehalfof.join(',').trim().split(',');
} else {
this.usernames = [this.org.getUsername()];
}

for (let username of this.usernames) {
try {
// Convert any aliases to usernames
Expand All @@ -54,7 +60,7 @@ export class UserPasswordGenerateCommand extends SfdxCommand {
});
} catch (e) {
if (e.message.includes('Cannot set password for self')) {
e.action = messages.getMessage('noSelfSetAction');
throw SfdxError.create('@salesforce/plugin-user', 'password.generate', 'noSelfSetError');
}
throw SfdxError.wrap(e);
}
Expand Down
15 changes: 7 additions & 8 deletions src/commands/user/permset/assign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,32 +43,31 @@ export class UserPermsetAssignCommand extends SfdxCommand {

public async run(): Promise<{ successes: SuccessMsg[]; failures: FailureMsg[] }> {
try {
if (this.flags && this.flags.onbehalfof && this.flags.onbehalfof.length > 0) {
this.usernames = this.flags.onbehalfof.trim().split(',');
if (this.flags.onbehalfof) {
this.usernames = this.flags.onbehalfof.join(',').trim().split(',');
} else {
this.usernames = [this.org.getUsername()];
}

for (let username of this.usernames) {
for (const username of this.usernames) {
// Convert any aliases to usernames
username = (await Aliases.fetch(username)) || username;
const aliasOrUsername = (await Aliases.fetch(username)) || username;
const connection: Connection = await Connection.create({
authInfo: await AuthInfo.create({ username }),
});
const org = await Org.create({ connection });
const user: User = await User.create({ org });
const fields: UserFields = await user.retrieve(username);

await user.assignPermissionSets(fields.id, this.flags.permsetname.trim().split(','));

try {
await user.assignPermissionSets(fields.id, this.flags.permsetname.split(','));
this.successes.push({
name: fields.username,
name: aliasOrUsername,
value: this.flags.permsetname,
});
} catch (e) {
this.failures.push({
name: fields.username,
name: aliasOrUsername,
message: e.message,
});
}
Expand Down
161 changes: 161 additions & 0 deletions test/commands/user/create.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { $$, expect, test } from '@salesforce/command/lib/test';
import { Aliases, Connection, DefaultUserFields, fs, Org, User } from '@salesforce/core';
import { stubMethod } from '@salesforce/ts-sinon';

const username = '[email protected]';

describe('force:user:create', () => {
async function prepareStubs(throws: { license?: boolean; duplicate?: boolean } = {}, readsFile = false) {
stubMethod($$.SANDBOX, Org.prototype, 'getConnection').callsFake(() => Connection.prototype);
stubMethod($$.SANDBOX, DefaultUserFields, 'create').resolves({
getFields: () => {
return {
id: '0052D0000043PawWWR',
username: '[email protected]',
alias: 'testAlias',
email: username,
emailEncodingKey: 'UTF-8',
languageLocaleKey: 'en_US',
localeSidKey: 'en_US',
profileId: '00e2D000000bNexWWR',
lastName: 'User',
timeZoneSidKey: 'America/Los_Angeles',
};
},
});
stubMethod($$.SANDBOX, Aliases, 'fetch').resolves('testAlias');
stubMethod($$.SANDBOX, User, 'create').callsFake(() => User.prototype);
stubMethod($$.SANDBOX, Org.prototype, 'getUsername').returns(username);
stubMethod($$.SANDBOX, Org.prototype, 'getOrgId').returns('abc123');

if (throws.license) {
stubMethod($$.SANDBOX, User.prototype, 'createUser').throws(new Error('LICENSE_LIMIT_EXCEEDED'));
stubMethod($$.SANDBOX, Connection.prototype, 'query').resolves({ records: [{ Name: 'testName' }] });
} else if (throws.duplicate) {
stubMethod($$.SANDBOX, User.prototype, 'createUser').throws(new Error('DUPLICATE_USERNAME'));
} else {
stubMethod($$.SANDBOX, User.prototype, 'createUser').resolves();
}

if (readsFile) {
stubMethod($$.SANDBOX, fs, 'readJson').resolves({ generatepassword: true });
}
}

test
.do(async () => {
await prepareStubs({}, false);
})
.stdout()
.command([
'user:create',
'--json',
'--targetusername',
'[email protected]',
'--targetdevhubusername',
'[email protected]',
])
.it('default create creates user exactly from DefaultUserFields', (ctx) => {
const expected = {
alias: 'testAlias',
email: username,
emailEncodingKey: 'UTF-8',
id: '0052D0000043PawWWR',
languageLocaleKey: 'en_US',
lastName: 'User',
localeSidKey: 'en_US',
orgId: 'abc123',
profileId: '00e2D000000bNexWWR',
timeZoneSidKey: 'America/Los_Angeles',
username: '[email protected]',
};
const result = JSON.parse(ctx.stdout).result;
expect(result).to.deep.equal(expected);
});

test
.do(async () => {
await prepareStubs({}, true);
})
.stdout()
.command([
'user:create',
'--json',
'--definitionfile',
'parent/child/file.json',
'--targetusername',
'[email protected]',
'--targetdevhubusername',
'[email protected]',
'[email protected]',
'generatepassword=false',
])
// we set generatepassword=false in the varargs, in the definitionfile we have generatepassword=true, so we SHOULD NOT generate a password
.it('will merge fields from the cli args, and the definitionfile correctly, preferring cli args', (ctx) => {
const expected = {
alias: 'testAlias',
email: '[email protected]',
emailEncodingKey: 'UTF-8',
generatepassword: 'false',
id: '0052D0000043PawWWR',
languageLocaleKey: 'en_US',
lastName: 'User',
localeSidKey: 'en_US',
orgId: 'abc123',
profileId: '00e2D000000bNexWWR',
timeZoneSidKey: 'America/Los_Angeles',
username: '[email protected]',
};
const result = JSON.parse(ctx.stdout).result;
expect(result).to.deep.equal(expected);
});

test
.do(async () => {
await prepareStubs({ license: true }, false);
})
.stdout()
.command([
'user:create',
'--json',
'--targetusername',
'[email protected]',
'--targetdevhubusername',
'[email protected]',
])
.it('will handle a failed `createUser` call with a licenseLimitExceeded error', (ctx) => {
const result = JSON.parse(ctx.stdout);
expect(result.status).to.equal(1);
expect(result.message).to.equal('There are no available user licenses for the user profile "testName".');
expect(result.name).to.equal('licenseLimitExceeded');
});

test
.do(async () => {
await prepareStubs({ duplicate: true }, false);
})
.stdout()
.command([
'user:create',
'--json',
'--targetusername',
'[email protected]',
'--targetdevhubusername',
'[email protected]',
])
.it('will handle a failed `createUser` call with a DuplicateUsername error', (ctx) => {
const result = JSON.parse(ctx.stdout);
expect(result.status).to.equal(1);
expect(result.name).to.equal('duplicateUsername');
expect(result.message).to.equal(
'The username "[email protected]" already exists in this or another Salesforce org. Usernames must be unique across all Salesforce orgs.'
);
});
});
Loading

0 comments on commit 22f2252

Please sign in to comment.