Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add tests for utils #33

Merged
merged 16 commits into from
Feb 15, 2024
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ yarn-error.log
.husky/_/husky.sh
yarn.lock
LICENSE
coverage
4 changes: 2 additions & 2 deletions src/processor.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import fs from 'fs/promises';
import { Validator } from './validator';
import { SourceUnit, FunctionDefinition, ContractDefinition } from 'solc-typed-ast';
import { NodeToProcess } from './types';
import { getLineNumberFromSrc, parseNodeNatspec } from './utils';
import { SourceUnit, FunctionDefinition, ContractDefinition } from 'solc-typed-ast';

interface IWarning {
export interface IWarning {
location: string;
messages: string[];
}
Expand Down
19 changes: 7 additions & 12 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs/promises';
import path from 'path';
import { ASTKind, ASTReader, SourceUnit, compileSol, FunctionDefinition } from 'solc-typed-ast';
import { Natspec, NatspecDefinition, NodeToProcess } from './types';
import { ASTKind, ASTReader, SourceUnit, compileSol, FunctionDefinition } from 'solc-typed-ast';

export async function getSolidityFilesAbsolutePaths(files: string[]): Promise<string[]> {
return files.filter((file) => file.endsWith('.sol')).map((file) => path.resolve(file));
Expand All @@ -26,11 +26,6 @@ export async function getProjectCompiledSources(rootPath: string, includedPaths:
);
}

export async function getFileCompiledSource(filePath: string): Promise<SourceUnit> {
const compiledFile = await compileSol(filePath, 'auto');
return new ASTReader().read(compiledFile.data, ASTKind.Any, compiledFile.files)[0];
}

export function isFileInDirectory(directory: string, filePath: string): boolean {
// Convert both paths to absolute and normalize them
const absoluteDirectoryPath = path.resolve(directory) + path.sep;
Expand All @@ -43,11 +38,11 @@ export function isFileInDirectory(directory: string, filePath: string): boolean
export async function getRemappings(rootPath: string): Promise<string[]> {
// First try the remappings.txt file
try {
return await getRemappingsFromFile(path.join(rootPath, 'remappings.txt'));
return await exports.getRemappingsFromFile(path.join(rootPath, 'remappings.txt'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the exports achieve?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the exports achieve?

It makes the mocks possible in tests. I know that a change source code for tests is a bad practice, but the other ways to use mocks in that case are pretty dirty: https://medium.com/welldone-software/jest-how-to-mock-a-function-call-inside-a-module-21c05c57a39f

} catch (e) {
// If the remappings file does not exist, try foundry.toml
try {
return await getRemappingsFromConfig(path.join(rootPath, 'foundry.toml'));
return await exports.getRemappingsFromConfig(path.join(rootPath, 'foundry.toml'));
} catch {
return [];
}
Expand All @@ -66,14 +61,14 @@ export async function getRemappingsFromFile(remappingsPath: string): Promise<str

export async function getRemappingsFromConfig(foundryConfigPath: string): Promise<string[]> {
const foundryConfigContent = await fs.readFile(foundryConfigPath, 'utf8');
const regex = /\n+remappings[\s|\n]*\=[\s\n]*\[\n*\s*(?<remappings>[a-zA-Z-="'\/_,\n\s]+)/;
const regex = /remappings[\s|\n]*\=[\s\n]*\[(?<remappings>[^\]]+)]/;
gas1cent marked this conversation as resolved.
Show resolved Hide resolved
const matches = foundryConfigContent.match(regex);
if (matches) {
return matches
.groups!.remappings.split('\n')
.groups!.remappings.split(',')
.map((line) => line.trim())
.filter((line) => line.length)
.map((line) => line.replace(',', ''));
.map((line) => line.replace(/["']/g, ''))
.filter((line) => line.length);
} else {
return [];
}
Expand Down
10 changes: 5 additions & 5 deletions src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ export class Validator {
let alerts: string[] = [];
const counter = getElementFrequency(natspecParams);

for (let paramName of members) {
if (!natspecParams.includes(paramName)) {
alerts.push(`@param ${paramName} is missing`);
} else if (counter[paramName] > 1) {
alerts.push(`@param ${paramName} is duplicated`);
for (let memberName of members) {
if (!natspecParams.includes(memberName)) {
alerts.push(`@param ${memberName} is missing`);
} else if (counter[memberName] > 1) {
alerts.push(`@param ${memberName} is duplicated`);
}
}

Expand Down
7 changes: 7 additions & 0 deletions test/contracts/BasicSample.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ contract BasicSample is AbstractBasic {
*/
bytes32 internal constant _EMPTY_STRING = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;

/**
* @notice A public state variable
*/
uint256 public somePublicNumber;

constructor(bool _randomFlag) {}

/**
* @notice External function that returns a bool
* @dev A dev comment
Expand Down
58 changes: 24 additions & 34 deletions test/parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ContractDefinition } from 'solc-typed-ast';
import { getFileCompiledSource } from './utils';
import { parseNodeNatspec } from '../src/utils';
import { mockNatspec } from './mocks';
import { getFileCompiledSource, findNode } from './utils/helpers';
import { mockNatspec } from './utils/mocks';

describe('Parser', () => {
let contract: ContractDefinition;
Expand All @@ -13,7 +13,7 @@ describe('Parser', () => {
});

it('should parse the inheritdoc tag', async () => {
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionNoParams')!;
const node = findNode(contract.vFunctions, 'viewFunctionNoParams');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -30,7 +30,7 @@ describe('Parser', () => {
});

it('should parse constant', async () => {
const node = contract.vStateVariables.find(({ name }) => name === 'SOME_CONSTANT')!;
const node = findNode(contract.vStateVariables, 'SOME_CONSTANT');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -43,7 +43,7 @@ describe('Parser', () => {
});

it('should parse variable', async () => {
const node = contract.vStateVariables.find(({ name }) => name === 'someVariable')!;
const node = findNode(contract.vStateVariables, 'someVariable');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -56,7 +56,7 @@ describe('Parser', () => {
});

it('should parse modifier', async () => {
const node = contract.vModifiers.find(({ name }) => name === 'someModifier')!;
const node = findNode(contract.vModifiers, 'someModifier');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -78,7 +78,7 @@ describe('Parser', () => {
});

it('should parse external function', async () => {
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionNoParams')!;
const node = findNode(contract.vFunctions, 'viewFunctionNoParams');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -97,7 +97,7 @@ describe('Parser', () => {
});

it('should parse private function', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewPrivate')!;
const node = findNode(contract.vFunctions, '_viewPrivate');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand Down Expand Up @@ -129,7 +129,7 @@ describe('Parser', () => {
});

it('should parse multiline descriptions', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewMultiline')!;
const node = findNode(contract.vFunctions, '_viewMultiline');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -145,7 +145,7 @@ describe('Parser', () => {
});

it('should parse multiple of the same tag', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewDuplicateTag')!;
const node = findNode(contract.vFunctions, '_viewDuplicateTag');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -172,7 +172,7 @@ describe('Parser', () => {
});

it('should parse error', async () => {
const node = contract.vErrors.find(({ name }) => name === 'SimpleError')!;
const node = findNode(contract.vErrors, 'SimpleError');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -188,7 +188,7 @@ describe('Parser', () => {
});

it('should parse event', async () => {
const node = contract.vEvents.find(({ name }) => name === 'SimpleEvent')!;
const node = findNode(contract.vEvents, 'SimpleEvent');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -204,7 +204,7 @@ describe('Parser', () => {
});

it('should parse struct', async () => {
const node = contract.vStructs.find(({ name }) => name === 'SimplestStruct')!;
const node = findNode(contract.vStructs, 'SimplestStruct');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -231,18 +231,8 @@ describe('Parser', () => {
);
});

// TODO: Parse natspec for enums
// it('should parse enum', async () => {
// const node = contract.vEnums.find(({ name }) => name === 'SimpleEnum')!;
// const result = parseNodeNatspec(node);

// expect(result).toEqual(mockNatspec({
// tags: [],
// }));
// });

it('should parse external function without parameters', async () => {
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionNoParams')!;
const node = findNode(contract.vFunctions, 'viewFunctionNoParams');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -268,7 +258,7 @@ describe('Parser', () => {
});

it('should parse external function with parameters', async () => {
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionWithParams')!;
const node = findNode(contract.vFunctions, 'viewFunctionWithParams');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand Down Expand Up @@ -307,7 +297,7 @@ describe('Parser', () => {
});

it('should parse struct', async () => {
const node = contract.vStructs.find(({ name }) => name === 'SimpleStruct')!;
const node = findNode(contract.vStructs, 'SimpleStruct');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -318,7 +308,7 @@ describe('Parser', () => {
});

it('should parse inheritdoc + natspec', async () => {
const node = contract.vStateVariables.find(({ name }) => name === 'someVariable')!;
const node = findNode(contract.vStateVariables, 'someVariable');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -337,21 +327,21 @@ describe('Parser', () => {
});

it('should not parse the inheritdoc tag with just 2 slashes', async () => {
const node = contract.vStateVariables.find(({ name }) => name === 'SOME_CONSTANT')!;
const node = findNode(contract.vStateVariables, 'SOME_CONSTANT');
const result = parseNodeNatspec(node);

expect(result).toEqual(mockNatspec({}));
});

it('should not parse regular comments as natspec', async () => {
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionWithParams')!;
const node = findNode(contract.vFunctions, 'viewFunctionWithParams');
const result = parseNodeNatspec(node);

expect(result).toEqual(mockNatspec({}));
});

it('should parse natspec with multiple spaces', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewPrivate')!;
const node = findNode(contract.vFunctions, '_viewPrivate');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand Down Expand Up @@ -379,14 +369,14 @@ describe('Parser', () => {
});

it('should not parse natspec with invalid number of slashes', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewInternal')!;
const node = findNode(contract.vFunctions, '_viewInternal');
const result = parseNodeNatspec(node);

expect(result).toEqual(mockNatspec({}));
});

it('should parse block natspec with invalid formatting', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewBlockLinterFail')!;
const node = findNode(contract.vFunctions, '_viewBlockLinterFail');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -402,7 +392,7 @@ describe('Parser', () => {
});

it('should parse block natspec with invalid formatting', async () => {
const node = contract.vFunctions.find(({ name }) => name === '_viewLinterFail')!;
const node = findNode(contract.vFunctions, '_viewLinterFail');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand All @@ -422,7 +412,7 @@ describe('Parser', () => {
});

it('should correctly parse empty return tag', async () => {
const node = contract.vFunctions.find(({ name }) => name === 'functionUnnamedEmptyReturn')!;
const node = findNode(contract.vFunctions, 'functionUnnamedEmptyReturn');
const result = parseNodeNatspec(node);

expect(result).toEqual(
Expand Down
Loading
Loading