Skip to content

Commit

Permalink
fix: unmanaged cancelled jobs
Browse files Browse the repository at this point in the history
- throw an error when the unmanaged c++ jobs are cancelled or contain error
- add tests
  • Loading branch information
danlucian committed May 20, 2022
1 parent a041d7d commit dca7769
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 3 deletions.
17 changes: 16 additions & 1 deletion src/lib/polling/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { sleep } from '../common';
import { ScanResult } from '../ecosystems/types';
import { ResolutionMeta } from './types';
import {
ResolutionMeta,
ResolveAndMonitorFactsResponse,
ResolveAndTestFactsResponse,
} from './types';
import { FailedToRunTestError } from '../errors';

export async function delayNextStep(
attemptsCount: number,
Expand Down Expand Up @@ -35,3 +40,13 @@ export function extractResolutionMetaFromScanResult({
targetReference,
};
}

export function handleProcessingStatus(
response: ResolveAndMonitorFactsResponse | ResolveAndTestFactsResponse,
): void {
if (response?.status === 'CANCELLED' || response?.status === 'ERROR') {
throw new FailedToRunTestError(
'Failed to process the project. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output',
);
}
}
5 changes: 4 additions & 1 deletion src/lib/polling/polling-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ResolveAndMonitorFactsResponse,
ResolveFactsState,
} from './types';
import { delayNextStep } from './common';
import { delayNextStep, handleProcessingStatus } from './common';
import {
generateProjectAttributes,
generateTags,
Expand Down Expand Up @@ -76,6 +76,9 @@ export async function pollingMonitorWithTokenUntilDone(
};

const response = await makeRequest<ResolveAndMonitorFactsResponse>(payload);

handleProcessingStatus(response);

if (response.ok && response.isMonitored) {
return response;
}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/polling/polling-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getAuthHeader } from '../api-token';
import { ScanResult } from '../ecosystems/types';

import { ResolveAndTestFactsResponse } from './types';
import { delayNextStep } from './common';
import { delayNextStep, handleProcessingStatus } from './common';
import { TestDependenciesResult } from '../snyk-test/legacy';

export async function requestTestPollingToken(
Expand Down Expand Up @@ -54,6 +54,8 @@ export async function pollingTestWithTokenUntilDone(

const response = await makeRequest<ResolveAndTestFactsResponse>(payload);

handleProcessingStatus(response);

if (response.result) {
const {
issues,
Expand Down
17 changes: 17 additions & 0 deletions test/jest/unit/lib/ecosystems/common.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { isUnmanagedEcosystem } from '../../../../../src/lib/ecosystems/common';
import { handleProcessingStatus } from '../../../../../src/lib/polling/common';
import { FailedToRunTestError } from '../../../../../src/lib/errors';

describe('isUnmanagedEcosystem fn', () => {
it.each`
Expand All @@ -13,3 +15,18 @@ describe('isUnmanagedEcosystem fn', () => {
},
);
});

describe('handleProcessingStatus fn', () => {
it.each`
actual | expected
${'CANCELLED'} | ${'Failed to process the project. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output'}
${'ERROR'} | ${'Failed to process the project. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output'}
`(
'should validate that given $actual as input, is considered or not an unmanaged ecosystem',
({ actual, expected }) => {
expect(() => {
handleProcessingStatus({ status: actual } as any);
}).toThrowError(new FailedToRunTestError(expected));
},
);
});
63 changes: 63 additions & 0 deletions test/jest/unit/lib/ecosystems/resolve-monitor.facts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { resolveAndMonitorFacts } from '../../../../../src/lib/ecosystems/resolv
import * as pluginAnalytics from '../../../../../src/lib/ecosystems/plugin-analytics';
import * as analytics from '../../../../../src/lib/analytics';
import * as httpClient from '../../../../../src/lib/request/promise';
import * as promise from '../../../../../src/lib/request/promise';

describe('resolve and test facts', () => {
afterEach(() => jest.restoreAllMocks());
Expand Down Expand Up @@ -82,6 +83,68 @@ describe('resolve and test facts', () => {
});
});

it('failing to resolve and monitor file-signatures fact for c/c++ projects when the job is cancelled', async () => {
const requestMonitorPollingTokenSpy = jest.spyOn(
pollingMonitor,
'requestMonitorPollingToken',
);
const makeRequestSpy = jest.spyOn(promise, 'makeRequest');

requestMonitorPollingTokenSpy.mockResolvedValueOnce({
token,
status: 'OK',
pollingTask,
});

makeRequestSpy.mockResolvedValueOnce({
token,
status: 'CANCELLED',
pollingTask,
});

const [testResults, errors] = await resolveAndMonitorFacts(
scanResults,
{} as Options,
);

expect(testResults).toEqual([]);
expect(errors[0]).toEqual({
error: 'Could not monitor dependencies in path',
path: 'path',
scanResult: {
analytics: [
{
data: {
totalFileSignatures: 3,
totalSecondsElapsedToGenerateFileSignatures: 0,
},
name: 'fileSignaturesAnalyticsContext',
},
],
facts: [
{
data: [
{
hashes_ffm: [
{ data: 'ucMc383nMM/wkFRM4iOo5Q', format: 1 },
{ data: 'k+DxEmslFQWuJsZFXvSoYw', format: 1 },
],
path: 'fastlz_example/fastlz.h',
},
],
type: 'fileSignatures',
},
],
identity: { type: 'cpp' },
name: 'my-unmanaged-c-project',
target: {
branch: 'master',
remoteUrl: 'https://github.com/some-org/some-unmanaged-project.git',
},
},
});
});

it('successfully resolves and monitor file-signatures fact for c/c++ projects', async () => {
const resolveAndTestFactsSpy = jest.spyOn(
pollingMonitor,
Expand Down
37 changes: 37 additions & 0 deletions test/jest/unit/lib/ecosystems/resolve-test-facts.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Options } from '../../../../../src/lib/types';
import * as pollingTest from '../../../../../src/lib/polling/polling-test';
import * as promise from '../../../../../src/lib/request/promise';
import { depGraphData, scanResults } from './fixtures/';
import { resolveAndTestFacts } from '../../../../../src/lib/ecosystems/resolve-test-facts';
import * as pluginAnalytics from '../../../../../src/lib/ecosystems/plugin-analytics';
Expand Down Expand Up @@ -50,6 +51,42 @@ describe('resolve and test facts', () => {
);
});

it.each`
actual | expected
${'CANCELLED'} | ${'Failed to process the project. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output'}
${'ERROR'} | ${'Failed to process the project. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output'}
`(
'should handle different file-signatures processing statuses for the testing flow',
async ({ actual, expected }) => {
const requestTestPollingTokenSpy = jest.spyOn(
pollingTest,
'requestTestPollingToken',
);
const makeRequestSpy = jest.spyOn(promise, 'makeRequest');

requestTestPollingTokenSpy.mockResolvedValueOnce({
token,
status: 'OK',
pollingTask,
});

makeRequestSpy.mockResolvedValueOnce({
token,
status: actual,
pollingTask,
});

const [testResults, errors] = await resolveAndTestFacts(
'cpp',
scanResults,
{} as Options,
);

expect(testResults).toEqual([]);
expect(errors[0]).toContain(expected);
},
);

it('successfully resolving and testing file-signatures fact for c/c++ projects', async () => {
const resolveAndTestFactsSpy = jest.spyOn(
pollingTest,
Expand Down

0 comments on commit dca7769

Please sign in to comment.