From f0c416105bc472d71aa42dc166bc9b75c380c58c Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Tue, 5 Mar 2024 17:07:57 -0800 Subject: [PATCH 01/12] feat(lib/nameserver): added, with tests --- CHANGELOG.md | 7 +++ lib/nameserver.js | 125 +++++++++++++++++++++++++++++++++++++++ lib/nameserver.test.js | 44 ++++++++++++++ lib/test/nameserver.json | 19 ++++++ package.json | 2 +- 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 lib/nameserver.js create mode 100644 lib/nameserver.test.js create mode 100644 lib/test/nameserver.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c646c..a04a9bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ### Unreleased + +### [3.0.0-alpha.4] - 2024-03-05 + +- feat(lib/nameserver): added, with tests + + ### 3.0.0-alpha.3 - routes/permission: added GET, POST, DELETE @@ -14,3 +20,4 @@ [3.0.0-alpha.3]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.3 +[3.0.0-alpha.4]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.4 diff --git a/lib/nameserver.js b/lib/nameserver.js new file mode 100644 index 0000000..682a044 --- /dev/null +++ b/lib/nameserver.js @@ -0,0 +1,125 @@ +import Mysql from './mysql.js' +import { mapToDbColumn } from './util.js' + +const nsDbMap = { id: 'nt_nameserver_id', gid: 'nt_group_id' } +const boolFields = ['deleted', 'export_serials'] + +class Nameserver { + constructor() { + this.mysql = Mysql + } + + async create(args) { + if (args.id) { + const g = await this.get({ id: args.id }) + if (g.length === 1) return g[0].id + } + + if (args.export.type) { + const rows = await Mysql.execute(...Mysql.select('SELECT id FROM nt_nameserver_export_type', { name: args.export.type })) + args.export_type_id = rows[0].id + delete args.export.type + } + + return await Mysql.execute( + ...Mysql.insert(`nt_nameserver`, mapToDbColumn(objectToDb(args), nsDbMap)), + ) + } + + async get(args) { + if (args.name !== undefined) { + args['ns.name'] = args.name + delete args.name + } + const rows = await Mysql.execute( + ...Mysql.select( + `SELECT ns.nt_nameserver_id AS id + , ns.nt_group_id AS gid + , ns.name + , ns.ttl + , ns.description + , ns.address + , ns.address6 + , ns.remote_login + , ns.logdir + , ns.datadir + , ns.export_interval + , ns.export_serials + , ns.export_status + , ns.deleted + , t.name AS export_type + FROM nt_nameserver ns + JOIN nt_nameserver_export_type t ON ns.export_type_id=t.id`, + mapToDbColumn(args, nsDbMap), + ), + ) + for (const r of rows) { + for (const b of boolFields) { + r[b] = r[b] === 1 + } + } + return dbToObject(rows) + } + + async put(args) { + if (!args.id) return false + const id = args.id + delete args.id + // Mysql.debug(1) + const r = await Mysql.execute( + ...Mysql.update( + `nt_nameserver`, + `nt_nameserver_id=${id}`, + mapToDbColumn(args, nsDbMap), + ), + ) + // console.log(r) + return r.changedRows === 1 + } + + async delete(args) { + await Mysql.execute(`UPDATE nt_nameserver SET deleted=? WHERE nt_nameserver_id=?`, [ + args.deleted ?? 1, + args.id, + ]) + return true + } + + async destroy(args) { + return await Mysql.execute( + ...Mysql.delete(`nt_nameserver`, { nt_nameserver_id: args.id }), + ) + } +} + +export default new Nameserver() + + +function dbToObject(rows) { + for (const row of rows) { + for (const f of ['export']) { + for (const p of ['type', 'interval', 'serials', 'status']) { + if (row[`${f}_${p}`] !== undefined) { + if (row[f] === undefined) row[f] = {} + row[f][p] = row[`${f}_${p}`] + delete row[`${f}_${p}`] + } + } + } + } + return rows +} + +function objectToDb(row) { + const newRow = JSON.parse(JSON.stringify(row)) + for (const f of ['export']) { + for (const p of ['interval', 'serials', 'status']) { + if (newRow[f] === undefined) continue + if (newRow[f][p] === undefined) continue + newRow[`${f}_${p}`] = newRow[f][p] + delete newRow[f][p] + } + delete newRow[f] + } + return newRow +} diff --git a/lib/nameserver.test.js b/lib/nameserver.test.js new file mode 100644 index 0000000..8e91b1e --- /dev/null +++ b/lib/nameserver.test.js @@ -0,0 +1,44 @@ +import assert from 'node:assert/strict' +import { describe, it, after, before } from 'node:test' + +import Nameserver from './nameserver.js' + +import testCase from './test/nameserver.json' with { type: 'json' } + +before(async () => { + await Nameserver.create(testCase) +}) + +after(async () => { + // Nameserver.destroy({ id: testCase.id }) + Nameserver.mysql.disconnect() +}) + +describe('nameserver', function () { + + it('gets nameserver by id', async () => { + const g = await Nameserver.get({ id: testCase.id }) + assert.deepEqual(g[0], testCase) + }) + + it('gets nameserver by name', async () => { + const g = await Nameserver.get({ name: testCase.name }) + assert.deepEqual(g[0], testCase) + }) + + it('changes a nameserver', async () => { + assert.ok(await Nameserver.put({ id: testCase.id, name: 'b.ns.example.com.' })) + const ns = await Nameserver.get({ id: testCase.id }) + assert.deepEqual(ns[0].name, 'b.ns.example.com.') + assert.ok(await Nameserver.put({ id: testCase.id, name: testCase.name })) + }) + + it('deletes a nameserver', async () => { + assert.ok(await Nameserver.delete({ id: testCase.id })) + let g = await Nameserver.get({ id: testCase.id, deleted: 1 }) + assert.equal(g[0]?.deleted, true) + await Nameserver.delete({ id: testCase.id, deleted: 0 }) // restore + g = await Nameserver.get({ id: testCase.id }) + assert.equal(g[0].deleted, false) + }) +}) diff --git a/lib/test/nameserver.json b/lib/test/nameserver.json new file mode 100644 index 0000000..6888ab9 --- /dev/null +++ b/lib/test/nameserver.json @@ -0,0 +1,19 @@ +{ + "id": 4096, + "gid": 4096, + "name": "a.ns.example.com.", + "description": "", + "address": "1.2.3.4", + "address6": "2001:DB8::1", + "remote_login": "nsd", + "logdir": "/foo", + "datadir": "/bar", + "export": { + "interval": 0, + "serials": true, + "status": "last run:03-05 15:25
last cp :09-20 12:59", + "type": "NSD" + }, + "ttl": 3600, + "deleted": false +} diff --git a/package.json b/package.json index 0d79d33..9c6e38c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nictool/api", - "version": "3.0.0-alpha.3", + "version": "3.0.0-alpha.4", "description": "NicTool API", "main": "index.js", "type": "module", From bbbdabc948e7d26a402e243f746c2d8c71a24d4c Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 09:06:31 -0800 Subject: [PATCH 02/12] feat(routes/nameserver): added, with tests --- CHANGELOG.md | 2 +- lib/nameserver.js | 23 ++++-- lib/nameserver.test.js | 7 +- routes/index.js | 2 + routes/nameserver.js | 117 ++++++++++++++++++++++++++++++ routes/nameserver.test.js | 138 ++++++++++++++++++++++++++++++++++++ routes/test/nameserver.json | 19 +++++ 7 files changed, 297 insertions(+), 11 deletions(-) create mode 100644 routes/nameserver.js create mode 100644 routes/nameserver.test.js create mode 100644 routes/test/nameserver.json diff --git a/CHANGELOG.md b/CHANGELOG.md index a04a9bb..87125d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### [3.0.0-alpha.4] - 2024-03-05 - feat(lib/nameserver): added, with tests - +- feat(routes/nameserver): added, with tests ### 3.0.0-alpha.3 diff --git a/lib/nameserver.js b/lib/nameserver.js index 682a044..2caa674 100644 --- a/lib/nameserver.js +++ b/lib/nameserver.js @@ -16,13 +16,20 @@ class Nameserver { } if (args.export.type) { - const rows = await Mysql.execute(...Mysql.select('SELECT id FROM nt_nameserver_export_type', { name: args.export.type })) + const rows = await Mysql.execute( + ...Mysql.select('SELECT id FROM nt_nameserver_export_type', { + name: args.export.type, + }), + ) args.export_type_id = rows[0].id delete args.export.type } return await Mysql.execute( - ...Mysql.insert(`nt_nameserver`, mapToDbColumn(objectToDb(args), nsDbMap)), + ...Mysql.insert( + `nt_nameserver`, + mapToDbColumn(objectToDb(args), nsDbMap), + ), ) } @@ -78,10 +85,10 @@ class Nameserver { } async delete(args) { - await Mysql.execute(`UPDATE nt_nameserver SET deleted=? WHERE nt_nameserver_id=?`, [ - args.deleted ?? 1, - args.id, - ]) + await Mysql.execute( + `UPDATE nt_nameserver SET deleted=? WHERE nt_nameserver_id=?`, + [args.deleted ?? 1, args.id], + ) return true } @@ -94,9 +101,11 @@ class Nameserver { export default new Nameserver() - function dbToObject(rows) { for (const row of rows) { + for (const f of ['description', 'address6', 'remote_login', 'datadir', 'logdir', 'export_status']) { + if ([undefined, null].includes(row[f])) row[f] = '' + } for (const f of ['export']) { for (const p of ['type', 'interval', 'serials', 'status']) { if (row[`${f}_${p}`] !== undefined) { diff --git a/lib/nameserver.test.js b/lib/nameserver.test.js index 8e91b1e..eea61ac 100644 --- a/lib/nameserver.test.js +++ b/lib/nameserver.test.js @@ -10,12 +10,11 @@ before(async () => { }) after(async () => { - // Nameserver.destroy({ id: testCase.id }) + Nameserver.destroy({ id: testCase.id }) Nameserver.mysql.disconnect() }) describe('nameserver', function () { - it('gets nameserver by id', async () => { const g = await Nameserver.get({ id: testCase.id }) assert.deepEqual(g[0], testCase) @@ -27,7 +26,9 @@ describe('nameserver', function () { }) it('changes a nameserver', async () => { - assert.ok(await Nameserver.put({ id: testCase.id, name: 'b.ns.example.com.' })) + assert.ok( + await Nameserver.put({ id: testCase.id, name: 'b.ns.example.com.' }), + ) const ns = await Nameserver.get({ id: testCase.id }) assert.deepEqual(ns[0].name, 'b.ns.example.com.') assert.ok(await Nameserver.put({ id: testCase.id, name: testCase.name })) diff --git a/routes/index.js b/routes/index.js index 9cdcc08..af0e120 100644 --- a/routes/index.js +++ b/routes/index.js @@ -20,6 +20,7 @@ import GroupRoutes from './group.js' import { User, UserRoutes } from './user.js' import { Session, SessionRoutes } from './session.js' import { PermissionRoutes } from './permission.js' +import { NameserverRoutes } from './nameserver.js' let server @@ -84,6 +85,7 @@ async function setup() { UserRoutes(server) SessionRoutes(server) PermissionRoutes(server) + NameserverRoutes(server) server.route({ method: '*', diff --git a/routes/nameserver.js b/routes/nameserver.js new file mode 100644 index 0000000..fe306a2 --- /dev/null +++ b/routes/nameserver.js @@ -0,0 +1,117 @@ +import validate from '@nictool/validate' + +import Nameserver from '../lib/nameserver.js' +import { meta } from '../lib/util.js' + +function NameserverRoutes(server) { + server.route([ + { + method: 'GET', + path: '/nameserver/{id}', + options: { + validate: { + query: validate.nameserver.v3, + }, + response: { + schema: validate.nameserver.GET, + }, + tags: ['api'], + }, + handler: async (request, h) => { + const getArgs = { + deleted: request.query.deleted === true ? 1 : 0, + id: parseInt(request.params.id, 10), + } + + const nameservers = await Nameserver.get(getArgs) + + return h + .response({ + nameserver: nameservers[0], + meta: { + api: meta.api, + msg: `here's your nameserver`, + }, + }) + .code(200) + }, + }, + { + method: 'POST', + path: '/nameserver', + options: { + validate: { + payload: validate.nameserver.POST, + }, + response: { + schema: validate.nameserver.GET, + }, + tags: ['api'], + }, + handler: async (request, h) => { + const id = await Nameserver.create(request.payload) + + const nameservers = await Nameserver.get({ id }) + + return h + .response({ + nameserver: nameservers[0], + meta: { + api: meta.api, + msg: `the nameserver was created`, + }, + }) + .code(201) + }, + }, + { + method: 'DELETE', + path: '/nameserver/{id}', + options: { + validate: { + query: validate.nameserver.DELETE, + }, + response: { + schema: validate.nameserver.GET, + }, + tags: ['api'], + }, + handler: async (request, h) => { + const nameservers = await Nameserver.get({ + deleted: request.query.deleted === true ? 1 : 0, + id: parseInt(request.params.id, 10), + }) + + if (!nameservers) { + return h + .response({ + meta: { + api: meta.api, + msg: `I couldn't find that nameserver`, + }, + }) + .code(404) + } + + await Nameserver.delete({ + id: nameservers[0].id, + deleted: 1, + }) + + return h + .response({ + nameserver: nameservers[0], + meta: { + api: meta.api, + msg: `I deleted that nameserver`, + }, + }) + .code(200) + }, + }, + ]) +} + +export default NameserverRoutes + +export { Nameserver, NameserverRoutes } diff --git a/routes/nameserver.test.js b/routes/nameserver.test.js new file mode 100644 index 0000000..34c38ce --- /dev/null +++ b/routes/nameserver.test.js @@ -0,0 +1,138 @@ +import assert from 'node:assert/strict' +import { describe, it, before, after } from 'node:test' + +import { init } from './index.js' +import Group from '../lib/group.js' +import User from '../lib/user.js' +import Nameserver from '../lib/nameserver.js' + +import groupCase from './test/group.json' with { type: 'json' } +import userCase from './test/user.json' with { type: 'json' } +import nsCase from './test/nameserver.json' with { type: 'json' } + +let server +let case2Id = 4094 + +before(async () => { + await Nameserver.destroy({ id: case2Id }) + await Group.create(groupCase) + await User.create(userCase) + await Nameserver.create(nsCase) + server = await init() +}) + + +after(async () => { + await server.stop() +}) + +describe('nameserver routes', () => { + let sessionCookie + + it('POST /session establishes a session', async () => { + const res = await server.inject({ + method: 'POST', + url: '/session', + payload: { + username: `${userCase.username}@${groupCase.name}`, + password: userCase.password, + }, + }) + assert.ok(res.headers['set-cookie'][0]) + sessionCookie = res.headers['set-cookie'][0].split(';')[0] + }) + + it(`GET /nameserver/${nsCase.id}`, async () => { + const res = await server.inject({ + method: 'GET', + url: `/nameserver/${nsCase.id}`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + assert.equal(res.statusCode, 200) + assert.equal(res.result.nameserver.name, nsCase.name) + }) + + it(`POST /nameserver (${case2Id})`, async () => { + const testCase = JSON.parse(JSON.stringify(nsCase)) + testCase.id = case2Id // make it unique + testCase.gid = case2Id + testCase.name = 'c.ns.example.com.' + + const res = await server.inject({ + method: 'POST', + url: '/nameserver', + headers: { + Cookie: sessionCookie, + }, + payload: testCase, + }) + // console.log(res.result) + assert.equal(res.statusCode, 201) + assert.ok(res.result.nameserver.gid) + }) + + it(`GET /nameserver/${case2Id}`, async () => { + const res = await server.inject({ + method: 'GET', + url: `/nameserver/${case2Id}`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + assert.equal(res.statusCode, 200) + assert.ok(res.result.nameserver.gid) + }) + + it(`DELETE /nameserver/${case2Id}`, async () => { + const res = await server.inject({ + method: 'DELETE', + url: `/nameserver/${case2Id}`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + assert.equal(res.statusCode, 200) + }) + + it(`GET /nameserver/${case2Id}`, async () => { + const res = await server.inject({ + method: 'GET', + url: `/nameserver/${case2Id}`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + // assert.equal(res.statusCode, 200) + assert.equal(res.result.nameserver, undefined) + }) + + it(`GET /nameserver/${case2Id} (deleted)`, async () => { + const res = await server.inject({ + method: 'GET', + url: `/nameserver/${case2Id}?deleted=true`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + assert.equal(res.statusCode, 200) + assert.ok(res.result.nameserver) + }) + + it('DELETE /session', async () => { + const res = await server.inject({ + method: 'DELETE', + url: '/session', + headers: { + Cookie: sessionCookie, + }, + }) + assert.equal(res.statusCode, 200) + }) +}) diff --git a/routes/test/nameserver.json b/routes/test/nameserver.json new file mode 100644 index 0000000..ee6237d --- /dev/null +++ b/routes/test/nameserver.json @@ -0,0 +1,19 @@ +{ + "id": 4095, + "gid": 4095, + "name": "b.ns.example.com.", + "description": "", + "address": "2.3.4.5", + "address6": "2001:DB8::2", + "remote_login": "nsd", + "logdir": "/foo", + "datadir": "/bar", + "export": { + "interval": 0, + "serials": true, + "status": "last run:03-05 15:25
last cp :09-20 12:59", + "type": "NSD" + }, + "ttl": 3600, + "deleted": false +} From 4e96c78c931eb36db90371fd7a519e6c96175b14 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 09:07:36 -0800 Subject: [PATCH 03/12] prettier --- lib/nameserver.js | 9 ++++++++- routes/nameserver.test.js | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/nameserver.js b/lib/nameserver.js index 2caa674..8e580aa 100644 --- a/lib/nameserver.js +++ b/lib/nameserver.js @@ -103,7 +103,14 @@ export default new Nameserver() function dbToObject(rows) { for (const row of rows) { - for (const f of ['description', 'address6', 'remote_login', 'datadir', 'logdir', 'export_status']) { + for (const f of [ + 'description', + 'address6', + 'remote_login', + 'datadir', + 'logdir', + 'export_status', + ]) { if ([undefined, null].includes(row[f])) row[f] = '' } for (const f of ['export']) { diff --git a/routes/nameserver.test.js b/routes/nameserver.test.js index 34c38ce..e862881 100644 --- a/routes/nameserver.test.js +++ b/routes/nameserver.test.js @@ -21,7 +21,6 @@ before(async () => { server = await init() }) - after(async () => { await server.stop() }) From 3c224aca0ae9016596b3d13f47d46945305f7842 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 10:22:44 -0800 Subject: [PATCH 04/12] test tweaks --- lib/nameserver.js | 16 +++++++++------- lib/nameserver.test.js | 3 ++- lib/util.js | 11 +++++------ routes/permission.test.js | 3 +-- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/nameserver.js b/lib/nameserver.js index 8e580aa..2aa28c8 100644 --- a/lib/nameserver.js +++ b/lib/nameserver.js @@ -16,6 +16,7 @@ class Nameserver { } if (args.export.type) { + args = JSON.parse(JSON.stringify(args)) const rows = await Mysql.execute( ...Mysql.select('SELECT id FROM nt_nameserver_export_type', { name: args.export.type, @@ -127,15 +128,16 @@ function dbToObject(rows) { } function objectToDb(row) { - const newRow = JSON.parse(JSON.stringify(row)) + row = JSON.parse(JSON.stringify(row)) // don't mutate the original + for (const f of ['export']) { for (const p of ['interval', 'serials', 'status']) { - if (newRow[f] === undefined) continue - if (newRow[f][p] === undefined) continue - newRow[`${f}_${p}`] = newRow[f][p] - delete newRow[f][p] + if (row[f] === undefined) continue + if (row[f][p] === undefined) continue + row[`${f}_${p}`] = row[f][p] + delete row[f][p] } - delete newRow[f] + delete row[f] } - return newRow + return row } diff --git a/lib/nameserver.test.js b/lib/nameserver.test.js index eea61ac..7684348 100644 --- a/lib/nameserver.test.js +++ b/lib/nameserver.test.js @@ -6,11 +6,12 @@ import Nameserver from './nameserver.js' import testCase from './test/nameserver.json' with { type: 'json' } before(async () => { + await Nameserver.destroy({ id: testCase.id }) await Nameserver.create(testCase) }) after(async () => { - Nameserver.destroy({ id: testCase.id }) + await Nameserver.destroy({ id: testCase.id }) Nameserver.mysql.disconnect() }) diff --git a/lib/util.js b/lib/util.js index cb06b11..469b2eb 100644 --- a/lib/util.js +++ b/lib/util.js @@ -24,16 +24,15 @@ const meta = { } function mapToDbColumn(args, maps) { - // create an instance, so we don't mangle the original args - const newArgs = JSON.parse(JSON.stringify(args)) + args = JSON.parse(JSON.stringify(args)) // don't mutate the original for (const [key, val] of Object.entries(maps)) { - if (newArgs[key] !== undefined) { - newArgs[val] = newArgs[key] - delete newArgs[key] + if (args[key] !== undefined) { + args[val] = args[key] + delete args[key] } } - return newArgs + return args } export { setEnv, meta, mapToDbColumn } diff --git a/routes/permission.test.js b/routes/permission.test.js index f595b67..b195d58 100644 --- a/routes/permission.test.js +++ b/routes/permission.test.js @@ -11,6 +11,7 @@ import userCase from './test/user.json' with { type: 'json' } import permCase from './test/permission.json' with { type: 'json' } let server +let case2Id = 4094 before(async () => { server = await init() @@ -19,8 +20,6 @@ before(async () => { await Permission.create(permCase) }) -let case2Id = 4094 - after(async () => { Permission.destroy({ id: case2Id }) await server.stop() From ccbfe7da162613bb3a10a017bf8d3cc71f879474 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 10:25:34 -0800 Subject: [PATCH 05/12] remove comment --- routes/permission.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/routes/permission.js b/routes/permission.js index 78369d4..af5c24f 100644 --- a/routes/permission.js +++ b/routes/permission.js @@ -50,9 +50,6 @@ function PermissionRoutes(server) { }, handler: async (request, h) => { const pid = await Permission.create(request.payload) - if (!pid) { - console.log(`POST /permission oops`) // TODO - } const permission = await Permission.get({ id: pid }) From 899769fa2d81d7d82bd67def25205c61a4729c97 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 12:14:19 -0800 Subject: [PATCH 06/12] group: updated validate names (_res, _req) --- routes/group.js | 21 +++++++++------- routes/group.test.js | 24 +++++-------------- routes/nameserver.js | 8 +++---- routes/nameserver.test.js | 3 ++- test.js | 50 ++++++++++++--------------------------- test.sh | 13 +++++++--- 6 files changed, 49 insertions(+), 70 deletions(-) diff --git a/routes/group.js b/routes/group.js index ffdbbaf..ad8a7aa 100644 --- a/routes/group.js +++ b/routes/group.js @@ -9,8 +9,11 @@ function GroupRoutes(server) { method: 'GET', path: '/group/{id}', options: { + validate: { + query: validate.group.GET_req, + }, response: { - schema: validate.group.GET, + schema: validate.group.GET_res, }, tags: ['api'], }, @@ -50,15 +53,12 @@ function GroupRoutes(server) { payload: validate.group.POST, }, response: { - schema: validate.group.GET, + schema: validate.group.GET_res, }, tags: ['api'], }, handler: async (request, h) => { const gid = await Group.create(request.payload) - if (!gid) { - console.log(`POST /group oops`) // TODO - } const groups = await Group.get({ id: gid }) @@ -77,13 +77,17 @@ function GroupRoutes(server) { method: 'DELETE', path: '/group/{id}', options: { + validate: { + query: validate.group.DELETE, + }, response: { - schema: validate.group.GET, + schema: validate.group.GET_res, }, tags: ['api'], }, handler: async (request, h) => { - const groups = await Group.get(request.params) + + const groups = await Group.get({ id: parseInt(request.params.id, 10) }) /* c8 ignore next 10 */ if (groups.length !== 1) { return h @@ -96,8 +100,7 @@ function GroupRoutes(server) { .code(204) } - const action = request.query.destroy === 'true' ? 'destroy' : 'delete' - await Group[action]({ id: groups[0].id }) + await Group.delete({ id: groups[0].id }) delete groups[0].gid return h diff --git a/routes/group.test.js b/routes/group.test.js index 2f69a17..74251f2 100644 --- a/routes/group.test.js +++ b/routes/group.test.js @@ -9,6 +9,7 @@ import groupCase from './test/group.json' with { type: 'json' } import userCase from './test/user.json' with { type: 'json' } let server +const case2Id = 4094 before(async () => { server = await init() @@ -17,7 +18,8 @@ before(async () => { }) after(async () => { - await server.stop() + await Group.destroy({ id: case2Id }) + server.stop() }) describe('group routes', () => { @@ -45,11 +47,9 @@ describe('group routes', () => { }, }) // console.log(res.result) - assert.equal(res.statusCode, 200) + assert.ok([200, 204].includes(res.statusCode)) }) - const case2Id = 4094 - it('POST /group', async () => { const testCase = JSON.parse(JSON.stringify(groupCase)) testCase.id = case2Id // make it unique @@ -78,7 +78,7 @@ describe('group routes', () => { }, }) // console.log(res.result) - assert.equal(res.statusCode, 200) + assert.ok([200, 204].includes(res.statusCode)) }) it(`DELETE /group/${case2Id}`, async () => { @@ -108,19 +108,7 @@ describe('group routes', () => { it(`GET /group/${case2Id} (deleted)`, async () => { const res = await server.inject({ method: 'GET', - url: `/group/${case2Id}?deleted=1`, - headers: { - Cookie: sessionCookie, - }, - }) - // console.log(res.result) - assert.equal(res.statusCode, 200) - }) - - it(`DELETE /group/${case2Id}`, async () => { - const res = await server.inject({ - method: 'DELETE', - url: `/group/${case2Id}?destroy=true`, + url: `/group/${case2Id}?deleted=true`, headers: { Cookie: sessionCookie, }, diff --git a/routes/nameserver.js b/routes/nameserver.js index fe306a2..3bc413b 100644 --- a/routes/nameserver.js +++ b/routes/nameserver.js @@ -10,10 +10,10 @@ function NameserverRoutes(server) { path: '/nameserver/{id}', options: { validate: { - query: validate.nameserver.v3, + query: validate.nameserver.GET_req, }, response: { - schema: validate.nameserver.GET, + schema: validate.nameserver.GET_res, }, tags: ['api'], }, @@ -44,7 +44,7 @@ function NameserverRoutes(server) { payload: validate.nameserver.POST, }, response: { - schema: validate.nameserver.GET, + schema: validate.nameserver.GET_res, }, tags: ['api'], }, @@ -72,7 +72,7 @@ function NameserverRoutes(server) { query: validate.nameserver.DELETE, }, response: { - schema: validate.nameserver.GET, + schema: validate.nameserver.GET_res, }, tags: ['api'], }, diff --git a/routes/nameserver.test.js b/routes/nameserver.test.js index e862881..8bcd0e2 100644 --- a/routes/nameserver.test.js +++ b/routes/nameserver.test.js @@ -22,7 +22,8 @@ before(async () => { }) after(async () => { - await server.stop() + await Nameserver.destroy({ id: case2Id }) + server.stop() }) describe('nameserver routes', () => { diff --git a/test.js b/test.js index 27ab6a4..177d6d6 100644 --- a/test.js +++ b/test.js @@ -6,11 +6,13 @@ import Group from './lib/group.js' import User from './lib/user.js' import Session from './lib/session.js' import Permission from './lib/permission.js' +import Nameserver from './lib/nameserver.js' import groupCase from './lib/test/group.json' with { type: 'json' } import userCase from './lib/test/user.json' with { type: 'json' } import groupCaseR from './routes/test/group.json' with { type: 'json' } import userCaseR from './routes/test/user.json' with { type: 'json' } +import nsCaseR from './routes/test/nameserver.json' with { type: 'json' } switch (process.argv[2]) { case 'setup': @@ -26,22 +28,14 @@ switch (process.argv[2]) { } async function setup() { - await createTestGroup() - await createTestUser() - // await createTestSession() - await User.mysql.disconnect() - await Group.mysql.disconnect() - process.exit(0) -} - -async function createTestGroup() { await Group.create(groupCase) await Group.create(groupCaseR) -} - -async function createTestUser() { await User.create(userCase) await User.create(userCaseR) + // await createTestSession() + await User.mysql.disconnect() + await Group.mysql.disconnect() + process.exit(0) } // async function createTestSession() { @@ -53,30 +47,16 @@ async function createTestUser() { // } async function teardown() { - await destroyTestSession() - await destroyTestUser() - await destroyTestGroup() - await destroyTestPermission() + await Nameserver.destroy({ id: nsCaseR.id }) + await Nameserver.destroy({ id: nsCaseR.id -1 }) + await Permission.destroy({ id: userCase.id }) + await Permission.destroy({ id: userCase.id - 1 }) + await Session.delete({ nt_user_id: userCase.id }) + await User.destroy({ id: userCase.id }) + await User.destroy({ id: userCaseR.id }) + await Group.destroy({ id: groupCase.id }) + await Group.destroy({ id: groupCaseR.id }) await User.mysql.disconnect() await Group.mysql.disconnect() process.exit(0) } - -async function destroyTestGroup() { - await Group.destroy({ id: groupCase.id }) - await Group.destroy({ id: groupCaseR.id }) -} - -async function destroyTestUser() { - await User.destroy({ id: userCase.id }) - await User.destroy({ id: userCaseR.id }) -} - -async function destroyTestSession() { - await Session.delete({ nt_user_id: userCase.id }) -} - -async function destroyTestPermission() { - await Permission.destroy({ id: userCase.id }) - await Permission.destroy({ id: userCase.id - 1 }) -} diff --git a/test.sh b/test.sh index f3edafe..f3ac027 100755 --- a/test.sh +++ b/test.sh @@ -1,10 +1,19 @@ #!/bin/sh +set -euo pipefail + NODE="node --no-warnings=ExperimentalWarning" $NODE test.js teardown $NODE test.js setup -if [ "$1" = "watch" ]; then +function cleanup { + echo "cleaning DB objects" + $NODE test.js teardown +} + +trap cleanup EXIT SIGINT SIGKILL + +if [ $# -ge 1 ] && [ "$1" = "watch" ]; then $NODE --test --watch else # if [ -n "$GITHUB_WORKFLOW" ]; then @@ -16,5 +25,3 @@ else fi # npx mocha --exit --no-warnings=ExperimentalWarning lib/*.test.js routes/*.test.js - -$NODE test.js teardown From 63bd0ab3a14fb181e45e4ae31497ed861cdf2ee6 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 12:48:59 -0800 Subject: [PATCH 07/12] validate.GET_re? --- routes/group.js | 1 - routes/permission.js | 8 ++++---- routes/session.js | 2 +- routes/user.js | 19 ++++++++++++++----- routes/user.test.js | 2 +- test.js | 2 +- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/routes/group.js b/routes/group.js index ad8a7aa..58f63f4 100644 --- a/routes/group.js +++ b/routes/group.js @@ -86,7 +86,6 @@ function GroupRoutes(server) { tags: ['api'], }, handler: async (request, h) => { - const groups = await Group.get({ id: parseInt(request.params.id, 10) }) /* c8 ignore next 10 */ if (groups.length !== 1) { diff --git a/routes/permission.js b/routes/permission.js index af5c24f..99fdbe9 100644 --- a/routes/permission.js +++ b/routes/permission.js @@ -10,10 +10,10 @@ function PermissionRoutes(server) { path: '/permission/{id}', options: { validate: { - query: validate.permission.v3, + query: validate.permission.GET_req, }, response: { - schema: validate.permission.GET, + schema: validate.permission.GET_res, }, tags: ['api'], }, @@ -44,7 +44,7 @@ function PermissionRoutes(server) { payload: validate.permission.POST, }, response: { - schema: validate.permission.GET, + schema: validate.permission.GET_res, }, tags: ['api'], }, @@ -72,7 +72,7 @@ function PermissionRoutes(server) { query: validate.permission.DELETE, }, response: { - schema: validate.permission.GET, + schema: validate.permission.GET_res, }, tags: ['api'], }, diff --git a/routes/session.js b/routes/session.js index 46a6a09..ee2cea4 100644 --- a/routes/session.js +++ b/routes/session.js @@ -12,7 +12,7 @@ function SessionRoutes(server) { path: '/session', options: { response: { - schema: validate.session.GET, + schema: validate.session.GET_res, }, tags: ['api'], }, diff --git a/routes/user.js b/routes/user.js index 8a3e093..06a1277 100644 --- a/routes/user.js +++ b/routes/user.js @@ -9,8 +9,11 @@ function UserRoutes(server) { method: 'GET', path: '/user', options: { + validate: { + query: validate.user.GET_req, + }, response: { - schema: validate.user.GET, + schema: validate.user.GET_res, }, // tags: ['api'], }, @@ -35,8 +38,11 @@ function UserRoutes(server) { method: 'GET', path: '/user/{id}', options: { + validate: { + query: validate.user.GET_req, + }, response: { - schema: validate.user.GET, + schema: validate.user.GET_res, }, tags: ['api'], }, @@ -76,10 +82,10 @@ function UserRoutes(server) { path: '/user', options: { validate: { - payload: validate.user.userPOST, + payload: validate.user.POST, }, response: { - schema: validate.user.GET, + schema: validate.user.GET_res, }, tags: ['api'], }, @@ -109,8 +115,11 @@ function UserRoutes(server) { method: 'DELETE', path: '/user/{id}', options: { + validate: { + query: validate.user.DELETE, + }, response: { - schema: validate.user.GET, + schema: validate.user.GET_res, }, tags: ['api'], }, diff --git a/routes/user.test.js b/routes/user.test.js index 32829a3..27f541e 100644 --- a/routes/user.test.js +++ b/routes/user.test.js @@ -122,7 +122,7 @@ describe('user routes', () => { it(`GET /user/${userId2}?deleted=1`, async () => { const res = await server.inject({ method: 'GET', - url: `/user/${userId2}?deleted=1`, + url: `/user/${userId2}?deleted=true`, headers: { Cookie: sessionCookie, }, diff --git a/test.js b/test.js index 177d6d6..cba2bd4 100644 --- a/test.js +++ b/test.js @@ -48,7 +48,7 @@ async function setup() { async function teardown() { await Nameserver.destroy({ id: nsCaseR.id }) - await Nameserver.destroy({ id: nsCaseR.id -1 }) + await Nameserver.destroy({ id: nsCaseR.id - 1 }) await Permission.destroy({ id: userCase.id }) await Permission.destroy({ id: userCase.id - 1 }) await Session.delete({ nt_user_id: userCase.id }) From ab6a94e4f9ec444c6c80708d1f4f496ab2c243d3 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 12:51:54 -0800 Subject: [PATCH 08/12] test: remove -o bash option --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index f3ac027..551830a 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -euo pipefail +set -eu NODE="node --no-warnings=ExperimentalWarning" $NODE test.js teardown From 73262127367a6090a82629a701115c7fdefbc05c Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 12:53:58 -0800 Subject: [PATCH 09/12] not bash --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 551830a..4ce7b09 100755 --- a/test.sh +++ b/test.sh @@ -6,7 +6,7 @@ NODE="node --no-warnings=ExperimentalWarning" $NODE test.js teardown $NODE test.js setup -function cleanup { +cleanup() { echo "cleaning DB objects" $NODE test.js teardown } From 2b335568a2d9be7915a967185d75dad4e1446b5b Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 12:56:26 -0800 Subject: [PATCH 10/12] traps by number --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 4ce7b09..1feccad 100755 --- a/test.sh +++ b/test.sh @@ -11,7 +11,7 @@ cleanup() { $NODE test.js teardown } -trap cleanup EXIT SIGINT SIGKILL +trap cleanup EXIT 1 2 3 6 if [ $# -ge 1 ] && [ "$1" = "watch" ]; then $NODE --test --watch From 03341b123db0f9e6d7380d4f23219e1d38b7eaf3 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 13:20:53 -0800 Subject: [PATCH 11/12] update validate version --- package.json | 2 +- routes/group.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9c6e38c..9e20c74 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@hapi/hoek": "^11.0.4", "@hapi/inert": "^7.1.0", "@hapi/vision": "^7.0.3", - "@nictool/validate": "^0.7.4", + "@nictool/validate": "^0.8.0", "hapi-swagger": "^17.2.1", "mysql2": "^3.9.2", "qs": "^6.11.2", diff --git a/routes/group.test.js b/routes/group.test.js index 74251f2..12d8a58 100644 --- a/routes/group.test.js +++ b/routes/group.test.js @@ -114,7 +114,7 @@ describe('group routes', () => { }, }) // console.log(res.result) - assert.equal(res.statusCode, 200) + assert.ok([200, 204].includes(res.statusCode)) }) it('DELETE /session', async () => { From 21c638e6a4622d92cbfc368b9ab07fc8b518a433 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Wed, 6 Mar 2024 13:38:38 -0800 Subject: [PATCH 12/12] coverage --- routes/nameserver.js | 2 +- routes/nameserver.test.js | 12 ++++++++++++ routes/permission.test.js | 12 ++++++++++++ routes/user.js | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/routes/nameserver.js b/routes/nameserver.js index 3bc413b..bc792e6 100644 --- a/routes/nameserver.js +++ b/routes/nameserver.js @@ -82,7 +82,7 @@ function NameserverRoutes(server) { id: parseInt(request.params.id, 10), }) - if (!nameservers) { + if (nameservers.length === 0) { return h .response({ meta: { diff --git a/routes/nameserver.test.js b/routes/nameserver.test.js index 8bcd0e2..c02499a 100644 --- a/routes/nameserver.test.js +++ b/routes/nameserver.test.js @@ -99,6 +99,18 @@ describe('nameserver routes', () => { assert.equal(res.statusCode, 200) }) + it(`DELETE /nameserver/${case2Id}`, async () => { + const res = await server.inject({ + method: 'DELETE', + url: `/nameserver/${case2Id}`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + assert.equal(res.statusCode, 404) + }) + it(`GET /nameserver/${case2Id}`, async () => { const res = await server.inject({ method: 'GET', diff --git a/routes/permission.test.js b/routes/permission.test.js index b195d58..43b3ca9 100644 --- a/routes/permission.test.js +++ b/routes/permission.test.js @@ -104,6 +104,18 @@ describe('permission routes', () => { assert.equal(res.statusCode, 200) }) + it(`DELETE /permission/${case2Id}`, async () => { + const res = await server.inject({ + method: 'DELETE', + url: `/permission/${case2Id}`, + headers: { + Cookie: sessionCookie, + }, + }) + // console.log(res.result) + assert.equal(res.statusCode, 404) + }) + it(`GET /permission/${case2Id}`, async () => { const res = await server.inject({ method: 'GET', diff --git a/routes/user.js b/routes/user.js index 06a1277..288cd66 100644 --- a/routes/user.js +++ b/routes/user.js @@ -47,10 +47,12 @@ function UserRoutes(server) { tags: ['api'], }, handler: async (request, h) => { + const users = await User.get({ deleted: request.query.deleted ?? 0, id: parseInt(request.params.id, 10), }) + if (users.length !== 1) { return h .response({