diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e4ead768..50b8860984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Changelog ### Chores +- Added unit tests for mnemonic generation and validation ([PR 1379](https://github.com/input-output-hk/daedalus/pull/1379)) - Simplified the test setup ([PR 1378](https://github.com/input-output-hk/daedalus/pull/1378)) - Updated PR template ([PR 1376](https://github.com/input-output-hk/daedalus/pull/1376)) - Implemented new About Us dialog design with a close button ([PR 1369](https://github.com/input-output-hk/daedalus/pull/1369) diff --git a/features/README.md b/features/README.md index e7ae1a4944..cbf47c4b1c 100644 --- a/features/README.md +++ b/features/README.md @@ -20,6 +20,16 @@ Make sure Daedalus is properly installed (see above). $ yarn test:unit ``` +## Unbound tests + +Unbound tests run as long as you keep them running +(never end except if an error occurs). + +Example: +`yarn test:unit:unbound --tags @mnemonics` +generates and validates mnemonics as long as you keep it +running (the number of executions is updated in the terminal) + # Run UI tests 1. Make sure Daedalus is properly installed (see above). diff --git a/features/mnemonics-generation-and-validation.feature b/features/mnemonics-generation-and-validation.feature new file mode 100644 index 0000000000..83d56cd2f8 --- /dev/null +++ b/features/mnemonics-generation-and-validation.feature @@ -0,0 +1,13 @@ +Feature: Mnemonics generation and validation + + As a developer I want to be sure our mnemonic + (12-word recovery phrase) is correctly generated/validated + + @unit @slow + Scenario: All generated wallet recovery mnemonics are valid + Given I generate 10000 wallet recovery mnemonics + Then all generated wallet recovery mnemonics should be valid + + @unbound @mnemonics + Scenario: Unbound manual test run gives no invalid mnemeonics + Given I generate and validate an unbound number of wallet recovery mnemonics diff --git a/features/tests/unit/steps/mnemonics-steps.js b/features/tests/unit/steps/mnemonics-steps.js new file mode 100644 index 0000000000..7b9ce92033 --- /dev/null +++ b/features/tests/unit/steps/mnemonics-steps.js @@ -0,0 +1,42 @@ +import { Given, When, Then } from 'cucumber'; +import { expect } from 'chai'; +import { range } from 'lodash'; +import { generateAccountMnemonics } from '../../../../source/renderer/app/api/utils/mnemonics'; +import { isValidMnemonic } from '../../../../source/common/crypto/decrypt'; +import { WALLET_RECOVERY_PHRASE_WORD_COUNT } from '../../../../source/renderer/app/config/cryptoConfig'; + +const isValidWalletRecoveryPhrase = mnemonic => + isValidMnemonic(mnemonic, WALLET_RECOVERY_PHRASE_WORD_COUNT); + +Given('I generate {int} wallet recovery mnemonics', function( + numberOfMnemonics +) { + this.context.mnemonics = range(numberOfMnemonics).map(() => + generateAccountMnemonics().join(' ') + ); +}); + +Then('all generated wallet recovery mnemonics should be valid', function() { + for (const mnemonic of this.context.mnemonics) { + if (!isValidWalletRecoveryPhrase(mnemonic)) { + throw new Error(`"${mnemonic}" is not valid`); + } + } +}); + +Given( + 'I generate and validate an unbound number of wallet recovery mnemonics', + function() { + let numberOfTestsExecuted = 0; + while (true) { + const mnemonic = generateAccountMnemonics().join(' '); + if (!isValidWalletRecoveryPhrase(mnemonic)) { + throw new Error(`"${mnemonic}" is not valid`); + } + numberOfTestsExecuted++; + process.stdout.clearLine(); + process.stdout.cursorTo(0); + process.stdout.write(numberOfTestsExecuted + ' mnemonics validated.'); + } + } +); diff --git a/package.json b/package.json index 7ff1ea303d..e779e895ff 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "test": "NODE_ENV=test yarn build && yarn test:unit && yarn test:ui", "test:unit": "yarn cucumber --require 'features/tests/unit/**/*.js' --tags '@unit and not @skip'", "test:unit:watch": "nodemon --watch source --watch features --exec \"yarn test:unit --tags '@unit and @watch'\"", + "test:unit:unbound": "yarn cucumber --require 'features/tests/unit/**/*.js' --tags '@unbound and not @skip'", "test:ui": "yarn cucumber --require 'features/tests/ui/**/*.js' --tags '@ui and not @skip'", "test:ui:watch": "gulp test:ui:watch", "cucumber": "cross-env NODE_ENV=test cucumber-js --require-module @babel/register -f node_modules/cucumber-pretty --format-options '{\"snippetInterface\": \"async-await\"}'",