Adding more test cases for the controllers, tables and views weren't as well covered as required.

This commit is contained in:
mike12345567 2021-03-15 16:36:38 +00:00
parent 3406138f34
commit 794372987e
11 changed files with 241 additions and 107 deletions

View File

@ -65,12 +65,14 @@ exports.save = async function(ctx) {
// Don't rename if the name is the same // Don't rename if the name is the same
let { _rename } = tableToSave let { _rename } = tableToSave
/* istanbul ignore next */
if (_rename && _rename.old === _rename.updated) { if (_rename && _rename.old === _rename.updated) {
_rename = null _rename = null
delete tableToSave._rename delete tableToSave._rename
} }
// rename row fields when table column is renamed // rename row fields when table column is renamed
/* istanbul ignore next */
if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) { if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
ctx.throw(400, "Cannot rename a linked column.") ctx.throw(400, "Cannot rename a linked column.")
} else if (_rename && tableToSave.primaryDisplay === _rename.old) { } else if (_rename && tableToSave.primaryDisplay === _rename.old) {
@ -159,7 +161,7 @@ exports.destroy = async function(ctx) {
ctx.eventEmitter && ctx.eventEmitter &&
ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete) ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete)
ctx.status = 200 ctx.status = 200
ctx.message = `Table ${ctx.params.tableId} deleted.` ctx.body = { message: `Table ${ctx.params.tableId} deleted.` }
} }
exports.validateCSVSchema = async function(ctx) { exports.validateCSVSchema = async function(ctx) {

View File

@ -90,7 +90,8 @@ exports.handleDataImport = async (user, table, dataImport) => {
return table return table
} }
exports.handleSearchIndexes = async (db, table) => { exports.handleSearchIndexes = async (appId, table) => {
const db = new CouchDB(appId)
// create relevant search indexes // create relevant search indexes
if (table.indexes && table.indexes.length > 0) { if (table.indexes && table.indexes.length > 0) {
const currentIndexes = await db.getIndexes() const currentIndexes = await db.getIndexes()
@ -150,6 +151,9 @@ class TableSaveFunctions {
constructor({ db, ctx, oldTable, dataImport }) { constructor({ db, ctx, oldTable, dataImport }) {
this.db = db this.db = db
this.ctx = ctx this.ctx = ctx
if (this.ctx && this.ctx.user) {
this.appId = this.ctx.user.appId
}
this.oldTable = oldTable this.oldTable = oldTable
this.dataImport = dataImport this.dataImport = dataImport
// any rows that need updated // any rows that need updated
@ -178,7 +182,7 @@ class TableSaveFunctions {
// after saving // after saving
async after(table) { async after(table) {
table = await exports.handleSearchIndexes(this.db, table) table = await exports.handleSearchIndexes(this.appId, table)
table = await exports.handleDataImport( table = await exports.handleDataImport(
this.ctx.user, this.ctx.user,
table, table,

View File

@ -29,11 +29,13 @@ const controller = {
save: async ctx => { save: async ctx => {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
const { originalName, ...viewToSave } = ctx.request.body const { originalName, ...viewToSave } = ctx.request.body
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
const view = viewTemplate(viewToSave) const view = viewTemplate(viewToSave)
if (!viewToSave.name) {
ctx.throw(400, "Cannot create view without a name")
}
designDoc.views = { designDoc.views = {
...designDoc.views, ...designDoc.views,
[viewToSave.name]: view, [viewToSave.name]: view,
@ -60,17 +62,16 @@ const controller = {
await db.put(table) await db.put(table)
ctx.body = table.views[viewToSave.name] ctx.body = {
ctx.message = `View ${viewToSave.name} saved successfully.` ...table.views[viewToSave.name],
name: viewToSave.name,
}
}, },
destroy: async ctx => { destroy: async ctx => {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
const viewName = decodeURI(ctx.params.viewName) const viewName = decodeURI(ctx.params.viewName)
const view = designDoc.views[viewName] const view = designDoc.views[viewName]
delete designDoc.views[viewName] delete designDoc.views[viewName]
await db.put(designDoc) await db.put(designDoc)
@ -80,16 +81,17 @@ const controller = {
await db.put(table) await db.put(table)
ctx.body = view ctx.body = view
ctx.message = `View ${ctx.params.viewName} saved successfully.`
}, },
exportView: async ctx => { exportView: async ctx => {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
const viewName = decodeURI(ctx.query.view) const viewName = decodeURI(ctx.query.view)
const view = designDoc.views[viewName] const view = designDoc.views[viewName]
const format = ctx.query.format const format = ctx.query.format
if (!format) {
ctx.throw(400, "Format must be specified, either csv or json")
}
if (view) { if (view) {
ctx.params.viewName = viewName ctx.params.viewName = viewName
@ -102,6 +104,7 @@ const controller = {
} }
} else { } else {
// table all_ view // table all_ view
/* istanbul ignore next */
ctx.params.viewName = viewName ctx.params.viewName = viewName
} }

View File

@ -1,6 +1,7 @@
const setup = require("./utilities") const setup = require("./utilities")
const tableUtils = require("../../controllers/table/utils")
describe("/analytics", () => { describe("run misc tests", () => {
let request = setup.getRequest() let request = setup.getRequest()
let config = setup.getConfig() let config = setup.getConfig()
@ -10,29 +11,44 @@ describe("/analytics", () => {
await config.init() await config.init()
}) })
describe("isEnabled", () => { describe("/analytics", () => {
it("check if analytics enabled", async () => { it("check if analytics enabled", async () => {
const res = await request const res = await request
.get(`/api/analytics`) .get(`/api/analytics`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(typeof res.body.enabled).toEqual("boolean") expect(typeof res.body.enabled).toEqual("boolean")
})
})
describe("/health", () => {
it("should confirm healthy", async () => {
await request.get("/health").expect(200)
}) })
}) })
})
describe("/health", () => { describe("/version", () => {
it("should confirm healthy", async () => { it("should confirm version", async () => {
let config = setup.getConfig() const res = await request.get("/version").expect(200)
await config.getRequest().get("/health").expect(200) expect(res.text.split(".").length).toEqual(3)
})
}) })
})
describe("/version", () => { describe("test table utilities", () => {
it("should confirm version", async () => { it("should be able to import a CSV", async () => {
const config = setup.getConfig() const table = await config.createTable()
const res = await config.getRequest().get("/version").expect(200) const dataImport = {
expect(res.text.split(".").length).toEqual(3) csvString: "a,b,c,d\n1,2,3,4"
}
await tableUtils.handleDataImport({
appId: config.getAppId(),
userId: "test",
}, table, dataImport)
const rows = await config.getRows()
expect(rows[0].a).toEqual("1")
expect(rows[0].b).toEqual("2")
expect(rows[0].c).toEqual("3")
})
}) })
}) })

View File

@ -348,7 +348,7 @@ describe("/rows", () => {
const view = await config.createView() const view = await config.createView()
const row = await config.createRow() const row = await config.createRow()
const res = await request const res = await request
.get(`/api/views/${view._id}`) .get(`/api/views/${view.name}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)

View File

@ -1,5 +1,6 @@
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint, getDB } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicTable } = setup.structures
describe("/tables", () => { describe("/tables", () => {
let request = setup.getRequest() let request = setup.getRequest()
@ -12,25 +13,22 @@ describe("/tables", () => {
}) })
describe("create", () => { describe("create", () => {
it("returns a success message when the table is successfully created", done => { it("returns a success message when the table is successfully created", async () => {
request const res = await request
.post(`/api/tables`) .post(`/api/tables`)
.send({ .send({
name: "TestTable", name: "TestTable",
key: "name", key: "name",
schema: { schema: {
name: { type: "string" } name: {type: "string"}
} }
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (err, res) => { expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.")
expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.") expect(res.body.name).toEqual("TestTable")
expect(res.body.name).toEqual("TestTable") })
done()
})
})
it("renames all the row fields for a table when a schema key is renamed", async () => { it("renames all the row fields for a table when a schema key is renamed", async () => {
const testTable = await config.createTable() const testTable = await config.createTable()
@ -46,7 +44,7 @@ describe("/tables", () => {
const updatedTable = await request const updatedTable = await request
.post(`/api/tables`) .post(`/api/tables`)
.send({ .send({
_id: testTable._id, _id: testTable._id,
_rev: testTable._rev, _rev: testTable._rev,
name: "TestTable", name: "TestTable",
@ -56,41 +54,40 @@ describe("/tables", () => {
updated: "updatedName" updated: "updatedName"
}, },
schema: { schema: {
updatedName: { type: "string" } updatedName: {type: "string"}
} }
}) })
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(updatedTable.res.statusMessage).toEqual("Table TestTable saved successfully.")
expect(updatedTable.body.name).toEqual("TestTable")
expect(updatedTable.res.statusMessage).toEqual("Table TestTable saved successfully.") const res = await request
expect(updatedTable.body.name).toEqual("TestTable") .get(`/api/${testTable._id}/rows/${testRow.body._id}`)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
const res = await request expect(res.body.updatedName).toEqual("test")
.get(`/api/${testTable._id}/rows/${testRow.body._id}`) expect(res.body.name).toBeUndefined()
.set(config.defaultHeaders()) })
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.updatedName).toEqual("test") it("should apply authorization to endpoint", async () => {
expect(res.body.name).toBeUndefined() await checkBuilderEndpoint({
}) config,
method: "POST",
it("should apply authorization to endpoint", async () => { url: `/api/tables`,
await checkBuilderEndpoint({ body: {
config, name: "TestTable",
method: "POST", key: "name",
url: `/api/tables`, schema: {
body: { name: {type: "string"}
name: "TestTable",
key: "name",
schema: {
name: { type: "string" }
}
} }
}) }
}) })
}) })
})
describe("fetch", () => { describe("fetch", () => {
let testTable let testTable
@ -103,28 +100,91 @@ describe("/tables", () => {
delete testTable._rev delete testTable._rev
}) })
it("returns all the tables for that instance in the response body", done => { it("returns all the tables for that instance in the response body", async () => {
request const res = await request
.get(`/api/tables`) .get(`/api/tables`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { const fetchedTable = res.body[0]
const fetchedTable = res.body[0] expect(fetchedTable.name).toEqual(testTable.name)
expect(fetchedTable.name).toEqual(testTable.name) expect(fetchedTable.type).toEqual("table")
expect(fetchedTable.type).toEqual("table")
done()
})
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await checkBuilderEndpoint({ await checkBuilderEndpoint({
config, config,
method: "GET", method: "GET",
url: `/api/tables`, url: `/api/tables`,
})
}) })
}) })
})
describe("indexing", () => {
it("should be able to create a table with indexes", async () => {
const db = getDB(config)
const indexCount = (await db.getIndexes()).total_rows
const table = basicTable()
table.indexes = ["name"]
const res = await request
.post(`/api/tables`)
.send(table)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.body._id).toBeDefined()
expect(res.body._rev).toBeDefined()
expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1)
// update index to see what happens
table.indexes = ["name", "description"]
await request
.post(`/api/tables`)
.send({
...table,
_id: res.body._id,
_rev: res.body._rev,
})
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
// shouldn't have created a new index
expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1)
})
})
describe("updating user table", () => {
it("should add roleId and email field when adjusting user table schema", async () => {
const res = await request
.post(`/api/tables`)
.send({
...basicTable(),
_id: "ta_users",
})
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.schema.email).toBeDefined()
expect(res.body.schema.roleId).toBeDefined()
})
})
describe("validate csv", () => {
it("should be able to validate a CSV layout", async () => {
const res = await request
.post(`/api/tables/csv/validate`)
.send({
csvString: "a,b,c,d\n1,2,3,4"
})
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.schema).toBeDefined()
expect(res.body.schema.a).toEqual({
type: "string",
success: true,
})
})
})
describe("destroy", () => { describe("destroy", () => {
let testTable let testTable
@ -137,19 +197,16 @@ describe("/tables", () => {
delete testTable._rev delete testTable._rev
}) })
it("returns a success response when a table is deleted.", async done => { it("returns a success response when a table is deleted.", async () => {
request const res = await request
.delete(`/api/tables/${testTable._id}/${testTable._rev}`) .delete(`/api/tables/${testTable._id}/${testTable._rev}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { expect(res.body.message).toEqual(`Table ${testTable._id} deleted.`)
expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`) })
done()
})
})
it("deletes linked references to the table after deletion", async done => { it("deletes linked references to the table after deletion", async () => {
const linkedTable = await config.createTable({ const linkedTable = await config.createTable({
name: "LinkedTable", name: "LinkedTable",
type: "table", type: "table",
@ -171,18 +228,15 @@ describe("/tables", () => {
}, },
}) })
request const res = await request
.delete(`/api/tables/${testTable._id}/${testTable._rev}`) .delete(`/api/tables/${testTable._id}/${testTable._rev}`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { expect(res.body.message).toEqual(`Table ${testTable._id} deleted.`)
expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`) const dependentTable = await config.getTable(linkedTable._id)
const dependentTable = await config.getTable(linkedTable._id) expect(dependentTable.schema.TestTable).not.toBeDefined()
expect(dependentTable.schema.TestTable).not.toBeDefined() })
done()
})
})
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await checkBuilderEndpoint({ await checkBuilderEndpoint({
@ -191,6 +245,5 @@ describe("/tables", () => {
url: `/api/tables/${testTable._id}/${testTable._rev}`, url: `/api/tables/${testTable._id}/${testTable._rev}`,
}) })
}) })
}) })
}) })

View File

@ -1,5 +1,6 @@
const rowController = require("../../../controllers/row") const rowController = require("../../../controllers/row")
const appController = require("../../../controllers/application") const appController = require("../../../controllers/application")
const CouchDB = require("../../../../db")
function Request(appId, params) { function Request(appId, params) {
this.user = { appId } this.user = { appId }
@ -77,3 +78,7 @@ exports.checkPermissionsEndpoint = async ({
.set(failHeader) .set(failHeader)
.expect(403) .expect(403)
} }
exports.getDB = config => {
return new CouchDB(config.getAppId())
}

View File

@ -29,9 +29,7 @@ describe("/views", () => {
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual( expect(res.body.tableId).toBe(table._id)
"View TestView saved successfully."
)
}) })
it("updates the table row with the new view metadata", async () => { it("updates the table row with the new view metadata", async () => {
@ -46,10 +44,8 @@ describe("/views", () => {
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.tableId).toBe(table._id)
expect(res.res.statusMessage).toEqual(
"View TestView saved successfully."
)
const updatedTable = await config.getTable(table._id) const updatedTable = await config.getTable(table._id)
expect(updatedTable.views).toEqual({ expect(updatedTable.views).toEqual({
TestView: { TestView: {
@ -173,4 +169,49 @@ describe("/views", () => {
expect(res.body).toMatchSnapshot() expect(res.body).toMatchSnapshot()
}) })
}) })
describe("destroy", () => {
it("should be able to delete a view", async () => {
const table = await config.createTable()
const view = await config.createView()
const res = await request
.delete(`/api/views/${view.name}`)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.body.map).toBeDefined()
expect(res.body.meta.tableId).toEqual(table._id)
})
})
describe("exportView", () => {
it("should be able to delete a view", async () => {
await config.createTable()
await config.createRow()
const view = await config.createView()
let res = await request
.get(`/api/views/export?view=${view.name}&format=json`)
.set(config.defaultHeaders())
.expect(200)
let error
try {
const obj = JSON.parse(res.text)
expect(obj.length).toBe(1)
} catch (err) {
error = err
}
expect(error).toBeUndefined()
res = await request
.get(`/api/views/export?view=${view.name}&format=csv`)
.set(config.defaultHeaders())
.expect(200)
// this shouldn't be JSON
try {
JSON.parse(res.text)
} catch (err) {
error = err
}
expect(error).toBeDefined()
})
})
}) })

View File

@ -3,11 +3,11 @@ const usageQuota = require("../../utilities/usageQuota")
const thread = require("../thread") const thread = require("../thread")
const triggers = require("../triggers") const triggers = require("../triggers")
const { basicAutomation, basicTable } = require("../../tests/utilities/structures") const { basicAutomation, basicTable } = require("../../tests/utilities/structures")
const TestConfig = require("../../tests/utilities/TestConfiguration")
const { wait } = require("../../utilities") const { wait } = require("../../utilities")
const env = require("../../environment") const env = require("../../environment")
const { makePartial } = require("../../tests/utilities") const { makePartial } = require("../../tests/utilities")
const { cleanInputValues } = require("../automationUtils") const { cleanInputValues } = require("../automationUtils")
const setup = require("./utilities")
let workerJob let workerJob
@ -31,13 +31,14 @@ jest.mock("worker-farm", () => {
}) })
describe("Run through some parts of the automations system", () => { describe("Run through some parts of the automations system", () => {
let config = new TestConfig(false) let config = setup.getConfig()
beforeEach(async () => { beforeEach(async () => {
await automation.init() await automation.init()
await config.init() await config.init()
}) })
afterAll(setup.afterAll)
it("should be able to init in builder", async () => { it("should be able to init in builder", async () => {
await triggers.externalTrigger(basicAutomation(), { a: 1 }) await triggers.externalTrigger(basicAutomation(), { a: 1 })

View File

@ -30,6 +30,7 @@ const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS)
allDbs(Pouch) allDbs(Pouch)
// replicate your local levelDB pouch to a running HTTP compliant couch or pouchdb server. // replicate your local levelDB pouch to a running HTTP compliant couch or pouchdb server.
/* istanbul ignore next */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function replicateLocal() { function replicateLocal() {
Pouch.allDbs().then(dbs => { Pouch.allDbs().then(dbs => {

View File

@ -171,6 +171,13 @@ class TestConfiguration {
return this._req(null, { tableId, rowId }, controllers.row.find) return this._req(null, { tableId, rowId }, controllers.row.find)
} }
async getRows(tableId) {
if (!tableId && this.table) {
tableId = this.table._id
}
return this._req(null, { tableId }, controllers.row.fetchTableRows)
}
async createRole(config = null) { async createRole(config = null) {
config = config || basicRole() config = config || basicRole()
return this._req(config, null, controllers.role.save) return this._req(config, null, controllers.role.save)
@ -195,6 +202,7 @@ class TestConfiguration {
const view = config || { const view = config || {
map: "function(doc) { emit(doc[doc.key], doc._id); } ", map: "function(doc) { emit(doc[doc.key], doc._id); } ",
tableId: this.table._id, tableId: this.table._id,
name: "ViewTest",
} }
return this._req(view, null, controllers.view.save) return this._req(view, null, controllers.view.save)
} }