Skip to content

Commit

Permalink
tests: full, 3x100% test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
tdd committed Sep 27, 2018
1 parent f07eccd commit 1e203bb
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
1 change: 0 additions & 1 deletion TODO.md
@@ -1,6 +1,5 @@
# TODO

- Tests détaillés util/ (on vise une cov 3x100%)
- Utilitaires de migration, à utiliser en code, pour pré-hasher/chiffrer avant d'appliquer les plugins
- Explicit local dev setup: ESLint, Prettier… VSCode project settings, `.editorconfig`, husky, commitizen/commitlint/etc., pre-commit/pre-push hooks
- Assert proper peerDeps (Mongoose >= ?)
Expand Down
36 changes: 36 additions & 0 deletions util/ciphers.spec.js
@@ -0,0 +1,36 @@
const { cipher, decipher } = require('./ciphers')

const key = '126d8cf92d95941e9907b0d9913ce00e'

describe('Cipher Utils', () => {
const REGEX_OBSCURED = /^[\w/+]{38,}=*$/

describe('cipher', () => {
it('should use stable, derived IVs by default', () => {
const obscured1 = cipher(key, 'foo@bar.com')
const obscured2 = cipher(key, 'foo@bar.com')
expect(obscured1).toMatch(REGEX_OBSCURED)
expect(obscured2).toMatch(REGEX_OBSCURED)
expect(obscured1).toEqual(obscured2)
})

it('should allow stable, derived IVs', () => {
const obscured1 = cipher(key, 'foo@bar.com', { deriveIV: false })
const obscured2 = cipher(key, 'foo@bar.com', { deriveIV: false })
expect(obscured1).toMatch(REGEX_OBSCURED)
expect(obscured2).toMatch(REGEX_OBSCURED)
expect(obscured1).not.toEqual(obscured2)
})
})

describe('decipher', () => {
it('should work on both derived and random IV obscured texts', () => {
expect(
decipher(key, '2ZXfUUBPTPaETqXIA33bRwQNnif1/u/axrI84yQShR9Q==')
).toEqual('foo@bar.com')
expect(
decipher(key, 'Zj6jEHwYOGVDT92Dg9rKFw8DdfreEhm4pB4qtq6CdAFw==')
).toEqual('foo@bar.com')
})
})
})
64 changes: 64 additions & 0 deletions util/passwords.spec.js
@@ -0,0 +1,64 @@
const crypto = require('crypto')
const spy = jest.spyOn(crypto, 'createHash')
const { checkPassword, hashPassword } = require('./passwords')

describe('Password Utils', () => {
describe('checkPassword', () => {
it('should resolve to true on a matching hash', () => {
expect(
checkPassword(
'secret',
'$2a$04$VX4I2s9192QIuOLYdYw0aO.mc1GlnpgpLRzF8D7BpNxl/ficBIt4y'
)
).resolves.toBeTruthy()
})

it('should resolve to false on a non-matching hash', () => {
expect(
checkPassword(
'secret',
'$2a$04$VX4I2s9192QIuOLYdYw0aO.mc1GlnpgpLRzF8D7BpNxl/xxxxxxxx'
)
).resolves.toBeFalsy()

expect(checkPassword('secret', '')).resolves.toBeFalsy()
})
})

describe('hashPassword', () => {
// function hashPassword(clearText, { rounds = ROUNDS, sync = false } = {})
it('should default to 4 rounds outside production', () => {
expect(hashPassword('secret')).resolves.toMatch(/^\$2a\$04\$/)
})

it('should default to 10 rounds in production', () => {
jest.resetModules()
const oldEnv = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
try {
const { hashPassword } = require('./passwords')
expect(hashPassword('secret')).resolves.toMatch(/^\$2a\$10\$/)
} finally {
process.env.NODE_ENV = oldEnv
}
})

it('should accept custom rounds', () => {
expect(hashPassword('secret', { rounds: 6 })).resolves.toMatch(
/^\$2a\$06\$/
)
})

it('should allow synchronous usage', () => {
expect(hashPassword('secret', { sync: true })).toMatch(/^\$2a\$04\$/)
})

it('should SHA512 its input if it’s above maximum bcrypt input size, to preserve entropy', () => {
spy.mockClear()
const input = crypto.randomBytes(256).toString('utf8')
hashPassword(input, { sync: true })

expect(spy).toHaveBeenCalledWith('sha512')
})
})
})

0 comments on commit 1e203bb

Please sign in to comment.