Skip to content

Commit

Permalink
chore: add test cases for /api/auth/register
Browse files Browse the repository at this point in the history
  • Loading branch information
mistakia committed Apr 6, 2024
1 parent 0628e8c commit 47aac97
Show file tree
Hide file tree
Showing 7 changed files with 664 additions and 71 deletions.
19 changes: 9 additions & 10 deletions api/routes/auth.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@ import express from 'express'
import nano from 'nanocurrency'
import { tools } from 'nanocurrency-web'

import { USERNAME_RE } from '#constants'

const router = express.Router()
const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/

router.post('/register', async (req, res) => {
const { logger, db } = req.app.locals
try {
const required = ['pub', 'address', 'signature', 'username']
const required = ['public_key', 'address', 'signature', 'username']
for (const prop of required) {
if (!req.body[prop]) {
return res.status(400).send({ error: `missing ${prop} param` })
}
}

const { pub, signature, username } = req.body
const { public_key, signature, username } = req.body
let { address } = req.body

if (!nano.checkKey(pub)) {
return res.status(401).send({ error: 'invalid pub param' })
if (!nano.checkKey(public_key)) {
return res.status(401).send({ error: 'invalid public_key param' })
}

if (!nano.checkAddress(address)) {
Expand All @@ -32,14 +31,14 @@ router.post('/register', async (req, res) => {
}

const publicKey = tools.addressToPublicKey(address)
const validSignature = tools.verify(publicKey, signature, pub)
if (!validSignature) {
const valid_signature = tools.verify(publicKey, signature, public_key)
if (!valid_signature) {
return res.status(401).send({ error: 'invalid signature' })
}

const usernameExists = await db('users')
.where({ username })
.whereNot({ pub })
.whereNot({ public_key })
if (usernameExists.length) {
return res.status(401).send({ error: 'username exists' })
}
Expand All @@ -54,7 +53,7 @@ router.post('/register', async (req, res) => {
if (!accountId) {
const result = await db('users')
.insert({
pub,
public_key,
username,
last_visit: Math.round(Date.now() / 1000)
})
Expand Down
1 change: 0 additions & 1 deletion constants.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export const discordNano = '370266023905198083'
export const discordNanoTrade = '403628195548495882'
export const repo = 'mistakia/nano-community'
export const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/g
export const BURN_ACCOUNT =
'nano_1111111111111111111111111111111111111111111111111111hifc8npp'
export const REPRESENTATIVE_TRACKING_MINIMUM_VOTING_WEIGHT = 10000000000000000000000000000000000
4 changes: 2 additions & 2 deletions db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -511,10 +511,10 @@ DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`pub` varchar(64) NOT NULL,
`public_key` varchar(64) NOT NULL,
`last_visit` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `pub` (`pub`)
UNIQUE KEY `public_key` (`public_key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;

-- --------------------------------------------------------
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"deploy": "ipfs-deploy build/index.html",
"build": "cross-env NODE_ENV=production webpack --config webpack/webpack.prod.babel.mjs --color --progress && react-snap",
"lint": "eslint . --ext js,mjs",
"prettier": "prettier --write ."
"prettier": "prettier --write .",
"test": "TZ=America/New_York NODE_ENV=test TEST=all mocha --exit"
},
"homepage": "https://nano.community/",
"reactSnap": {
Expand Down Expand Up @@ -140,6 +141,8 @@
"@mui/x-data-grid": "^6.19.5",
"babel-loader": "9.1.3",
"babel-plugin-module-resolver": "^5.0.0",
"chai": "4.3.7",
"chai-http": "^4.4.0",
"circular-dependency-plugin": "^5.2.2",
"compression-webpack-plugin": "^10.0.0",
"concurrently": "^8.2.0",
Expand All @@ -165,6 +168,7 @@
"ipfs-deploy": "^12.0.1",
"jss": "^10.10.0",
"markdown-it-anchor": "^8.6.7",
"mocha": "^10.4.0",
"nib": "^1.2.0",
"percentile": "^1.6.0",
"prettier": "2.8.8",
Expand Down
183 changes: 183 additions & 0 deletions test/auth.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/* global describe before it */
import chai from 'chai'
import crypto from 'crypto'
import chaiHTTP from 'chai-http'
import { tools, wallet } from 'nanocurrency-web'

import server from '#api/server.mjs'
import knex from '#db'
import { mochaGlobalSetup } from './global.mjs'

process.env.NODE_ENV = 'test'
// chai.should()
chai.use(chaiHTTP)
const expect = chai.expect

describe('API /auth', () => {
before(mochaGlobalSetup)

describe('errors', () => {
it('should return 400 if pub field is missing', async () => {
const response = await chai
.request(server)
.post('/api/auth/register')
.send({
address: 'someaddress',
signature: 'somesignature',
username: 'test_username'
}) // missing public_key
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing public_key param')
})

it('should return 400 if address field is missing', async () => {
const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'somepub',
signature: 'somesignature',
username: 'test_username'
}) // missing address
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing address param')
})

it('should return 400 if signature field is missing', async () => {
const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'somepub',
address: 'someaddress',
username: 'test_username'
}) // missing signature
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing signature param')
})

it('should return 400 if username field is missing', async () => {
const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'somepub',
address: 'someaddress',
signature: 'somesignature'
}) // missing username
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing username param')
})

it('should return 401 if pub param is invalid', async () => {
const w = wallet.generate()
const account = w.accounts[0]

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'invalidpub',
address: account.address,
signature: 'somesignature',
username: 'test_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid public_key param')
})

it('should return 401 if address param is invalid', async () => {
const w = wallet.generate()
const account = w.accounts[0]

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account.publicKey,
address: 'invalidaddress',
signature: 'somesignature',
username: 'test_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid address param')
})

const invalid_usernames = [
'contains space',
'constains@character',
'1starts_with_number',
'contains!character',
'contains.period',
'contains-hyphen',
'contains$dollar',
'contains#hash'
]

invalid_usernames.forEach((username) => {
it(`should return 401 if username param is invalid: ${username}`, async () => {
const w = wallet.generate()
const account = w.accounts[0]

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account.publicKey,
address: account.address,
signature: 'somesignature',
username
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid username param')
})
})

it('should return 401 if signature is invalid', async () => {
const w = wallet.generate()
const account = w.accounts[0]

const signature = tools.sign(account.privateKey, 'some message')

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account.publicKey,
address: account.address,
signature,
username: 'test_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid signature')
})

it('should return 401 if username already exists', async () => {
const seed = crypto.randomBytes(64).toString('hex')
const accounts = wallet.accounts(seed, 0, 1)
const account_0 = accounts[0]
const account_1 = accounts[1]

await knex('users').insert({
id: 1,
username: 'existing_username',
public_key: account_0.publicKey,
last_visit: Math.floor(Date.now() / 1000)
})

const signature = tools.sign(account_1.privateKey, account_1.publicKey)

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account_1.publicKey,
address: account_1.address,
signature,
username: 'existing_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('username exists')
})
})
})
12 changes: 12 additions & 0 deletions test/global.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import knex from '#db'
import path, { dirname } from 'path'
import fs from 'fs/promises'
import { fileURLToPath } from 'url'

const __dirname = dirname(fileURLToPath(import.meta.url))
const sqlFile = path.resolve(__dirname, '../db/schema.sql')

export async function mochaGlobalSetup() {
const sql = await fs.readFile(sqlFile, 'utf8')
await knex.raw(sql)
}
Loading

0 comments on commit 47aac97

Please sign in to comment.