diff --git a/CHANGELOG.md b/CHANGELOG.md index f666c172..59d23848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [6.0.2] - 2025-09-18 + +๐Ÿงน `Chore`: `foundry-test-functions` is deprecated and will be removed in v7.0.0. Please rename to `foundry-test-function-naming`. +WILL BE REPLACED IN v7 + +

+ ## [6.0.1] - 2025-08-22 ๐Ÿ› ๏ธ `Fix`: `no-unused-vars` for modifiers diff --git a/conf/rulesets/solhint-all.js b/conf/rulesets/solhint-all.js index c251f20f..35178955 100644 --- a/conf/rulesets/solhint-all.js +++ b/conf/rulesets/solhint-all.js @@ -72,6 +72,7 @@ module.exports = Object.freeze({ 'const-name-snakecase': 'warn', 'contract-name-capwords': 'warn', 'event-name-capwords': 'warn', + 'foundry-test-function-naming': ['warn', ['setUp']], 'foundry-test-functions': ['warn', ['setUp']], 'func-name-mixedcase': 'warn', 'func-named-parameters': ['warn', 4], diff --git a/docs/rules.md b/docs/rules.md index 2bf7b701..fb8a9158 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -27,27 +27,28 @@ title: "Rule Index of Solhint" ## Style Guide Rules -| Rule Id | Error | Recommended | Deprecated | -| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------ | ---------- | -| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$โœ”๏ธ | | -| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$โœ”๏ธ | | -| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$โœ”๏ธ | | -| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$โœ”๏ธ | | -| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$โœ”๏ธ | | -| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases | | | -| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$โœ”๏ธ | | -| [func-named-parameters](./rules/naming/func-named-parameters.md) | Enforce named parameters for function calls with 4 or more arguments. This rule may have some false positives | | | -| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | | -| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$โœ”๏ธ | | -| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | | -| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | | -| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | | -| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$โœ”๏ธ | | -| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable names must be in mixedCase. (Does not check IMMUTABLES nor CONSTANTS (use inherent rules for that) | $~~~~~~~~$โœ”๏ธ | | -| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$โœ”๏ธ | | -| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | | -| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | | -| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$โœ”๏ธ | | +| Rule Id | Error | Recommended | Deprecated | +| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------------ | ---------- | +| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$โœ”๏ธ | | +| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$โœ”๏ธ | | +| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$โœ”๏ธ | | +| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$โœ”๏ธ | | +| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$โœ”๏ธ | | +| [foundry-test-function-naming](./rules/naming/foundry-test-function-naming.md) | Enforce naming convention on functions for Foundry test cases | | | +| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`) | | | +| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$โœ”๏ธ | | +| [func-named-parameters](./rules/naming/func-named-parameters.md) | Enforce named parameters for function calls with 4 or more arguments. This rule may have some false positives | | | +| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | | +| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$โœ”๏ธ | | +| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | | +| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | | +| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | | +| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$โœ”๏ธ | | +| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable names must be in mixedCase. (Does not check IMMUTABLES nor CONSTANTS (use inherent rules for that) | $~~~~~~~~$โœ”๏ธ | | +| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$โœ”๏ธ | | +| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | | +| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | | +| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$โœ”๏ธ | | ## Gas Consumption Rules diff --git a/docs/rules/naming/foundry-test-function-naming.md b/docs/rules/naming/foundry-test-function-naming.md new file mode 100644 index 00000000..e2efe3c7 --- /dev/null +++ b/docs/rules/naming/foundry-test-function-naming.md @@ -0,0 +1,79 @@ +--- +warning: "This is a dynamically generated file. Do not edit manually." +layout: "default" +title: "foundry-test-function-naming | Solhint" +--- + +# foundry-test-function-naming +![Category Badge](https://img.shields.io/badge/-Style%20Guide%20Rules-informational) +![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow) + +## Description +Enforce naming convention on functions for Foundry test cases + +## Options +This rule accepts an array of options: + +| Index | Description | Default Value | +| ----- | ----------------------------------------------------- | ------------- | +| 0 | Rule severity. Must be one of "error", "warn", "off". | warn | +| 1 | Array of required Foundry test hook function names. | setUp | + + +### Example Config +```json +{ + "rules": { + "foundry-test-function-naming": [ + "warn", + [ + "setUp" + ] + ] + } +} +``` + +### Notes +- This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped +- Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\w{1,}``` +- This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol``` +- This rule applies only to `external` and `public` functions +- This rule skips the `setUp()` function by default + +## Examples +### ๐Ÿ‘ Examples of **correct** code for this rule + +#### Foundry test case with correct Function declaration + +```solidity +function test_NumberIs42() public {} +``` + +#### Foundry test case with correct Function declaration + +```solidity +function testFail_Subtract43() public {} +``` + +#### Foundry test case with correct Function declaration + +```solidity +function testFuzz_FuzzyTest() public {} +``` + +### ๐Ÿ‘Ž Examples of **incorrect** code for this rule + +#### Foundry test case with incorrect Function declaration + +```solidity +function numberIs42() public {} +``` + +## Version +This rule was introduced in the latest version. + +## Resources +- [Rule source](https://github.com/protofire/solhint/blob/master/lib/rules/naming/foundry-test-function-naming.js) +- [Document source](https://github.com/protofire/solhint/blob/master/docs/rules/naming/foundry-test-function-naming.md) +- [Test cases](https://github.com/protofire/solhint/blob/master/test/rules/naming/foundry-test-function-naming.js) diff --git a/docs/rules/naming/foundry-test-functions.md b/docs/rules/naming/foundry-test-functions.md index 1fa0272d..c58a5afb 100644 --- a/docs/rules/naming/foundry-test-functions.md +++ b/docs/rules/naming/foundry-test-functions.md @@ -9,7 +9,7 @@ title: "foundry-test-functions | Solhint" ![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow) ## Description -Enforce naming convention on functions for Foundry test cases +Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`) ## Options This rule accepts an array of options: diff --git a/lib/common/identifier-naming.js b/lib/common/identifier-naming.js index 368490ae..b0850b55 100644 --- a/lib/common/identifier-naming.js +++ b/lib/common/identifier-naming.js @@ -32,14 +32,20 @@ module.exports = { }, isFoundryTestCase(text) { - // this one checks CamelCase after test keyword + // PREVIOUS VERSION + // โ†’ strict version: requires that after `test...` the first character must be uppercase (CamelCase enforced) // const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?[A-Z](Revert(If_|When_){1})?\w{1,}$/ + // PREVIOUS VERSION + // โ†’ strict version: requires that after `invariant` or `statefulFuzz` the first character must be uppercase (CamelCase enforced) + // const regexInvariant = /^(invariant|statefulFuzz)(_)?[A-Z]\w{1,}$/ + // this one checks test functions with optional suffixes (Fork, Fuzz, Fail, RevertIf_, RevertWhen_, etc.) + // now it only requires that after those, at least one word character exists (no CamelCase enforcement) const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?(Revert(If_|When_){1})?\w{1,}$/ const matchRegexTest = match(text, regexTest) - // this one checks CamelCase after test keyword - // const regexInvariant = /^(invariant|statefulFuzz)(_)?[A-Z]\w{1,}$/ + // this one checks invariant or statefulFuzz functions, with optional underscore, + // followed by at least one word character (no CamelCase enforcement) const regexInvariant = /^(invariant|statefulFuzz)(_)?\w{1,}$/ const matchRegexInvariant = match(text, regexInvariant) diff --git a/lib/rules/naming/foundry-test-function-naming.js b/lib/rules/naming/foundry-test-function-naming.js new file mode 100644 index 00000000..e7a995aa --- /dev/null +++ b/lib/rules/naming/foundry-test-function-naming.js @@ -0,0 +1,100 @@ +const BaseChecker = require('../base-checker') +const naming = require('../../common/identifier-naming') +const { severityDescription } = require('../../doc/utils') + +const DEFAULT_SEVERITY = 'warn' +const DEFAULT_SKIP_FUNCTIONS = ['setUp'] + +const ruleId = 'foundry-test-function-naming' +const meta = { + type: 'naming', + + docs: { + description: `Enforce naming convention on functions for Foundry test cases`, + category: 'Style Guide Rules', + options: [ + { + description: severityDescription, + default: DEFAULT_SEVERITY, + }, + { + description: 'Array of required Foundry test hook function names.', + default: DEFAULT_SKIP_FUNCTIONS, + }, + ], + examples: { + good: [ + { + description: 'Foundry test case with correct Function declaration', + code: 'function test_NumberIs42() public {}', + }, + { + description: 'Foundry test case with correct Function declaration', + code: 'function testFail_Subtract43() public {}', + }, + { + description: 'Foundry test case with correct Function declaration', + code: 'function testFuzz_FuzzyTest() public {}', + }, + ], + bad: [ + { + description: 'Foundry test case with incorrect Function declaration', + code: 'function numberIs42() public {}', + }, + ], + }, + notes: [ + { + note: 'This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped', + }, + { note: 'Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\\w{1,}```' }, + { + note: 'This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```', + }, + { + note: 'This rule applies only to `external` and `public` functions', + }, + { + note: 'This rule skips the `setUp()` function by default', + }, + ], + }, + + recommended: false, + defaultSetup: [DEFAULT_SEVERITY, DEFAULT_SKIP_FUNCTIONS], + + schema: { + type: 'array', + description: 'Array of function names to skip from the check', + items: { type: 'string', errorMessage: 'Each item must be a string' }, + }, +} + +class FoundryTestFunctionNaming extends BaseChecker { + constructor(reporter, config) { + super(reporter, ruleId, meta) + this.skippedFunctions = config + ? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS) + : DEFAULT_SKIP_FUNCTIONS + } + + FunctionDefinition(node) { + // function name should not be in skipped functions array + // should be external or public + if ( + !this.searchInArray(this.skippedFunctions, node.name) && + (node.visibility === 'public' || node.visibility === 'external') + ) { + if (!naming.isFoundryTestCase(node.name)) { + this.error(node, `Function ${node.name}() must match Foundry test naming convention`) + } + } + } + + searchInArray(array, searchString) { + return array.indexOf(searchString) !== -1 + } +} + +module.exports = FoundryTestFunctionNaming diff --git a/lib/rules/naming/foundry-test-functions.js b/lib/rules/naming/foundry-test-functions.js index 06996d7b..eb6127dc 100644 --- a/lib/rules/naming/foundry-test-functions.js +++ b/lib/rules/naming/foundry-test-functions.js @@ -10,7 +10,8 @@ const meta = { type: 'naming', docs: { - description: `Enforce naming convention on functions for Foundry test cases`, + description: + 'Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`)', category: 'Style Guide Rules', options: [ { @@ -77,6 +78,9 @@ class FoundryTestFunctionsChecker extends BaseChecker { this.skippedFunctions = config ? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS) : DEFAULT_SKIP_FUNCTIONS + + // avoid spamming the deprecation warning + this._deprecationWarned = false } FunctionDefinition(node) { @@ -87,7 +91,10 @@ class FoundryTestFunctionsChecker extends BaseChecker { (node.visibility === 'public' || node.visibility === 'external') ) { if (!naming.isFoundryTestCase(node.name)) { - this.error(node, `Function ${node.name}() must match Foundry test naming convention`) + this.error( + node, + `[DEPRECATED] rule. Use "foundry-test-function-naming". Function ${node.name}() must match Foundry test naming convention` + ) } } } diff --git a/lib/rules/naming/index.js b/lib/rules/naming/index.js index 8399b7f5..9d2bb79c 100644 --- a/lib/rules/naming/index.js +++ b/lib/rules/naming/index.js @@ -10,7 +10,11 @@ const VarNameMixedcaseChecker = require('./var-name-mixedcase') const NamedParametersMappingChecker = require('./named-parameters-mapping') const ImmutableVarsNamingChecker = require('./immutable-vars-naming') const FunctionNamedParametersChecker = require('./func-named-parameters') + +// ๐Ÿ‘‡ old (alias with deprecation) const FoundryTestFunctionsChecker = require('./foundry-test-functions') +// ๐Ÿ‘‡ new name +const FoundryTestFunctionNaming = require('./foundry-test-function-naming') module.exports = function checkers(reporter, config) { return [ @@ -26,6 +30,9 @@ module.exports = function checkers(reporter, config) { new NamedParametersMappingChecker(reporter), new ImmutableVarsNamingChecker(reporter, config), new FunctionNamedParametersChecker(reporter, config), + + // ๐Ÿ‘‡ call both new FoundryTestFunctionsChecker(reporter, config), + new FoundryTestFunctionNaming(reporter, config), ] } diff --git a/test/rules/gas-consumption/gas-named-return-values.js b/test/rules/gas-consumption/gas-named-return-values.js index 4b603c81..216edaca 100644 --- a/test/rules/gas-consumption/gas-named-return-values.js +++ b/test/rules/gas-consumption/gas-named-return-values.js @@ -76,6 +76,7 @@ describe('Linter - gas-named-return-values', () => { 'compiler-version': 'off', 'comprehensive-interface': 'off', 'foundry-test-functions': 'off', + 'foundry-test-function-naming': 'off', 'use-natspec': 'off', }, }) diff --git a/test/rules/naming/foundry-test-function-naming.js b/test/rules/naming/foundry-test-function-naming.js new file mode 100644 index 00000000..c016d170 --- /dev/null +++ b/test/rules/naming/foundry-test-function-naming.js @@ -0,0 +1,231 @@ +const assert = require('assert') +const linter = require('../../../lib/index') +const contractWith = require('../../common/contract-builder').contractWith +const { assertErrorCount, assertNoErrors, assertErrorMessage } = require('../../common/asserts') + +const ALLOWED_FUNCTION_NAMES = [ + 'test', + 'test_', + 'testFork_', + 'testFuzz_', + 'testFail_', + 'test_Revert_', + 'test_If_', + 'test_When_', + 'testFail_Revert_', + 'testFail_If_', + 'testFail_When_', + 'testFork_Revert_', + 'testFork_If_', + 'testFork_When_', + 'testFuzz_Revert_', + 'testFuzz_If_', + 'testFuzz_When_', + 'invariant', + 'invariant_', + 'invariantA', + 'statefulFuzz', + 'statefulFuzz_', +] + +const DISALLOWED_FUNCTION_NAMES = ['Test_', 'Test', '', 'any', 'setUp', 'other', '_'] + +const composeFunctionName = (prefix, name, visibility) => + 'function ' + prefix + name + ' ' + visibility + ' { testNumber = 42; }' + +describe('Linter - foundry-test-function-naming', () => { + for (const prefix of DISALLOWED_FUNCTION_NAMES) { + it(`should raise error for DISALLOWED_FUNCTION_NAMES [${prefix}] when PUBLIC`, () => { + const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'public') + const code = contractWith(functionDefinition) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertErrorCount(report, 1) + assertErrorMessage( + report, + `Function ${prefix + 'FunctionName()'} must match Foundry test naming convention` + ) + }) + } + + for (const prefix of DISALLOWED_FUNCTION_NAMES) { + it(`should NOT raise error for DISALLOWED_FUNCTION_NAMES [${prefix}] when INTERNAL`, () => { + const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'internal') + const code = contractWith(functionDefinition) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertNoErrors(report) + }) + } + + for (const prefix of ALLOWED_FUNCTION_NAMES) { + it(`should NOT raise error for ALLOWED_FUNCTION_NAMES [${prefix}] when PUBLIC`, () => { + const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'public') + const code = contractWith(functionDefinition) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertNoErrors(report) + }) + } + + for (const prefix of ALLOWED_FUNCTION_NAMES) { + it(`should NOT raise error for ALLOWED_FUNCTION_NAMES [${prefix}] when EXTERNAL`, () => { + const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'external') + const code = contractWith(functionDefinition) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertNoErrors(report) + }) + } + + for (const prefix of ALLOWED_FUNCTION_NAMES) { + it(`should NOT raise error for ALLOWED_FUNCTION_NAMES [${prefix}] when INTERNAL`, () => { + const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'external') + const code = contractWith(functionDefinition) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertNoErrors(report) + }) + } + + it(`should NOT raise error for setUp function, since is configured as SKIPPED`, () => { + const code = contractWith( + 'function setUp() public { testNumber = 42; } function finish() public { testNumber = 42; }' + ) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertNoErrors(report) + }) + + it(`should NOT raise error for setUp and finish functions but RAISE for the other two functions`, () => { + const code = contractWith(` + function setUp() public { testNumber = 42; } + function finish() public { testNumber = 43; } + function invalidFunction1() external { testNumber = 44; } + function invalidFunction2() external { testNumber = 45; }`) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', ['setUp', 'finish']] }, + }) + + assertErrorCount(report, 2) + assert.equal( + report.reports[0].message, + `Function invalidFunction1() must match Foundry test naming convention` + ) + assert.equal( + report.reports[1].message, + 'Function invalidFunction2() must match Foundry test naming convention' + ) + }) + + it('should NOT raise error when recommended rules are configured', () => { + const code = contractWith(` + function setUp() public { testNumber = 42; } + function finish() public { testNumber = 43; } + function invalidFunction1() external { testNumber = 44; } + function invalidFunction2() external { testNumber = 45; }`) + + const report = linter.processStr(code, { + extends: 'solhint:recommended', + rules: { 'compiler-version': 'off' }, + }) + + assertNoErrors(report) + }) + + it('should raise 2 errors when all rules are configured and setUp is skipped', () => { + const code = contractWith(` + function setUp() public { testNumber = 42; } + function invalidFunction1() external { testNumber = 44; } + function invalidFunction2() external { testNumber = 45; }`) + + const report = linter.processStr(code, { + extends: 'solhint:recommended', + rules: { + 'compiler-version': 'off', + 'use-natspec': 'off', + 'foundry-test-function-naming': ['error', ['setUp', 'finish']], + }, + }) + + assertErrorCount(report, 2) + assert.equal( + report.reports[0].message, + `Function invalidFunction1() must match Foundry test naming convention` + ) + assert.equal( + report.reports[1].message, + 'Function invalidFunction2() must match Foundry test naming convention' + ) + }) + + it(`should NOT raise error only for setUp when rule is just on 'error' (setUp is default)`, () => { + const code = contractWith(` + function setUp() public { testNumber = 42; } + function finish() public { testNumber = 43; } + function invalidFunction1() external { testNumber = 44; } + function invalidFunction2() external { testNumber = 45; }`) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': 'error' }, + }) + + assertErrorCount(report, 3) + assert.equal( + report.reports[0].message, + 'Function finish() must match Foundry test naming convention' + ) + assert.equal( + report.reports[1].message, + `Function invalidFunction1() must match Foundry test naming convention` + ) + assert.equal( + report.reports[2].message, + 'Function invalidFunction2() must match Foundry test naming convention' + ) + }) + + it(`should raise error for all functions when rule SKIP array is empty`, () => { + const code = contractWith(` + function setUp() public { testNumber = 42; } + function finish() public { testNumber = 43; } + function invalidFunction() external { testNumber = 44; }`) + + const report = linter.processStr(code, { + rules: { 'foundry-test-function-naming': ['error', []] }, + }) + + assertErrorCount(report, 3) + assert.equal( + report.reports[0].message, + 'Function setUp() must match Foundry test naming convention' + ) + assert.equal( + report.reports[1].message, + `Function finish() must match Foundry test naming convention` + ) + assert.equal( + report.reports[2].message, + 'Function invalidFunction() must match Foundry test naming convention' + ) + }) +}) diff --git a/test/rules/naming/foundry-test-functions.js b/test/rules/naming/foundry-test-functions.js index 18b6d1a7..f95b6518 100644 --- a/test/rules/naming/foundry-test-functions.js +++ b/test/rules/naming/foundry-test-functions.js @@ -30,6 +30,8 @@ const ALLOWED_FUNCTION_NAMES = [ const DISALLOWED_FUNCTION_NAMES = ['Test_', 'Test', '', 'any', 'setUp', 'other', '_'] +const DEPRECATED_ALERT = '[DEPRECATED] rule. Use "foundry-test-function-naming". ' + const composeFunctionName = (prefix, name, visibility) => 'function ' + prefix + name + ' ' + visibility + ' { testNumber = 42; }' @@ -46,7 +48,8 @@ describe('Linter - foundry-test-functions', () => { assertErrorCount(report, 1) assertErrorMessage( report, - `Function ${prefix + 'FunctionName()'} must match Foundry test naming convention` + DEPRECATED_ALERT + + `Function ${prefix + 'FunctionName()'} must match Foundry test naming convention` ) }) } @@ -103,7 +106,7 @@ describe('Linter - foundry-test-functions', () => { }) } - it(`should NOT raise error for setUp function, since is confired as SKIPPED`, () => { + it(`should NOT raise error for setUp function, since is configured as SKIPPED`, () => { const code = contractWith( 'function setUp() public { testNumber = 42; } function finish() public { testNumber = 42; }' ) @@ -129,11 +132,11 @@ describe('Linter - foundry-test-functions', () => { assertErrorCount(report, 2) assert.equal( report.reports[0].message, - `Function invalidFunction1() must match Foundry test naming convention` + DEPRECATED_ALERT + 'Function invalidFunction1() must match Foundry test naming convention' ) assert.equal( report.reports[1].message, - 'Function invalidFunction2() must match Foundry test naming convention' + DEPRECATED_ALERT + 'Function invalidFunction2() must match Foundry test naming convention' ) }) @@ -170,11 +173,11 @@ describe('Linter - foundry-test-functions', () => { assertErrorCount(report, 2) assert.equal( report.reports[0].message, - `Function invalidFunction1() must match Foundry test naming convention` + DEPRECATED_ALERT + 'Function invalidFunction1() must match Foundry test naming convention' ) assert.equal( report.reports[1].message, - 'Function invalidFunction2() must match Foundry test naming convention' + DEPRECATED_ALERT + 'Function invalidFunction2() must match Foundry test naming convention' ) }) @@ -192,15 +195,15 @@ describe('Linter - foundry-test-functions', () => { assertErrorCount(report, 3) assert.equal( report.reports[0].message, - 'Function finish() must match Foundry test naming convention' + DEPRECATED_ALERT + 'Function finish() must match Foundry test naming convention' ) assert.equal( report.reports[1].message, - `Function invalidFunction1() must match Foundry test naming convention` + DEPRECATED_ALERT + 'Function invalidFunction1() must match Foundry test naming convention' ) assert.equal( report.reports[2].message, - 'Function invalidFunction2() must match Foundry test naming convention' + DEPRECATED_ALERT + 'Function invalidFunction2() must match Foundry test naming convention' ) }) @@ -217,15 +220,15 @@ describe('Linter - foundry-test-functions', () => { assertErrorCount(report, 3) assert.equal( report.reports[0].message, - 'Function setUp() must match Foundry test naming convention' + DEPRECATED_ALERT + 'Function setUp() must match Foundry test naming convention' ) assert.equal( report.reports[1].message, - `Function finish() must match Foundry test naming convention` + DEPRECATED_ALERT + `Function finish() must match Foundry test naming convention` ) assert.equal( report.reports[2].message, - 'Function invalidFunction() must match Foundry test naming convention' + DEPRECATED_ALERT + 'Function invalidFunction() must match Foundry test naming convention' ) }) }) diff --git a/test/rules/naming/func-named-parameters.js b/test/rules/naming/func-named-parameters.js index a5f7504a..c4f6f50e 100644 --- a/test/rules/naming/func-named-parameters.js +++ b/test/rules/naming/func-named-parameters.js @@ -71,6 +71,7 @@ describe('Linter - func-named-parameters', () => { 'compiler-version': 'off', 'comprehensive-interface': 'off', 'foundry-test-functions': 'off', + 'foundry-test-function-naming': 'off', 'use-natspec': 'off', }, })