Skip to content

Commit e88d780

Browse files
committed
fix: delete sandbox auth files before writing new one
1 parent 1071303 commit e88d780

File tree

2 files changed

+143
-16
lines changed

2 files changed

+143
-16
lines changed

src/org/org.ts

+20-7
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,9 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
11361136
private async queryLatestSandboxProcessBySandboxName(sandboxNameIn: string): Promise<SandboxProcessObject> {
11371137
const { tooling } = this.getConnection();
11381138
this.logger.debug(`QueryLatestSandboxProcessBySandboxName called with SandboxName: ${sandboxNameIn}`);
1139-
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
1139+
const queryStr = `SELECT ${sandboxProcessFields.join(
1140+
','
1141+
)} FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
11401142

11411143
const queryResult = await tooling.query(queryStr);
11421144
this.logger.debug(queryResult, 'Return from calling queryToolingApi');
@@ -1381,8 +1383,20 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
13811383
oauth2Options.clientId = productionAuthFields.clientId;
13821384
}
13831385

1386+
// Before creating the AuthInfo, delete any existing auth files for the sandbox.
1387+
// This is common when refreshing sandboxes, and will cause AuthInfo to throw
1388+
// because it doesn't want to overwrite existing auth files.
1389+
const stateAggregator = await StateAggregator.getInstance();
1390+
try {
1391+
await stateAggregator.orgs.read(sandboxRes.authUserName);
1392+
await stateAggregator.orgs.remove(sandboxRes.authUserName);
1393+
} catch (e) {
1394+
// ignore since this is only for deleting existing auth files.
1395+
}
1396+
13841397
const authInfo = await AuthInfo.create({
13851398
username: sandboxRes.authUserName,
1399+
oauth2Options,
13861400
parentUsername: productionAuthFields.username,
13871401
});
13881402

@@ -1396,7 +1410,6 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
13961410
);
13971411
// save auth info for sandbox
13981412
await authInfo.save({
1399-
...oauth2Options,
14001413
isScratch: false,
14011414
isSandbox: true,
14021415
});
@@ -1540,15 +1553,15 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
15401553

15411554
this.logger.debug(result, 'Result of calling sandboxAuth');
15421555
return result;
1543-
} catch (err) {
1544-
const error = err as Error;
1556+
} catch (err: unknown) {
1557+
const error = err instanceof Error ? err : SfError.wrap(isString(err) ? err : 'unknown');
15451558
// There are cases where the endDate is set before the sandbox has actually completed.
15461559
// In that case, the sandboxAuth call will throw a specific exception.
15471560
if (error?.name === 'INVALID_STATUS') {
1548-
this.logger.debug('Error while authenticating the user', error?.toString());
1561+
this.logger.debug('Error while authenticating the user:', error.message);
15491562
} else {
1550-
// If it fails for any unexpected reason, just pass that through
1551-
throw SfError.wrap(error);
1563+
// If it fails for any unexpected reason, rethrow
1564+
throw error;
15521565
}
15531566
}
15541567
}

test/unit/org/orgTest.ts

+123-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { assert, expect, config as chaiConfig } from 'chai';
1616
import { OAuth2 } from 'jsforce';
1717
import { Transport } from 'jsforce/lib/transport';
1818
import { SinonSpy, SinonStub } from 'sinon';
19-
import { Org, SandboxEvents, SandboxProcessObject, SandboxUserAuthResponse } from '../../../src/org/org';
19+
import { Org, SandboxEvents, SandboxInfo, SandboxProcessObject, SandboxUserAuthResponse } from '../../../src/org/org';
2020
import { AuthInfo } from '../../../src/org/authInfo';
2121
import {} from '../../../src/org/connection';
2222
import { Connection, SingleRecordQueryErrors } from '../../../src/org/connection';
@@ -894,6 +894,20 @@ describe('Org Tests', () => {
894894
],
895895
};
896896

897+
const sandboxProcessFields = [
898+
'Id',
899+
'Status',
900+
'SandboxName',
901+
'SandboxInfoId',
902+
'LicenseType',
903+
'CreatedDate',
904+
'CopyProgress',
905+
'SandboxOrganization',
906+
'SourceId',
907+
'Description',
908+
'EndDate',
909+
];
910+
897911
let prodTestData: MockTestOrgData;
898912
let prod: Org;
899913

@@ -973,16 +987,111 @@ describe('Org Tests', () => {
973987
SandboxName: 'test',
974988
EndDate: '2021-19-06T20:25:46.000+0000',
975989
} as SandboxProcessObject;
990+
const err = new Error('could not auth');
991+
err.name = 'INVALID_STATUS';
976992
// @ts-expect-error - type not assignable
977-
stubMethod<SandboxUserAuthResponse>($$.SANDBOX, prod.getConnection().tooling, 'request').throws({
978-
name: 'INVALID_STATUS',
979-
});
993+
stubMethod<SandboxUserAuthResponse>($$.SANDBOX, prod.getConnection().tooling, 'request').throws(err);
980994

981995
// @ts-expect-error because private method
982996
await prod.sandboxSignupComplete(sandboxResponse);
983997
expect(logStub.callCount).to.equal(3);
984998
// error swallowed
985-
expect(logStub.thirdCall.args[0]).to.equal('Error while authenticating the user');
999+
expect(logStub.thirdCall.args[0]).to.equal('Error while authenticating the user:');
1000+
});
1001+
});
1002+
1003+
describe('refreshSandbox', () => {
1004+
const sbxInfo: SandboxInfo = {
1005+
Id: '0GQ4p000000U6nFGAS',
1006+
SandboxName: 'testSbx1',
1007+
LicenseType: 'DEVELOPER',
1008+
HistoryDays: 0,
1009+
CopyChatter: false,
1010+
AutoActivate: true,
1011+
IsDeleted: false,
1012+
CreatedDate: '2024-02-16T17:06:47.000+0000',
1013+
CreatedById: '005B0000004TiUpIAK',
1014+
LastModifiedDate: '2024-02-16T17:06:47.000+0000',
1015+
LastModifiedById: '005B0000004TiUpIAK',
1016+
};
1017+
const sbxProcess = {
1018+
attributes: {
1019+
type: 'SandboxProcess',
1020+
url: '/services/data/v60.0/tooling/sobjects/SandboxProcess/0GR1Q0000004kmaWAA',
1021+
},
1022+
Id: '0GR1Q0000004kmaWAA',
1023+
Status: 'Activating',
1024+
SandboxName: 'sbxGS02',
1025+
SandboxInfoId: '0GQ1Q0000004iQDWAY',
1026+
LicenseType: 'DEVELOPER',
1027+
CreatedDate: '2024-02-21T23:06:58.000+0000',
1028+
CopyProgress: 95,
1029+
SandboxOrganization: '00DDX000000QT3W',
1030+
SourceId: null,
1031+
Description: null,
1032+
EndDate: null,
1033+
};
1034+
let updateStub: SinonStub;
1035+
let querySandboxProcessStub: SinonStub;
1036+
let pollStatusAndAuthStub: SinonStub;
1037+
1038+
beforeEach(async () => {
1039+
updateStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'update').resolves({
1040+
id: '0GQ4p000000U6nFGAS',
1041+
success: true,
1042+
});
1043+
querySandboxProcessStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'query');
1044+
pollStatusAndAuthStub = stubMethod($$.SANDBOX, prod, 'pollStatusAndAuth').resolves(sbxProcess);
1045+
});
1046+
1047+
it('will refresh the SandboxInfo sObject correctly with polling', async () => {
1048+
querySandboxProcessStub.resolves({ records: [sbxProcess] });
1049+
const expectedQuery = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE SandboxName='${
1050+
sbxInfo.SandboxName
1051+
}' ORDER BY CreatedDate DESC`;
1052+
1053+
const result = await prod.refreshSandbox(sbxInfo, { wait: Duration.seconds(30) });
1054+
1055+
expect(updateStub.calledOnce).to.be.true;
1056+
expect(updateStub.firstCall.args[0]).to.equal('SandboxInfo');
1057+
expect(updateStub.firstCall.args[1]).to.equal(sbxInfo);
1058+
expect(querySandboxProcessStub.calledOnce).to.be.true;
1059+
expect(querySandboxProcessStub.firstCall.args[0]).to.equal(expectedQuery);
1060+
expect(pollStatusAndAuthStub.calledOnce).to.be.true;
1061+
expect(result).to.equal(sbxProcess);
1062+
});
1063+
1064+
it('will refresh the SandboxInfo sObject correctly async', async () => {
1065+
querySandboxProcessStub.resolves({ records: [sbxProcess] });
1066+
const expectedQuery = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE SandboxName='${
1067+
sbxInfo.SandboxName
1068+
}' ORDER BY CreatedDate DESC`;
1069+
1070+
const result = await prod.refreshSandbox(sbxInfo, { async: true });
1071+
1072+
expect(updateStub.calledOnce).to.be.true;
1073+
expect(updateStub.firstCall.args[0]).to.equal('SandboxInfo');
1074+
expect(updateStub.firstCall.args[1]).to.equal(sbxInfo);
1075+
expect(querySandboxProcessStub.calledOnce).to.be.true;
1076+
expect(querySandboxProcessStub.firstCall.args[0]).to.equal(expectedQuery);
1077+
expect(pollStatusAndAuthStub.called).to.be.false;
1078+
expect(result).to.equal(sbxProcess);
1079+
});
1080+
1081+
it('will throw an error if it fails to update the SandboxInfo', async () => {
1082+
updateStub.restore();
1083+
updateStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'update').resolves({
1084+
error: 'duplicate value found',
1085+
success: false,
1086+
});
1087+
try {
1088+
await shouldThrow(prod.refreshSandbox(sbxInfo, { async: true }));
1089+
} catch (e) {
1090+
expect(updateStub.calledOnce).to.be.true;
1091+
expect((e as Error).message).to.include('The sandbox org refresh failed with a result of');
1092+
expect((e as Error).message).to.include('duplicate value found');
1093+
expect((e as SfError).exitCode).to.equal(1);
1094+
}
9861095
});
9871096
});
9881097

@@ -1058,8 +1167,9 @@ describe('Org Tests', () => {
10581167
});
10591168

10601169
describe('resumeSandbox', () => {
1061-
const expectedSoql =
1062-
'SELECT Id,Status,SandboxName,SandboxInfoId,LicenseType,CreatedDate,CopyProgress,SandboxOrganization,SourceId,Description,EndDate FROM SandboxProcess WHERE %s ORDER BY CreatedDate DESC';
1170+
const expectedSoql = `SELECT ${sandboxProcessFields.join(
1171+
','
1172+
)} FROM SandboxProcess WHERE %s ORDER BY CreatedDate DESC`;
10631173
let lifecycleSpy: SinonSpy;
10641174
let queryStub: SinonStub;
10651175
let pollStatusAndAuthSpy: SinonSpy;
@@ -1193,7 +1303,9 @@ describe('Org Tests', () => {
11931303
let queryStub: SinonStub;
11941304
let pollStatusAndAuthStub: SinonStub;
11951305
const sandboxNameIn = 'test-sandbox';
1196-
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
1306+
const queryStr = `SELECT ${sandboxProcessFields.join(
1307+
','
1308+
)} FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
11971309

11981310
beforeEach(async () => {
11991311
queryStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'query');
@@ -1250,7 +1362,9 @@ describe('Org Tests', () => {
12501362
const deletedSbxProcess = Object.assign({}, statusResult.records[0], { Status: 'Deleted' });
12511363
queryStub.resolves({ records: [deletingSbxProcess, statusResult.records[0], deletedSbxProcess] });
12521364
const where = 'name="foo"';
1253-
const expectedSoql = `SELECT Id,Status,SandboxName,SandboxInfoId,LicenseType,CreatedDate,CopyProgress,SandboxOrganization,SourceId,Description,EndDate FROM SandboxProcess WHERE ${where} ORDER BY CreatedDate DESC`;
1365+
const expectedSoql = `SELECT ${sandboxProcessFields.join(
1366+
','
1367+
)} FROM SandboxProcess WHERE ${where} ORDER BY CreatedDate DESC`;
12541368

12551369
// @ts-ignore Testing a private method
12561370
const sbxProcess = await prod.querySandboxProcess(where);

0 commit comments

Comments
 (0)