Skip to content

Commit

Permalink
test: add tests for utils (#33)
Browse files Browse the repository at this point in the history
Co-authored-by: Gas One Cent <[email protected]>
  • Loading branch information
dristpunk and gas1cent authored Feb 15, 2024
1 parent 607a8ed commit 050f258
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 102 deletions.
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'));
} 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>[^\]]+)]/;
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

0 comments on commit 050f258

Please sign in to comment.