Skip to content

Commit

Permalink
feat: add network check and UT (#746)
Browse files Browse the repository at this point in the history
* feat: add network check and UT

* chore: remove registry checks, moved to plugin-trust

* docs: update ping check message
  • Loading branch information
WillieRuemmele authored May 23, 2024
1 parent b8c5a6c commit 4e9f345
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 15 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"bugs": "https://github.com/forcedotcom/cli/issues",
"dependencies": {
"@inquirer/input": "^2.1.6",
"@jsforce/jsforce-node": "^3.2.0",
"@oclif/core": "^3.26.5",
"@salesforce/core": "^7.3.5",
"@salesforce/kit": "^3.1.0",
Expand Down
1 change: 0 additions & 1 deletion src/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export default class Doctor extends SfCommand<SfDoctorDiagnosis> {

public async run(): Promise<SfDoctorDiagnosis> {
const { flags } = await this.parse(Doctor);
// this.doctor = SFDoctor.getInstance();
this.doctor = SFDoctor.init(this.config);
const lifecycle = Lifecycle.getInstance();

Expand Down
51 changes: 42 additions & 9 deletions src/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,16 @@
*/

import childProcess from 'node:child_process';

import { got } from 'got';
import { Interfaces } from '@oclif/core';
import { Lifecycle, Messages } from '@salesforce/core';
import { Connection } from '@jsforce/jsforce-node';
import { SfDoctor, SfDoctorDiagnosis } from './doctor.js';

// const SUPPORTED_SHELLS = [
// 'bash',
// 'zsh',
// 'powershell'
// 'cmd.exe'
// ];

export type DiagnosticStatus = {
testName: string;
status: 'pass' | 'fail' | 'warn' | 'unknown';
}
};

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-info', 'diagnostics');
Expand Down Expand Up @@ -128,6 +122,45 @@ export class Diagnostics {
await Lifecycle.getInstance().emit('Doctor:diagnostic', { testName, status });
}

public async networkCheck(): Promise<void> {
await Promise.all(
[
// salesforce endpoints
'https://test.salesforce.com',
'https://appexchange.salesforce.com/services/data',
].map(async (url) => {
try {
const conn = new Connection();
await conn.request(url);
await Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: `can access: ${url}`, status: 'pass' });
} catch (e) {
await Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: `can't access: ${url}`, status: 'fail' });
this.doctor.addSuggestion(
`Cannot reach ${url} - potential network configuration error, check proxies, firewalls, environment variables`
);
}
})
);
// our S3 bucket, use the buildmanifest to avoid downloading the entire CLI
const manifestUrl =
'https://developer.salesforce.com/media/salesforce-cli/sf/channels/stable/sf-win32-x64-buildmanifest';
try {
await got.get(manifestUrl);
await Lifecycle.getInstance().emit('Doctor:diagnostic', {
testName: `can access: ${manifestUrl}`,
status: 'pass',
});
} catch (e) {
await Lifecycle.getInstance().emit('Doctor:diagnostic', {
testName: `can't access: ${manifestUrl}`,
status: 'fail',
});
this.doctor.addSuggestion(
`Cannot reach ${manifestUrl} - potential network configuration error, check proxies, firewalls, environment variables`
);
}
}

/**
* Checks and warns if any plugins are linked.
*/
Expand Down
6 changes: 3 additions & 3 deletions src/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type SfDoctor = {
writeFileSync(filePath: string, contents: string): string;
writeStderr(contents: string): Promise<boolean>;
writeStdout(contents: string): Promise<boolean>;
}
};

type CliConfig = Partial<Interfaces.Config> & { nodeEngine: string };

Expand All @@ -46,7 +46,7 @@ export type SfDoctorDiagnosis = {
commandName?: string;
commandExitCode?: string | number;
logFilePaths: string[];
}
};

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-info', 'doctor');
Expand All @@ -67,7 +67,7 @@ export class Doctor implements SfDoctor {
public readonly id: number;

// Contains all gathered data and results of diagnostics.
private diagnosis: SfDoctorDiagnosis;
private readonly diagnosis: SfDoctorDiagnosis;
private stdoutWriteStream: fs.WriteStream | undefined;
private stderrWriteStream: fs.WriteStream | undefined;

Expand Down
59 changes: 58 additions & 1 deletion test/diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { fromStub, spyMethod, stubInterface, stubMethod } from '@salesforce/ts-s
import { Config, Interfaces } from '@oclif/core';
import { Lifecycle } from '@salesforce/core';
import { ux } from '@oclif/core';
import { Connection } from '@jsforce/jsforce-node';
import { Doctor } from '../src/doctor.js';
import { Diagnostics } from '../src/diagnostics.js';

Expand Down Expand Up @@ -87,14 +88,70 @@ describe('Diagnostics', () => {
const results = diagnostics.run();

// This will have to be updated with each new test
expect(results.length).to.equal(4);
expect(results.length).to.equal(5);
expect(childProcessExecStub.called).to.be.true;
expect(lifecycleEmitSpy.called).to.be.true;
expect(lifecycleEmitSpy.args[0][0]).to.equal('Doctor:diagnostic');
expect(lifecycleEmitSpy.args[0][1]).to.have.property('testName');
expect(lifecycleEmitSpy.args[0][1]).to.have.property('status');
});

describe('networkCheck', () => {
it('passes when all URLs can be reached', async () => {
stubMethod(sandbox, Connection.prototype, 'request').resolves('{}');

const dr = Doctor.init(oclifConfig);
const diagnostics = new Diagnostics(dr, oclifConfig);
await diagnostics.networkCheck();

expect(drAddSuggestionSpy.called).to.be.false;
expect(lifecycleEmitSpy.called).to.be.true;
expect(lifecycleEmitSpy.args[0][1]).to.deep.equal({
status: 'pass',
testName: 'can access: https://test.salesforce.com',
});
});

it('fails when one URL cannot be reached', async () => {
stubMethod(sandbox, Connection.prototype, 'request').rejects(
'{"error":"unsupported_grant_type","error_description":"grant type not supported"}'
);

const dr = Doctor.init(oclifConfig);
const diagnostics = new Diagnostics(dr, oclifConfig);
await diagnostics.networkCheck();

expect(drAddSuggestionSpy.called).to.be.true;
expect(lifecycleEmitSpy.called).to.be.true;
expect(lifecycleEmitSpy.args[0][1]).to.deep.equal({
status: 'fail',
testName: "can't access: https://test.salesforce.com",
});
});

it('fails when one URL cannot be reached and others can be', async () => {
stubMethod(sandbox, Connection.prototype, 'request')
.onFirstCall()
.resolves('{}')
.rejects('{"error":"unsupported_grant_type","error_description":"grant type not supported"}');

const dr = Doctor.init(oclifConfig);
const diagnostics = new Diagnostics(dr, oclifConfig);
await diagnostics.networkCheck();

expect(drAddSuggestionSpy.called).to.be.true;
expect(lifecycleEmitSpy.called).to.be.true;
expect(lifecycleEmitSpy.args[0][1]).to.deep.equal({
status: 'pass',
testName: 'can access: https://test.salesforce.com',
});
expect(lifecycleEmitSpy.args[1][1]).to.deep.equal({
status: 'fail',
testName: "can't access: https://appexchange.salesforce.com/services/data",
});
});
});

describe('outdatedCliVersionCheck', () => {
it('passes when CLI version is equal to latest', async () => {
childProcessExecStub.callsFake((cmdString, opts, cb: (e: unknown, stdout: unknown, stderr: unknown) => void) => {
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1388,7 +1388,7 @@
strip-ansi "6.0.1"
ts-retry-promise "^0.8.0"

"@salesforce/core@^7.3.1", "@salesforce/core@^7.3.3", "@salesforce/core@^7.3.5", "@salesforce/core@^7.3.6", "@salesforce/core@^7.3.8":
"@salesforce/core@^7.3.3", "@salesforce/core@^7.3.5", "@salesforce/core@^7.3.6", "@salesforce/core@^7.3.8":
version "7.3.8"
resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-7.3.8.tgz#8a646b5321f08c0fb4d22e2fa8b1d60b3a20df9b"
integrity sha512-VWhXHfjwjtC3pJWYp8wt5/fnNQ5tK61ovMG5eteXzVD2oFd7og1f6YjwuAzoYIZK7kYWWv7KJfGtCsPs7Zw+Ww==
Expand Down

0 comments on commit 4e9f345

Please sign in to comment.