Skip to content

Commit 47aac97

Browse files
committed
chore: add test cases for /api/auth/register
1 parent 0628e8c commit 47aac97

File tree

7 files changed

+664
-71
lines changed

7 files changed

+664
-71
lines changed

api/routes/auth.mjs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,24 @@ import express from 'express'
22
import nano from 'nanocurrency'
33
import { tools } from 'nanocurrency-web'
44

5-
import { USERNAME_RE } from '#constants'
6-
75
const router = express.Router()
6+
const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/
87

98
router.post('/register', async (req, res) => {
109
const { logger, db } = req.app.locals
1110
try {
12-
const required = ['pub', 'address', 'signature', 'username']
11+
const required = ['public_key', 'address', 'signature', 'username']
1312
for (const prop of required) {
1413
if (!req.body[prop]) {
1514
return res.status(400).send({ error: `missing ${prop} param` })
1615
}
1716
}
1817

19-
const { pub, signature, username } = req.body
18+
const { public_key, signature, username } = req.body
2019
let { address } = req.body
2120

22-
if (!nano.checkKey(pub)) {
23-
return res.status(401).send({ error: 'invalid pub param' })
21+
if (!nano.checkKey(public_key)) {
22+
return res.status(401).send({ error: 'invalid public_key param' })
2423
}
2524

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

3433
const publicKey = tools.addressToPublicKey(address)
35-
const validSignature = tools.verify(publicKey, signature, pub)
36-
if (!validSignature) {
34+
const valid_signature = tools.verify(publicKey, signature, public_key)
35+
if (!valid_signature) {
3736
return res.status(401).send({ error: 'invalid signature' })
3837
}
3938

4039
const usernameExists = await db('users')
4140
.where({ username })
42-
.whereNot({ pub })
41+
.whereNot({ public_key })
4342
if (usernameExists.length) {
4443
return res.status(401).send({ error: 'username exists' })
4544
}
@@ -54,7 +53,7 @@ router.post('/register', async (req, res) => {
5453
if (!accountId) {
5554
const result = await db('users')
5655
.insert({
57-
pub,
56+
public_key,
5857
username,
5958
last_visit: Math.round(Date.now() / 1000)
6059
})

constants.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export const discordNano = '370266023905198083'
22
export const discordNanoTrade = '403628195548495882'
33
export const repo = 'mistakia/nano-community'
4-
export const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/g
54
export const BURN_ACCOUNT =
65
'nano_1111111111111111111111111111111111111111111111111111hifc8npp'
76
export const REPRESENTATIVE_TRACKING_MINIMUM_VOTING_WEIGHT = 10000000000000000000000000000000000

db/schema.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,10 @@ DROP TABLE IF EXISTS `users`;
511511
CREATE TABLE `users` (
512512
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
513513
`username` varchar(32) NOT NULL,
514-
`pub` varchar(64) NOT NULL,
514+
`public_key` varchar(64) NOT NULL,
515515
`last_visit` int(11) NOT NULL,
516516
PRIMARY KEY (`id`),
517-
UNIQUE KEY `pub` (`pub`)
517+
UNIQUE KEY `public_key` (`public_key`)
518518
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;
519519

520520
-- --------------------------------------------------------

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"deploy": "ipfs-deploy build/index.html",
1616
"build": "cross-env NODE_ENV=production webpack --config webpack/webpack.prod.babel.mjs --color --progress && react-snap",
1717
"lint": "eslint . --ext js,mjs",
18-
"prettier": "prettier --write ."
18+
"prettier": "prettier --write .",
19+
"test": "TZ=America/New_York NODE_ENV=test TEST=all mocha --exit"
1920
},
2021
"homepage": "https://nano.community/",
2122
"reactSnap": {
@@ -140,6 +141,8 @@
140141
"@mui/x-data-grid": "^6.19.5",
141142
"babel-loader": "9.1.3",
142143
"babel-plugin-module-resolver": "^5.0.0",
144+
"chai": "4.3.7",
145+
"chai-http": "^4.4.0",
143146
"circular-dependency-plugin": "^5.2.2",
144147
"compression-webpack-plugin": "^10.0.0",
145148
"concurrently": "^8.2.0",
@@ -165,6 +168,7 @@
165168
"ipfs-deploy": "^12.0.1",
166169
"jss": "^10.10.0",
167170
"markdown-it-anchor": "^8.6.7",
171+
"mocha": "^10.4.0",
168172
"nib": "^1.2.0",
169173
"percentile": "^1.6.0",
170174
"prettier": "2.8.8",

test/auth.test.mjs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/* global describe before it */
2+
import chai from 'chai'
3+
import crypto from 'crypto'
4+
import chaiHTTP from 'chai-http'
5+
import { tools, wallet } from 'nanocurrency-web'
6+
7+
import server from '#api/server.mjs'
8+
import knex from '#db'
9+
import { mochaGlobalSetup } from './global.mjs'
10+
11+
process.env.NODE_ENV = 'test'
12+
// chai.should()
13+
chai.use(chaiHTTP)
14+
const expect = chai.expect
15+
16+
describe('API /auth', () => {
17+
before(mochaGlobalSetup)
18+
19+
describe('errors', () => {
20+
it('should return 400 if pub field is missing', async () => {
21+
const response = await chai
22+
.request(server)
23+
.post('/api/auth/register')
24+
.send({
25+
address: 'someaddress',
26+
signature: 'somesignature',
27+
username: 'test_username'
28+
}) // missing public_key
29+
expect(response).to.have.status(400)
30+
expect(response.body.error).to.include('missing public_key param')
31+
})
32+
33+
it('should return 400 if address field is missing', async () => {
34+
const response = await chai
35+
.request(server)
36+
.post('/api/auth/register')
37+
.send({
38+
public_key: 'somepub',
39+
signature: 'somesignature',
40+
username: 'test_username'
41+
}) // missing address
42+
expect(response).to.have.status(400)
43+
expect(response.body.error).to.include('missing address param')
44+
})
45+
46+
it('should return 400 if signature field is missing', async () => {
47+
const response = await chai
48+
.request(server)
49+
.post('/api/auth/register')
50+
.send({
51+
public_key: 'somepub',
52+
address: 'someaddress',
53+
username: 'test_username'
54+
}) // missing signature
55+
expect(response).to.have.status(400)
56+
expect(response.body.error).to.include('missing signature param')
57+
})
58+
59+
it('should return 400 if username field is missing', async () => {
60+
const response = await chai
61+
.request(server)
62+
.post('/api/auth/register')
63+
.send({
64+
public_key: 'somepub',
65+
address: 'someaddress',
66+
signature: 'somesignature'
67+
}) // missing username
68+
expect(response).to.have.status(400)
69+
expect(response.body.error).to.include('missing username param')
70+
})
71+
72+
it('should return 401 if pub param is invalid', async () => {
73+
const w = wallet.generate()
74+
const account = w.accounts[0]
75+
76+
const response = await chai
77+
.request(server)
78+
.post('/api/auth/register')
79+
.send({
80+
public_key: 'invalidpub',
81+
address: account.address,
82+
signature: 'somesignature',
83+
username: 'test_username'
84+
})
85+
expect(response).to.have.status(401)
86+
expect(response.body.error).to.equal('invalid public_key param')
87+
})
88+
89+
it('should return 401 if address param is invalid', async () => {
90+
const w = wallet.generate()
91+
const account = w.accounts[0]
92+
93+
const response = await chai
94+
.request(server)
95+
.post('/api/auth/register')
96+
.send({
97+
public_key: account.publicKey,
98+
address: 'invalidaddress',
99+
signature: 'somesignature',
100+
username: 'test_username'
101+
})
102+
expect(response).to.have.status(401)
103+
expect(response.body.error).to.equal('invalid address param')
104+
})
105+
106+
const invalid_usernames = [
107+
'contains space',
108+
'constains@character',
109+
'1starts_with_number',
110+
'contains!character',
111+
'contains.period',
112+
'contains-hyphen',
113+
'contains$dollar',
114+
'contains#hash'
115+
]
116+
117+
invalid_usernames.forEach((username) => {
118+
it(`should return 401 if username param is invalid: ${username}`, async () => {
119+
const w = wallet.generate()
120+
const account = w.accounts[0]
121+
122+
const response = await chai
123+
.request(server)
124+
.post('/api/auth/register')
125+
.send({
126+
public_key: account.publicKey,
127+
address: account.address,
128+
signature: 'somesignature',
129+
username
130+
})
131+
expect(response).to.have.status(401)
132+
expect(response.body.error).to.equal('invalid username param')
133+
})
134+
})
135+
136+
it('should return 401 if signature is invalid', async () => {
137+
const w = wallet.generate()
138+
const account = w.accounts[0]
139+
140+
const signature = tools.sign(account.privateKey, 'some message')
141+
142+
const response = await chai
143+
.request(server)
144+
.post('/api/auth/register')
145+
.send({
146+
public_key: account.publicKey,
147+
address: account.address,
148+
signature,
149+
username: 'test_username'
150+
})
151+
expect(response).to.have.status(401)
152+
expect(response.body.error).to.equal('invalid signature')
153+
})
154+
155+
it('should return 401 if username already exists', async () => {
156+
const seed = crypto.randomBytes(64).toString('hex')
157+
const accounts = wallet.accounts(seed, 0, 1)
158+
const account_0 = accounts[0]
159+
const account_1 = accounts[1]
160+
161+
await knex('users').insert({
162+
id: 1,
163+
username: 'existing_username',
164+
public_key: account_0.publicKey,
165+
last_visit: Math.floor(Date.now() / 1000)
166+
})
167+
168+
const signature = tools.sign(account_1.privateKey, account_1.publicKey)
169+
170+
const response = await chai
171+
.request(server)
172+
.post('/api/auth/register')
173+
.send({
174+
public_key: account_1.publicKey,
175+
address: account_1.address,
176+
signature,
177+
username: 'existing_username'
178+
})
179+
expect(response).to.have.status(401)
180+
expect(response.body.error).to.equal('username exists')
181+
})
182+
})
183+
})

test/global.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import knex from '#db'
2+
import path, { dirname } from 'path'
3+
import fs from 'fs/promises'
4+
import { fileURLToPath } from 'url'
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url))
7+
const sqlFile = path.resolve(__dirname, '../db/schema.sql')
8+
9+
export async function mochaGlobalSetup() {
10+
const sql = await fs.readFile(sqlFile, 'utf8')
11+
await knex.raw(sql)
12+
}

0 commit comments

Comments
 (0)