From a5aef5a423aad09ad9b5bdb8118235bb0ef485d2 Mon Sep 17 00:00:00 2001 From: peternhale Date: Thu, 28 Mar 2024 19:22:56 +0000 Subject: [PATCH] fix: guard against undefined test results (#354) * fix: guard against undefined test results @W-15298950@ Account for a undefined ApexTestRunResult when formatting results * chore: apply review suggestions and write test * chore: remove possibility of returning undefined * chore: address review issues * chore: fix typo * chore: merge main --- src/execute/executeService.ts | 1 + src/i18n/i18n.ts | 3 +- src/i18n/index.ts | 4 +- src/streaming/streamingClient.ts | 13 +- src/tests/asyncTests.ts | 75 +++---- src/tests/testService.ts | 8 +- src/tests/types.ts | 12 +- src/utils/dateUtil.ts | 6 +- src/utils/elapsedTime.ts | 3 +- test/tests/asyncTests.test.ts | 372 ++++++++++++++----------------- tsconfig.json | 3 +- 11 files changed, 228 insertions(+), 272 deletions(-) diff --git a/src/execute/executeService.ts b/src/execute/executeService.ts index c838dbe2..ef5896f0 100644 --- a/src/execute/executeService.ts +++ b/src/execute/executeService.ts @@ -56,6 +56,7 @@ export class ExecuteService { } } } + throw new Error(nls.localize('authForAnonymousApexFailed')); } @elapsedTime() diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 0905d220..33968296 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -91,5 +91,6 @@ export const messages = { covIdFormatErr: 'Cannot specify code coverage with a TestRunId result', startHandshake: 'Attempting StreamingClient handshake', finishHandshake: 'Finished StreamingClient handshake', - subscribeStarted: 'Subscribing to ApexLog events' + subscribeStarted: 'Subscribing to ApexLog events', + authForAnonymousApexFailed: 'The authentication for execute anonymous failed' }; diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 77046ce6..2ebbc435 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -10,11 +10,11 @@ import { Localization, Message } from './localization'; function loadMessageBundle(): Message { try { - const layer = new Message(messages); - return layer; + return new Message(messages); } catch (e) { console.error('Cannot find messages in i18n module'); } + return undefined; } export const nls = new Localization(loadMessageBundle()); diff --git a/src/streaming/streamingClient.ts b/src/streaming/streamingClient.ts index 1b9c7e0b..166b5db8 100644 --- a/src/streaming/streamingClient.ts +++ b/src/streaming/streamingClient.ts @@ -9,8 +9,8 @@ import { Client } from 'faye'; import { Connection, LoggerLevel } from '@salesforce/core'; import { RetrieveResultsInterval, - StreamingErrors, StreamMessage, + StreamingErrors, TestResultMessage } from './types'; import { Progress } from '../common'; @@ -19,7 +19,8 @@ import { elapsedTime, refreshAuth } from '../utils'; import { ApexTestProgressValue, ApexTestQueueItem, - ApexTestQueueItemRecord + ApexTestQueueItemRecord, + ApexTestQueueItemStatus } from '../tests/types'; const TEST_RESULT_CHANNEL = '/systemTopic/TestResult'; @@ -298,10 +299,10 @@ export class StreamingClient { if ( result.records.some( (item) => - item.Status === 'Queued' /* ApexTestQueueItemStatus.Queued */ || - item.Status === 'Holding' /* ApexTestQueueItemStatus.Holding */ || - item.Status === 'Preparing' /* ApexTestQueueItemStatus.Preparing */ || - item.Status === 'Processing' /* ApexTestQueueItemStatus.Processing */ + item.Status === ApexTestQueueItemStatus.Queued || + item.Status === ApexTestQueueItemStatus.Holding || + item.Status === ApexTestQueueItemStatus.Preparing || + item.Status === ApexTestQueueItemStatus.Processing ) ) { return null; diff --git a/src/tests/asyncTests.ts b/src/tests/asyncTests.ts index 899003dd..7cb4a88b 100644 --- a/src/tests/asyncTests.ts +++ b/src/tests/asyncTests.ts @@ -9,7 +9,7 @@ import { Connection } from '@salesforce/core'; import { CancellationToken, Progress } from '../common'; import { nls } from '../i18n'; import { AsyncTestRun, StreamingClient } from '../streaming'; -import { formatStartTime, getCurrentTime } from '../utils'; +import { elapsedTime, formatStartTime, getCurrentTime } from '../utils'; import { formatTestErrors, getAsyncDiagnostic } from './diagnosticUtil'; import { ApexTestProgressValue, @@ -20,7 +20,6 @@ import { ApexTestResultData, ApexTestResultOutcome, ApexTestRunResult, - ApexTestRunResultRecord, ApexTestRunResultStatus, AsyncTestArrayConfiguration, AsyncTestConfiguration, @@ -32,9 +31,16 @@ import * as util from 'util'; import { QUERY_RECORD_LIMIT } from './constants'; import { CodeCoverage } from './codeCoverage'; import { HttpRequest } from 'jsforce'; -import { elapsedTime } from '../utils/elapsedTime'; import { isValidTestRunID } from '../narrowing'; +const finishedStatuses = [ + ApexTestRunResultStatus.Aborted, + ApexTestRunResultStatus.Failed, + ApexTestRunResultStatus.Completed, + ApexTestRunResultStatus.Passed, + ApexTestRunResultStatus.Skipped +]; + export class AsyncTests { public readonly connection: Connection; private readonly codecoverage: CodeCoverage; @@ -83,12 +89,12 @@ export class AsyncTests { } const asyncRunResult = await sClient.subscribe(undefined, testRunId); - const testRunSummary = await this.checkRunStatus(asyncRunResult.runId); + const runResult = await this.checkRunStatus(asyncRunResult.runId); return await this.formatAsyncResults( asyncRunResult, getCurrentTime(), codeCoverage, - testRunSummary, + runResult.testRunSummary, progress ); } catch (e) { @@ -113,13 +119,13 @@ export class AsyncTests { await sClient.init(); await sClient.handshake(); let queueItem: ApexTestQueueItem; - let testRunSummary = await this.checkRunStatus(testRunId); + let runResult = await this.checkRunStatus(testRunId); - if (testRunSummary !== undefined) { + if (runResult.testsComplete) { queueItem = await sClient.handler(undefined, testRunId); } else { queueItem = (await sClient.subscribe(undefined, testRunId)).queueItem; - testRunSummary = await this.checkRunStatus(testRunId); + runResult = await this.checkRunStatus(testRunId); } token && @@ -135,7 +141,7 @@ export class AsyncTests { { queueItem, runId: testRunId }, getCurrentTime(), codeCoverage, - testRunSummary + runResult.testRunSummary ); } catch (e) { throw formatTestErrors(e); @@ -146,16 +152,15 @@ export class AsyncTests { public async checkRunStatus( testRunId: string, progress?: Progress - ): Promise { + ): Promise<{ + testsComplete: boolean; + testRunSummary: ApexTestRunResult; + }> { if (!isValidTestRunID(testRunId)) { throw new Error(nls.localize('invalidTestRunIdErr', testRunId)); } - let testRunSummaryQuery = - 'SELECT AsyncApexJobId, Status, ClassesCompleted, ClassesEnqueued, '; - testRunSummaryQuery += - 'MethodsEnqueued, StartTime, EndTime, TestTime, UserId '; - testRunSummaryQuery += `FROM ApexTestRunResult WHERE AsyncApexJobId = '${testRunId}'`; + const testRunSummaryQuery = `SELECT AsyncApexJobId, Status, ClassesCompleted, ClassesEnqueued, MethodsEnqueued, StartTime, EndTime, TestTime, UserId FROM ApexTestRunResult WHERE AsyncApexJobId = '${testRunId}'`; progress?.report({ type: 'FormatTestResultProgress', @@ -163,33 +168,21 @@ export class AsyncTests { message: nls.localize('retrievingTestRunSummary') }); - const testRunSummaryResults = (await this.connection.tooling.query( - testRunSummaryQuery, - { - autoFetch: true - } - )) as ApexTestRunResult; - - if (testRunSummaryResults.records.length === 0) { + try { + const testRunSummaryResults = + await this.connection.singleRecordQuery( + testRunSummaryQuery, + { + tooling: true + } + ); + return { + testsComplete: finishedStatuses.includes(testRunSummaryResults.Status), + testRunSummary: testRunSummaryResults + }; + } catch (e) { throw new Error(nls.localize('noTestResultSummary', testRunId)); } - - if ( - testRunSummaryResults.records[0].Status === - ApexTestRunResultStatus.Aborted || - testRunSummaryResults.records[0].Status === - ApexTestRunResultStatus.Failed || - testRunSummaryResults.records[0].Status === - ApexTestRunResultStatus.Completed || - testRunSummaryResults.records[0].Status === - ApexTestRunResultStatus.Passed || - testRunSummaryResults.records[0].Status === - ApexTestRunResultStatus.Skipped - ) { - return testRunSummaryResults.records[0]; - } - - return undefined; } /** @@ -206,7 +199,7 @@ export class AsyncTests { asyncRunResult: AsyncTestRun, commandStartTime: number, codeCoverage = false, - testRunSummary: ApexTestRunResultRecord, + testRunSummary: ApexTestRunResult, progress?: Progress ): Promise { const coveredApexClassIdSet = new Set(); diff --git a/src/tests/testService.ts b/src/tests/testService.ts index d006184f..02227613 100644 --- a/src/tests/testService.ts +++ b/src/tests/testService.ts @@ -378,11 +378,9 @@ export class TestService { try { if (tests) { const payload = await this.buildTestPayload(tests); - const classes = payload.tests?.map((testItem) => { - if (testItem.className) { - return testItem.className; - } - }); + const classes = payload.tests + ?.filter((testItem) => testItem.className) + .map((testItem) => testItem.className); if (new Set(classes).size !== 1) { throw new Error(nls.localize('syncClassErr')); } diff --git a/src/tests/types.ts b/src/tests/types.ts index 27a8a719..6886fc67 100644 --- a/src/tests/types.ts +++ b/src/tests/types.ts @@ -255,7 +255,7 @@ export const enum ApexTestRunResultStatus { Skipped = 'Skipped' } -export type ApexTestRunResultRecord = { +export type ApexTestRunResult = { /** * The parent Apex job ID for the result */ @@ -267,23 +267,17 @@ export type ApexTestRunResultRecord = { /** * The time at which the test run started. */ - StartTime: string; + StartTime: string | undefined; /** * The time it took the test to run, in seconds. */ - TestTime: number; + TestTime: number | undefined; /** * The user who ran the test run */ UserId: string; }; -export type ApexTestRunResult = { - done: boolean; - totalSize: number; - records: ApexTestRunResultRecord[]; -}; - export const enum ApexTestQueueItemStatus { Holding = 'Holding', Queued = 'Queued', diff --git a/src/utils/dateUtil.ts b/src/utils/dateUtil.ts index 3a0eebd3..9ee222f2 100644 --- a/src/utils/dateUtil.ts +++ b/src/utils/dateUtil.ts @@ -18,9 +18,13 @@ export function getCurrentTime(): number { * @returns formatted date and time */ export function formatStartTime( - startTime: string | number, + startTime: string | number | undefined, format: 'ISO' | 'locale' = 'locale' ): string { + if (!startTime) { + return ''; + } + const date = new Date(startTime); if (format === 'ISO') { return date.toISOString(); diff --git a/src/utils/elapsedTime.ts b/src/utils/elapsedTime.ts index 554c3b84..8a202d9f 100644 --- a/src/utils/elapsedTime.ts +++ b/src/utils/elapsedTime.ts @@ -7,8 +7,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-return */ -import { Logger, LoggerLevel } from '@salesforce/core'; -import { LoggerLevelValue } from '@salesforce/core/lib/logger/logger'; +import { Logger, LoggerLevel, LoggerLevelValue } from '@salesforce/core'; const log = ( level: LoggerLevelValue, diff --git a/test/tests/asyncTests.test.ts b/test/tests/asyncTests.test.ts index 474a1c86..4fee601f 100644 --- a/test/tests/asyncTests.test.ts +++ b/test/tests/asyncTests.test.ts @@ -135,7 +135,10 @@ describe('Run Apex tests asynchronously', () => { sandboxStub .stub(StreamingClient.prototype, 'subscribe') .resolves(asyncResult); - sandboxStub.stub(AsyncTests.prototype, 'checkRunStatus'); + sandboxStub.stub(AsyncTests.prototype, 'checkRunStatus').resolves({ + testsComplete: true, + testRunSummary: {} as ApexTestRunResult + }); const testSrv = new TestService(mockConnection); const mockTestResultData = sandboxStub .stub(AsyncTests.prototype, 'formatAsyncResults') @@ -171,22 +174,20 @@ describe('Run Apex tests asynchronously', () => { mockConnection.getAuthInfoFields().orgId; missingTimeTestData.summary.username = mockConnection.getUsername(); const asyncTestSrv = new AsyncTests(mockConnection); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); - mockToolingQuery.onSecondCall().resolves({ + mockToolingQuery.onFirstCall().resolves({ done: true, totalSize: 1, records: [ @@ -210,19 +211,19 @@ describe('Run Apex tests asynchronously', () => { } ] } as ApexTestResult); - const testRunSummary = await asyncTestSrv.checkRunStatus(testRunId); + const runResult = await asyncTestSrv.checkRunStatus(testRunId); const getTestResultData = await asyncTestSrv.formatAsyncResults( { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), undefined, - testRunSummary + runResult.testRunSummary ); let summaryQuery = 'SELECT AsyncApexJobId, Status, ClassesCompleted, ClassesEnqueued, '; summaryQuery += 'MethodsEnqueued, StartTime, EndTime, TestTime, UserId '; summaryQuery += `FROM ApexTestRunResult WHERE AsyncApexJobId = '${testRunId}'`; - expect(mockToolingQuery.getCall(0).args[0]).to.equal(summaryQuery); + expect(mockSingleRecordQuery.getCall(0).args[0]).to.equal(summaryQuery); let testResultQuery = 'SELECT Id, QueueItemId, StackTrace, Message, '; testResultQuery += @@ -230,27 +231,25 @@ describe('Run Apex tests asynchronously', () => { testResultQuery += 'ApexClass.Id, ApexClass.Name, ApexClass.NamespacePrefix '; testResultQuery += `FROM ApexTestResult WHERE QueueItemId IN ('${pollResponse.records[0].Id}')`; - expect(mockToolingQuery.getCall(1).args[0]).to.equal(testResultQuery); + expect(mockToolingQuery.getCall(0).args[0]).to.equal(testResultQuery); expect(getTestResultData).to.deep.equals(missingTimeTestData); }); it('should report progress when checking test summary for run', async () => { const asyncTestSrv = new AsyncTests(mockConnection); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); - mockToolingQuery.onSecondCall().resolves({ done: true, totalSize: 1, records: [ @@ -293,22 +292,20 @@ describe('Run Apex tests asynchronously', () => { skippedTestData.summary.orgId = mockConnection.getAuthInfoFields().orgId; skippedTestData.summary.username = mockConnection.getUsername(); const asyncTestSrv = new AsyncTests(mockConnection); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); - mockToolingQuery.onSecondCall().resolves({ + mockToolingQuery.onFirstCall().resolves({ done: true, totalSize: 1, records: [ @@ -333,19 +330,19 @@ describe('Run Apex tests asynchronously', () => { ] } as ApexTestResult); - const testRunSummary = await asyncTestSrv.checkRunStatus(testRunId); + const runResult = await asyncTestSrv.checkRunStatus(testRunId); const getTestResultData = await asyncTestSrv.formatAsyncResults( { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), false, - testRunSummary + runResult.testRunSummary ); let summaryQuery = 'SELECT AsyncApexJobId, Status, ClassesCompleted, ClassesEnqueued, '; summaryQuery += 'MethodsEnqueued, StartTime, EndTime, TestTime, UserId '; summaryQuery += `FROM ApexTestRunResult WHERE AsyncApexJobId = '${testRunId}'`; - expect(mockToolingQuery.getCall(0).args[0]).to.equal(summaryQuery); + expect(mockSingleRecordQuery.getCall(0).args[0]).to.equal(summaryQuery); let testResultQuery = 'SELECT Id, QueueItemId, StackTrace, Message, '; testResultQuery += @@ -353,7 +350,7 @@ describe('Run Apex tests asynchronously', () => { testResultQuery += 'ApexClass.Id, ApexClass.Name, ApexClass.NamespacePrefix '; testResultQuery += `FROM ApexTestResult WHERE QueueItemId IN ('${pollResponse.records[0].Id}')`; - expect(mockToolingQuery.getCall(1).args[0]).to.equal(testResultQuery); + expect(mockToolingQuery.getCall(0).args[0]).to.equal(testResultQuery); expect(getTestResultData).to.deep.equals(skippedTestData); }); @@ -361,22 +358,20 @@ describe('Run Apex tests asynchronously', () => { diagnosticResult.summary.orgId = mockConnection.getAuthInfoFields().orgId; diagnosticResult.summary.username = mockConnection.getUsername(); const asyncTestSrv = new AsyncTests(mockConnection); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); - mockToolingQuery.onSecondCall().resolves({ + mockToolingQuery.onFirstCall().resolves({ done: true, totalSize: 1, records: [ @@ -401,12 +396,12 @@ describe('Run Apex tests asynchronously', () => { ] } as ApexTestResult); - const testRunSummary = await asyncTestSrv.checkRunStatus(testRunId); + const runResult = await asyncTestSrv.checkRunStatus(testRunId); const getTestResultData = await asyncTestSrv.formatAsyncResults( { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), false, - testRunSummary + runResult.testRunSummary ); expect(getTestResultData).to.deep.equals(diagnosticResult); @@ -419,22 +414,20 @@ describe('Run Apex tests asynchronously', () => { diagnosticFailure.tests[0].diagnostic.exceptionStackTrace = undefined; diagnosticFailure.tests[0].stackTrace = undefined; const asyncTestSrv = new AsyncTests(mockConnection); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); - mockToolingQuery.onSecondCall().resolves({ + mockToolingQuery.onFirstCall().resolves({ done: true, totalSize: 1, records: [ @@ -459,12 +452,12 @@ describe('Run Apex tests asynchronously', () => { ] } as ApexTestResult); - const testRunSummary = await asyncTestSrv.checkRunStatus(testRunId); + const runResult = await asyncTestSrv.checkRunStatus(testRunId); const getTestResultData = await asyncTestSrv.formatAsyncResults( { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), false, - testRunSummary + runResult.testRunSummary ); expect(getTestResultData).to.deep.equals(diagnosticFailure); @@ -472,20 +465,19 @@ describe('Run Apex tests asynchronously', () => { it('should return an error if no test results are found', async () => { const asyncTestSrv = new AsyncTests(mockConnection); - const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 0, - records: [] - } as ApexTestRunResult); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); + mockSingleRecordQuery.onFirstCall().throwsException('No records found'); try { - const testRunSummary = await asyncTestSrv.checkRunStatus(testRunId); + const runResult = await asyncTestSrv.checkRunStatus(testRunId); await asyncTestSrv.formatAsyncResults( { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), false, - testRunSummary + runResult.testRunSummary ); fail('Test should have thrown an error'); } catch (e) { @@ -498,12 +490,11 @@ describe('Run Apex tests asynchronously', () => { it('should return an error if invalid test run id was provided', async () => { const invalidId = '000000xxxxx'; const asyncTestSrv = new AsyncTests(mockConnection); - const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 0, - records: [] - } as ApexTestRunResult); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); + mockSingleRecordQuery.onFirstCall().resolves(undefined); try { await asyncTestSrv.checkRunStatus(invalidId); @@ -512,19 +503,18 @@ describe('Run Apex tests asynchronously', () => { expect(e.message).to.equal( nls.localize('invalidTestRunIdErr', invalidId) ); - expect(mockToolingQuery.notCalled).to.be.true; + expect(mockSingleRecordQuery.notCalled).to.be.true; } }); it('should return an error if invalid test run id prefix was provided', async () => { const invalidId = '708000000xxxxxx'; const asyncTestSrv = new AsyncTests(mockConnection); - const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 0, - records: [] - } as ApexTestRunResult); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); + mockSingleRecordQuery.onFirstCall().resolves(undefined); try { await asyncTestSrv.checkRunStatus(invalidId); @@ -533,7 +523,7 @@ describe('Run Apex tests asynchronously', () => { expect(e.message).to.equal( nls.localize('invalidTestRunIdErr', invalidId) ); - expect(mockToolingQuery.notCalled).to.be.true; + expect(mockSingleRecordQuery.notCalled).to.be.true; } }); @@ -543,40 +533,37 @@ describe('Run Apex tests asynchronously', () => { mockConnection.tooling, 'query' ); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: '2020-07-12T02:54:47.000+0000', + TestTime: 1765, + UserId: '005xx000000abcDAAU' + }); mockToolingAutoQuery.onCall(0).resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: '2020-07-12T02:54:47.000+0000', - TestTime: 1765, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); - - mockToolingAutoQuery.onCall(1).resolves({ done: true, totalSize: 6, records: mixedTestResults } as ApexTestResult); - mockToolingAutoQuery.onCall(2).resolves({ + mockToolingAutoQuery.onCall(1).resolves({ done: true, totalSize: 3, records: mixedPerClassCodeCoverage } as ApexCodeCoverage); - mockToolingAutoQuery.onCall(3).resolves({ + mockToolingAutoQuery.onCall(2).resolves({ done: true, totalSize: 3, records: codeCoverageQueryResult } as ApexCodeCoverageAggregate); - mockToolingAutoQuery.onCall(4).resolves({ + mockToolingAutoQuery.onCall(3).resolves({ done: true, totalSize: 1, records: [ @@ -586,12 +573,12 @@ describe('Run Apex tests asynchronously', () => { ] } as ApexOrgWideCoverage); - const testRunSummary = await asyncTestSrv.checkRunStatus(testRunId); + const runResult = await asyncTestSrv.checkRunStatus(testRunId); const getTestResultData = await asyncTestSrv.formatAsyncResults( { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), true, - testRunSummary + runResult.testRunSummary ); // verify summary data @@ -614,40 +601,38 @@ describe('Run Apex tests asynchronously', () => { it('should report progress for aggregating code coverage', async () => { const asyncTestSrv = new AsyncTests(mockConnection); + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' + ); const mockToolingQuery = sandboxStub.stub(mockConnection.tooling, 'query'); - mockToolingQuery.onCall(0).resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: '2020-07-12T02:54:47.000+0000', - TestTime: 1765, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + mockSingleRecordQuery.onCall(0).resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: '2020-07-12T02:54:47.000+0000', + TestTime: 1765, + UserId: '005xx000000abcDAAU' + }); - mockToolingQuery.onCall(1).resolves({ + mockToolingQuery.onCall(0).resolves({ done: true, totalSize: 6, records: mixedTestResults } as ApexTestResult); - mockToolingQuery.onCall(2).resolves({ + mockToolingQuery.onCall(1).resolves({ done: true, totalSize: 3, records: mixedPerClassCodeCoverage } as ApexCodeCoverage); - mockToolingQuery.onCall(3).resolves({ + mockToolingQuery.onCall(2).resolves({ done: true, totalSize: 3, records: codeCoverageQueryResult } as ApexCodeCoverageAggregate); - mockToolingQuery.onCall(4).resolves({ + mockToolingQuery.onCall(3).resolves({ done: true, totalSize: 1, records: [ @@ -662,7 +647,7 @@ describe('Run Apex tests asynchronously', () => { report: reportStub }; - const testRunSummary = await asyncTestSrv.checkRunStatus( + const runResult = await asyncTestSrv.checkRunStatus( testRunId, progressReporter ); @@ -670,7 +655,7 @@ describe('Run Apex tests asynchronously', () => { { queueItem: pollResponse, runId: testRunId }, new Date().getTime(), true, - testRunSummary, + runResult.testRunSummary, progressReporter ); @@ -1226,39 +1211,28 @@ describe('Run Apex tests asynchronously', () => { describe('Report Test Run Status', async () => { it('should subscribe to test run for run still in progress', async () => { const asyncTestSrv = new AsyncTests(mockConnection); - const mockToolingQuery = sandboxStub.stub( - mockConnection.tooling, - 'query' + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' ); - mockToolingQuery + sandboxStub.stub(mockConnection.tooling, 'query'); + mockSingleRecordQuery .onFirstCall() .resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Queued, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult) + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Queued, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }) .onSecondCall() .resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); const formatResultsStub = sandboxStub.stub( asyncTestSrv, 'formatAsyncResults' @@ -1289,7 +1263,7 @@ describe('Run Apex tests asynchronously', () => { await asyncTestSrv.reportAsyncResults(testRunId); - expect(mockToolingQuery.calledTwice).to.be.true; + expect(mockSingleRecordQuery.calledTwice).to.be.true; expect(formatResultsStub.calledOnce).to.be.true; expect(subscribeStub.calledOnce).to.be.true; expect(handlerStub.notCalled).to.be.true; @@ -1297,23 +1271,18 @@ describe('Run Apex tests asynchronously', () => { it('should query for test run results if run is complete', async () => { const asyncTestSrv = new AsyncTests(mockConnection); - const mockToolingQuery = sandboxStub.stub( - mockConnection.tooling, - 'query' + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' ); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + sandboxStub.stub(mockConnection.tooling, 'query'); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); const formatResultsStub = sandboxStub.stub( asyncTestSrv, 'formatAsyncResults' @@ -1344,7 +1313,7 @@ describe('Run Apex tests asynchronously', () => { await asyncTestSrv.reportAsyncResults(testRunId); - expect(mockToolingQuery.calledOnce).to.be.true; + expect(mockSingleRecordQuery.calledOnce).to.be.true; expect(formatResultsStub.calledOnce).to.be.true; expect(subscribeStub.notCalled).to.be.true; expect(handlerStub.calledOnce).to.be.true; @@ -1352,23 +1321,18 @@ describe('Run Apex tests asynchronously', () => { it('should format results with retrieved test run summary', async () => { const asyncTestSrv = new AsyncTests(mockConnection); - const mockToolingQuery = sandboxStub.stub( - mockConnection.tooling, - 'query' + const mockSingleRecordQuery = sandboxStub.stub( + mockConnection, + 'singleRecordQuery' ); - mockToolingQuery.onFirstCall().resolves({ - done: true, - totalSize: 1, - records: [ - { - AsyncApexJobId: testRunId, - Status: ApexTestRunResultStatus.Completed, - StartTime: testStartTime, - TestTime: null, - UserId: '005xx000000abcDAAU' - } - ] - } as ApexTestRunResult); + sandboxStub.stub(mockConnection.tooling, 'query'); + mockSingleRecordQuery.onFirstCall().resolves({ + AsyncApexJobId: testRunId, + Status: ApexTestRunResultStatus.Completed, + StartTime: testStartTime, + TestTime: null, + UserId: '005xx000000abcDAAU' + }); const formatResultsStub = sandboxStub.stub( asyncTestSrv, 'formatAsyncResults' diff --git a/tsconfig.json b/tsconfig.json index 384d6d22..78c1f9c2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,8 @@ "outDir": "lib", "preserveConstEnums": true, "skipLibCheck": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "noImplicitReturns": true }, "exclude": ["node_modules", "lib"] }