Skip to content

Commit

Permalink
fix(results): limit rows of results into 1000 characters (#101)
Browse files Browse the repository at this point in the history
* fix(results): limit rows of results into 1000 characters

- Avoids including 100K's of minified code into results

* test: add unit tests for worker-task
  • Loading branch information
AriPerkkio committed May 4, 2021
1 parent ae0cd55 commit 14c170e
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 3 deletions.
19 changes: 16 additions & 3 deletions lib/engine/worker-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const UNKNOWN_RULE_ID = 'unable-to-parse-rule-id';
const LINE_REGEX = /while linting <text>:(([0-9]+)?)/;

const MAX_LINT_TIME_SECONDS = 5;
const MAX_ROW_LENGTH = 1000;

/**
* Create error message for LintMessage results
Expand Down Expand Up @@ -103,7 +104,10 @@ function getMessageReducer(repository: string) {
*/
function constructCodeFrame(
source: ESLint.LintResult['source'],
message: Linter.LintMessage
message: Pick<
Linter.LintMessage,
'line' | 'column' | 'endLine' | 'endColumn'
>
): Linter.LintMessage['source'] {
if (!source) return undefined;

Expand All @@ -115,7 +119,16 @@ function constructCodeFrame(
location.end = { line: message.endLine, column: message.endColumn };
}

return codeFrameColumns(source, location);
const rows = codeFrameColumns(source, location).split('\n');
const limitedRows = rows.map(row => {
if (row.length > MAX_ROW_LENGTH) {
return row.slice(0, MAX_ROW_LENGTH - 3) + '...';
}

return row;
});

return limitedRows.join('\n');
}

/**
Expand All @@ -134,7 +147,7 @@ function parseErrorStack(error: Error, file: SourceFile): LintMessage {

// Include erroneous line to source when line was successfully parsed from the stack
const source =
line > 0 ? codeFrameColumns(content, { start: { line } }) : undefined;
line > 0 ? constructCodeFrame(content, { line, column: 0 }) : undefined;

return createErrorMessage({
path,
Expand Down
1 change: 1 addition & 0 deletions test/unit/__mocks__/@config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const DEFAULT_MOCK_CONFIG = {
repositories: ['mock-repo-1', 'mock-repo-2', 'mock-repo-3'],
logLevel: 'verbose',
resultParser: 'markdown',
rulesUnderTesting: ['mock-rule-id'],
};

export const mockConfig = jest.fn().mockReturnValue(DEFAULT_MOCK_CONFIG);
Expand Down
15 changes: 15 additions & 0 deletions test/unit/__mocks__/@file-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { SourceFile } from '@file-client';

export const writeResults = jest.fn();
export const prepareResultsDirectory = jest.fn();
export const removeCachedRepository = jest.fn();
export const getFilesMock = jest.fn();

export const ResultsStore = {
getResults: jest.fn().mockReturnValue(['MOCK_RESULT']),
};

export const sourceFiles: SourceFile[] = [
{ content: '/'.repeat(2000), path: './mock/path/file.ts' },
];

export async function getFiles(options: unknown): Promise<SourceFile[]> {
getFilesMock(options);

return sourceFiles;
}
34 changes: 34 additions & 0 deletions test/unit/__mocks__/eslint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as actual from 'eslint';

type LintResult = actual.ESLint.LintResult;

export class ESLint {
static mockConstructor = jest.fn();

constructor(config: unknown) {
ESLint.mockConstructor(config);
}

async lintText(source: string): Promise<LintResult[]> {
return [
{
filePath: './mock/path/file.ts',
errorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0,
usedDeprecatedRules: [],
source,
messages: [
{
line: 1,
column: 0,
ruleId: 'mock-rule-id',
message: 'mock-message',
severity: 2,
},
],
},
];
}
}
3 changes: 3 additions & 0 deletions test/unit/__mocks__/worker_threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ export class Worker {
listeners[event].push(listener);
}
}

export const parentPort = { postMessage: jest.fn() };
export const workerData = { repository: 'mock-repository' };
1 change: 1 addition & 0 deletions test/unit/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@config/types';

jest.unmock('@config');
jest.unmock('eslint');

const DEFAULT_CONFIGURATION: ConfigWithOptionals = {
repositories: ['test-repo', 'test-repot-2'],
Expand Down
47 changes: 47 additions & 0 deletions test/unit/worker-task.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import workerTask, { WorkerMessage } from '@engine/worker-task';
import { parentPort } from '__mocks__/worker_threads';
import { sourceFiles } from '__mocks__/@file-client';

jest.mock('worker_threads', () => require('./__mocks__/worker_threads'));

function getPostMessageCalls<Type extends WorkerMessage['type']>(
type?: Type
): (WorkerMessage & { type: Type })[] {
const calls = parentPort.postMessage.mock.calls.map(call => {
const [message] = call;
return message;
});

return calls.filter(call => {
if (type) return call.type === type;
return call;
});
}

describe('engine/worker-task', () => {
beforeEach(() => {
parentPort.postMessage.mockClear();
});

test('should limit length of results to 1000 characters', async () => {
await workerTask();

const resultMessages = getPostMessageCalls('ON_RESULT');
expect(resultMessages).toHaveLength(1);

const { messages } = resultMessages[0].payload;
expect(messages).toHaveLength(1);

const [message] = messages;
const { source } = message;

// Original source should be 2000 lines
expect(sourceFiles[0].content).toHaveLength(2000);

// Final result should be limited to 1000 characters, + anything generated by @babel/code-frame
expect(source!.length).toBeLessThanOrEqual(1050);

// Last three characters should be '...'
expect(source!.slice(-3)).toBe('...');
});
});

0 comments on commit 14c170e

Please sign in to comment.