Merge pull request #1252 from Budibase/feature/test-refactor

Server test refactor
This commit is contained in:
Michael Drury 2021-03-05 13:43:18 +00:00 committed by GitHub
commit 1f03851f0d
20 changed files with 816 additions and 921 deletions

View File

@ -52,7 +52,8 @@
"collectCoverageFrom": [ "collectCoverageFrom": [
"src/**/*.js", "src/**/*.js",
"!**/node_modules/**", "!**/node_modules/**",
"!src/db/views/*.js" "!src/db/views/*.js",
"!src/api/routes/tests"
], ],
"coverageReporters": [ "coverageReporters": [
"lcov", "lcov",

View File

@ -8,14 +8,13 @@ const {
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const database = new CouchDB(ctx.user.appId) const database = new CouchDB(ctx.user.appId)
const datasources = ( ctx.body = (
await database.allDocs( await database.allDocs(
getDatasourceParams(null, { getDatasourceParams(null, {
include_docs: true, include_docs: true,
}) })
) )
).rows.map(row => row.doc) ).rows.map(row => row.doc)
ctx.body = datasources
} }
exports.save = async function(ctx) { exports.save = async function(ctx) {

View File

@ -1,25 +1,15 @@
const { const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions")
createApplication, const setup = require("./utilities")
builderEndpointShouldBlockNormalUsers,
supertest,
clearApplications,
defaultHeaders,
} = require("./couchTestUtils")
describe("/applications", () => { describe("/applications", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
beforeAll(async () => { afterAll(setup.afterAll)
({ request, server } = await supertest())
});
beforeEach(async () => { beforeEach(async () => {
await clearApplications(request) await clearAllApps()
}) await config.init()
afterAll(() => {
server.close()
}) })
describe("create", () => { describe("create", () => {
@ -27,7 +17,7 @@ describe("/applications", () => {
const res = await request const res = await request
.post("/api/applications") .post("/api/applications")
.send({ name: "My App" }) .send({ name: "My App" })
.set(defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual("Application My App created successfully") expect(res.res.statusMessage).toEqual("Application My App created successfully")
@ -35,41 +25,35 @@ describe("/applications", () => {
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
const otherApplication = await createApplication(request) await checkBuilderEndpoint({
const appId = otherApplication.instance._id config,
await builderEndpointShouldBlockNormalUsers({
request,
method: "POST", method: "POST",
url: `/api/applications`, url: `/api/applications`,
appId: appId,
body: { name: "My App" } body: { name: "My App" }
}) })
}) })
}) })
describe("fetch", () => { describe("fetch", () => {
it("lists all applications", async () => { it("lists all applications", async () => {
await createApplication(request, "app1") await config.createApp(request, "app1")
await createApplication(request, "app2") await config.createApp(request, "app2")
const res = await request const res = await request
.get("/api/applications") .get("/api/applications")
.set(defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body.length).toBe(2) // two created apps + the inited app
expect(res.body.length).toBe(3)
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
const otherApplication = await createApplication(request) await checkBuilderEndpoint({
const appId = otherApplication.instance._id config,
await builderEndpointShouldBlockNormalUsers({
request,
method: "GET", method: "GET",
url: `/api/applications`, url: `/api/applications`,
appId: appId,
}) })
}) })
}) })

View File

@ -1,74 +1,33 @@
const { const {
createApplication, checkBuilderEndpoint,
createTable, getAllTableRows,
getAllFromTable, clearAllAutomations,
defaultHeaders, } = require("./utilities/TestFunctions")
supertest, const { basicAutomation } = require("./utilities/structures")
insertDocument, const setup = require("./utilities")
destroyDocument,
builderEndpointShouldBlockNormalUsers
} = require("./couchTestUtils")
let { generateAutomationID } = require("../../../db/utils")
const { delay } = require("./testUtils")
const MAX_RETRIES = 4 const MAX_RETRIES = 4
const AUTOMATION_ID = generateAutomationID()
const TEST_AUTOMATION = {
_id: AUTOMATION_ID,
name: "My Automation",
screenId: "kasdkfldsafkl",
live: true,
uiTree: {
},
definition: {
trigger: {},
steps: [
],
},
type: "automation",
}
let ACTION_DEFINITIONS = {} let ACTION_DEFINITIONS = {}
let TRIGGER_DEFINITIONS = {} let TRIGGER_DEFINITIONS = {}
let LOGIC_DEFINITIONS = {} let LOGIC_DEFINITIONS = {}
describe("/automations", () => { describe("/automations", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let app
let appId
let automation let automation
let automationId
beforeAll(async () => { afterAll(setup.afterAll)
({ request, server } = await supertest())
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id
if (automation) await destroyDocument(automation.id)
}) })
afterAll(() => { const triggerWorkflow = async automation => {
server.close()
})
const createAutomation = async () => {
automation = await insertDocument(appId, {
type: "automation",
...TEST_AUTOMATION
})
automation = { ...automation, ...TEST_AUTOMATION }
}
const triggerWorkflow = async (automationId) => {
return await request return await request
.post(`/api/automations/${automationId}/trigger`) .post(`/api/automations/${automation._id}/trigger`)
.send({ name: "Test", description: "TEST" }) .send({ name: "Test", description: "TEST" })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
} }
@ -77,7 +36,7 @@ describe("/automations", () => {
it("returns a list of definitions for actions", async () => { it("returns a list of definitions for actions", async () => {
const res = await request const res = await request
.get(`/api/automations/action/list`) .get(`/api/automations/action/list`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -88,7 +47,7 @@ describe("/automations", () => {
it("returns a list of definitions for triggers", async () => { it("returns a list of definitions for triggers", async () => {
const res = await request const res = await request
.get(`/api/automations/trigger/list`) .get(`/api/automations/trigger/list`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -99,7 +58,7 @@ describe("/automations", () => {
it("returns a list of definitions for actions", async () => { it("returns a list of definitions for actions", async () => {
const res = await request const res = await request
.get(`/api/automations/logic/list`) .get(`/api/automations/logic/list`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -110,7 +69,7 @@ describe("/automations", () => {
it("returns all of the definitions in one", async () => { it("returns all of the definitions in one", async () => {
const res = await request const res = await request
.get(`/api/automations/definitions/list`) .get(`/api/automations/definitions/list`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -121,6 +80,7 @@ describe("/automations", () => {
}) })
describe("create", () => { describe("create", () => {
const autoConfig = basicAutomation()
it("should setup the automation fully", () => { it("should setup the automation fully", () => {
let trigger = TRIGGER_DEFINITIONS["ROW_SAVED"] let trigger = TRIGGER_DEFINITIONS["ROW_SAVED"]
trigger.id = "wadiawdo34" trigger.id = "wadiawdo34"
@ -131,52 +91,51 @@ describe("/automations", () => {
} }
createAction.id = "awde444wk" createAction.id = "awde444wk"
TEST_AUTOMATION.definition.steps.push(createAction) autoConfig.definition.steps.push(createAction)
TEST_AUTOMATION.definition.trigger = trigger autoConfig.definition.trigger = trigger
}) })
it("returns a success message when the automation is successfully created", async () => { it("returns a success message when the automation is successfully created", async () => {
const res = await request const res = await request
.post(`/api/automations`) .post(`/api/automations`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.send(TEST_AUTOMATION) .send(autoConfig)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body.message).toEqual("Automation created successfully") expect(res.body.message).toEqual("Automation created successfully")
expect(res.body.automation.name).toEqual("My Automation") expect(res.body.automation.name).toEqual("My Automation")
expect(res.body.automation._id).not.toEqual(null) expect(res.body.automation._id).not.toEqual(null)
automationId = res.body.automation._id automation = res.body.automation
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "POST", method: "POST",
url: `/api/automations`, url: `/api/automations`,
appId: appId, body: autoConfig
body: TEST_AUTOMATION
}) })
}) })
}) })
describe("trigger", () => { describe("trigger", () => {
it("trigger the automation successfully", async () => { it("trigger the automation successfully", async () => {
let table = await createTable(request, appId) let table = await config.createTable()
TEST_AUTOMATION.definition.trigger.inputs.tableId = table._id automation.definition.trigger.inputs.tableId = table._id
TEST_AUTOMATION.definition.steps[0].inputs.row.tableId = table._id automation.definition.steps[0].inputs.row.tableId = table._id
await createAutomation() automation = await config.createAutomation(automation)
await delay(500) await setup.delay(500)
const res = await triggerWorkflow(automation._id) const res = await triggerWorkflow(automation)
// this looks a bit mad but we don't actually have a way to wait for a response from the automation to // this looks a bit mad but we don't actually have a way to wait for a response from the automation to
// know that it has finished all of its actions - this is currently the best way // know that it has finished all of its actions - this is currently the best way
// also when this runs in CI it is very temper-mental so for now trying to make run stable by repeating until it works // also when this runs in CI it is very temper-mental so for now trying to make run stable by repeating until it works
// TODO: update when workflow logs are a thing // TODO: update when workflow logs are a thing
for (let tries = 0; tries < MAX_RETRIES; tries++) { for (let tries = 0; tries < MAX_RETRIES; tries++) {
expect(res.body.message).toEqual(`Automation ${automation._id} has been triggered.`) expect(res.body.message).toEqual(`Automation ${automation._id} has been triggered.`)
expect(res.body.automation.name).toEqual(TEST_AUTOMATION.name) expect(res.body.automation.name).toEqual(automation.name)
await delay(500) await setup.delay(500)
let elements = await getAllFromTable(request, appId, table._id) let elements = await getAllTableRows(config)
// don't test it unless there are values to test // don't test it unless there are values to test
if (elements.length > 1) { if (elements.length > 1) {
expect(elements.length).toEqual(5) expect(elements.length).toEqual(5)
@ -191,65 +150,63 @@ describe("/automations", () => {
describe("update", () => { describe("update", () => {
it("updates a automations data", async () => { it("updates a automations data", async () => {
await createAutomation() automation = await config.createAutomation(automation)
automation._id = automation.id
automation._rev = automation.rev
automation.name = "Updated Name" automation.name = "Updated Name"
automation.type = "automation" automation.type = "automation"
const res = await request const res = await request
.put(`/api/automations`) .put(`/api/automations`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.send(automation) .send(automation)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body.message).toEqual(`Automation ${AUTOMATION_ID} updated successfully.`) expect(res.body.message).toEqual(`Automation ${automation._id} updated successfully.`)
expect(res.body.automation.name).toEqual("Updated Name") expect(res.body.automation.name).toEqual("Updated Name")
}) })
}) })
describe("fetch", () => { describe("fetch", () => {
it("return all the automations for an instance", async () => { it("return all the automations for an instance", async () => {
await createAutomation() await clearAllAutomations(config)
const autoConfig = basicAutomation()
automation = await config.createAutomation(autoConfig)
const res = await request const res = await request
.get(`/api/automations`) .get(`/api/automations`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body[0]).toEqual(expect.objectContaining(TEST_AUTOMATION)) expect(res.body[0]).toEqual(expect.objectContaining(autoConfig))
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "GET", method: "GET",
url: `/api/automations`, url: `/api/automations`,
appId: appId,
}) })
}) })
}) })
describe("destroy", () => { describe("destroy", () => {
it("deletes a automation by its ID", async () => { it("deletes a automation by its ID", async () => {
await createAutomation() const automation = await config.createAutomation()
const res = await request const res = await request
.delete(`/api/automations/${automation.id}/${automation.rev}`) .delete(`/api/automations/${automation.id}/${automation.rev}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body.id).toEqual(TEST_AUTOMATION._id) expect(res.body.id).toEqual(automation._id)
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await createAutomation() const automation = await config.createAutomation()
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "DELETE", method: "DELETE",
url: `/api/automations/${automation.id}/${automation._rev}`, url: `/api/automations/${automation.id}/${automation._rev}`,
appId: appId,
}) })
}) })
}) })

View File

@ -1,326 +0,0 @@
const CouchDB = require("../../../db")
const supertest = require("supertest")
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
const packageJson = require("../../../../package")
const jwt = require("jsonwebtoken")
const env = require("../../../environment")
const {
BUILTIN_PERMISSION_IDS,
} = require("../../../utilities/security/permissions")
const TEST_CLIENT_ID = "test-client-id"
exports.TEST_CLIENT_ID = TEST_CLIENT_ID
exports.supertest = async () => {
let request
let server
env.PORT = 4002
server = require("../../../app")
request = supertest(server)
return { request, server }
}
exports.defaultHeaders = appId => {
const builderUser = {
userId: "BUILDER",
roleId: BUILTIN_ROLE_IDS.BUILDER,
}
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
const headers = {
Accept: "application/json",
Cookie: [`budibase:builder:local=${builderToken}`],
}
if (appId) {
headers["x-budibase-app-id"] = appId
}
return headers
}
exports.publicHeaders = appId => {
const headers = {
Accept: "application/json",
}
if (appId) {
headers["x-budibase-app-id"] = appId
}
return headers
}
exports.BASE_TABLE = {
name: "TestTable",
type: "table",
key: "name",
schema: {
name: {
type: "string",
constraints: {
type: "string",
},
},
description: {
type: "string",
constraints: {
type: "string",
},
},
},
}
exports.createTable = async (request, appId, table, removeId = true) => {
if (removeId && table != null && table._id) {
delete table._id
}
table = table || exports.BASE_TABLE
const res = await request
.post(`/api/tables`)
.set(exports.defaultHeaders(appId))
.send(table)
return res.body
}
exports.makeBasicRow = tableId => {
return {
name: "Test Contact",
description: "original description",
status: "new",
tableId: tableId,
}
}
exports.createRow = async (request, appId, tableId, row = null) => {
row = row || exports.makeBasicRow(tableId)
const res = await request
.post(`/api/${tableId}/rows`)
.send(row)
.set(exports.defaultHeaders(appId))
.expect("Content-Type", /json/)
.expect(200)
return res.body
}
exports.createRole = async (request, appId) => {
const roleBody = {
name: "NewRole",
inherits: BUILTIN_ROLE_IDS.BASIC,
permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY,
}
const res = await request
.post(`/api/roles`)
.send(roleBody)
.set(exports.defaultHeaders(appId))
.expect("Content-Type", /json/)
.expect(200)
return res.body
}
exports.addPermission = async (
request,
appId,
role,
resource,
level = "read"
) => {
const res = await request
.post(`/api/permission/${role}/${resource}/${level}`)
.set(exports.defaultHeaders(appId))
.expect("Content-Type", /json/)
.expect(200)
return res.body
}
exports.createLinkedTable = async (request, appId) => {
// get the ID to link to
const table = await exports.createTable(request, appId)
table.primaryDisplay = "name"
table.schema.link = {
type: "link",
fieldName: "link",
tableId: table._id,
}
return exports.createTable(request, appId, table, false)
}
exports.createAttachmentTable = async (request, appId) => {
const table = await exports.createTable(request, appId)
table.schema.attachment = {
type: "attachment",
}
return exports.createTable(request, appId, table, false)
}
exports.getAllFromTable = async (request, appId, tableId) => {
const res = await request
.get(`/api/${tableId}/rows`)
.set(exports.defaultHeaders(appId))
return res.body
}
exports.createView = async (request, appId, tableId, view) => {
view = view || {
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
tableId: tableId,
}
const res = await request
.post(`/api/views`)
.set(exports.defaultHeaders(appId))
.send(view)
return res.body
}
exports.createApplication = async (request, name = "test_application") => {
const res = await request
.post("/api/applications")
.send({
name,
})
.set(exports.defaultHeaders())
return res.body
}
exports.clearApplications = async request => {
const res = await request
.get("/api/applications")
.set(exports.defaultHeaders())
for (let app of res.body) {
const appId = app._id
await request
.delete(`/api/applications/${appId}`)
.set(exports.defaultHeaders(appId))
}
}
exports.createUser = async (
request,
appId,
email = "babs@babs.com",
password = "babs_password"
) => {
const res = await request
.post(`/api/users`)
.set(exports.defaultHeaders(appId))
.send({
name: "Bill",
email,
password,
roleId: BUILTIN_ROLE_IDS.POWER,
})
return res.body
}
const createUserWithRole = async (request, appId, roleId, email) => {
const password = `password_${email}`
await request
.post(`/api/users`)
.set(exports.defaultHeaders(appId))
.send({
email,
password,
roleId,
})
const anonUser = {
userId: "ANON",
roleId: BUILTIN_ROLE_IDS.PUBLIC,
appId: appId,
version: packageJson.version,
}
const anonToken = jwt.sign(anonUser, env.JWT_SECRET)
const loginResult = await request
.post(`/api/authenticate`)
.set({
Cookie: `budibase:${appId}:local=${anonToken}`,
"x-budibase-app-id": appId,
})
.send({ email, password })
// returning necessary request headers
return {
Accept: "application/json",
Cookie: loginResult.headers["set-cookie"],
}
}
exports.testPermissionsForEndpoint = async ({
request,
method,
url,
body,
appId,
passRole,
failRole,
}) => {
const passHeader = await createUserWithRole(
request,
appId,
passRole,
"passUser@budibase.com"
)
await createRequest(request, method, url, body)
.set(passHeader)
.expect(200)
const failHeader = await createUserWithRole(
request,
appId,
failRole,
"failUser@budibase.com"
)
await createRequest(request, method, url, body)
.set(failHeader)
.expect(403)
}
exports.builderEndpointShouldBlockNormalUsers = async ({
request,
method,
url,
body,
appId,
}) => {
const headers = await createUserWithRole(
request,
appId,
BUILTIN_ROLE_IDS.BASIC,
"basicUser@budibase.com"
)
await createRequest(request, method, url, body)
.set(headers)
.expect(403)
}
const createRequest = (request, method, url, body) => {
let req
if (method === "POST") req = request.post(url).send(body)
else if (method === "GET") req = request.get(url)
else if (method === "DELETE") req = request.delete(url)
else if (method === "PATCH") req = request.patch(url).send(body)
else if (method === "PUT") req = request.put(url).send(body)
return req
}
exports.insertDocument = async (databaseId, document) => {
const { id, ...documentFields } = document
return await new CouchDB(databaseId).put({ _id: id, ...documentFields })
}
exports.destroyDocument = async (databaseId, documentId) => {
return await new CouchDB(databaseId).destroy(documentId)
}
exports.getDocument = async (databaseId, documentId) => {
return await new CouchDB(databaseId).get(documentId)
}

View File

@ -1,67 +1,23 @@
const { let { basicDatasource } = require("./utilities/structures")
supertest, let { checkBuilderEndpoint } = require("./utilities/TestFunctions")
createApplication, let setup = require("./utilities")
defaultHeaders,
builderEndpointShouldBlockNormalUsers,
getDocument,
insertDocument
} = require("./couchTestUtils")
let { generateDatasourceID, generateQueryID } = require("../../../db/utils")
const DATASOURCE_ID = generateDatasourceID()
const TEST_DATASOURCE = {
_id: DATASOURCE_ID,
type: "datasource",
name: "Test",
source: "POSTGRES",
config: {},
type: "datasource",
}
const TEST_QUERY = {
_id: generateQueryID(DATASOURCE_ID),
datasourceId: DATASOURCE_ID,
name:"New Query",
parameters:[],
fields:{},
schema:{},
queryVerb:"read",
}
describe("/datasources", () => { describe("/datasources", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let app
let appId
let datasource
beforeAll(async () => { afterAll(setup.afterAll)
({ request, server } = await supertest())
});
afterAll(() => {
server.close()
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id })
});
async function createDatasource() {
return await insertDocument(appId, TEST_DATASOURCE)
}
async function createQuery() {
return await insertDocument(appId, TEST_QUERY)
}
describe("create", () => { describe("create", () => {
it("should create a new datasource", async () => { it("should create a new datasource", async () => {
const res = await request const res = await request
.post(`/api/datasources`) .post(`/api/datasources`)
.send(TEST_DATASOURCE) .send(basicDatasource())
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -74,7 +30,7 @@ describe("/datasources", () => {
let datasource let datasource
beforeEach(async () => { beforeEach(async () => {
datasource = await createDatasource() datasource = await config.createDatasource()
}); });
afterEach(() => { afterEach(() => {
@ -84,34 +40,34 @@ describe("/datasources", () => {
it("returns all the datasources from the server", async () => { it("returns all the datasources from the server", async () => {
const res = await request const res = await request
.get(`/api/datasources`) .get(`/api/datasources`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
const datasources = res.body; const datasources = res.body
expect(datasources).toEqual([ expect(datasources).toEqual([
{ {
"_id": datasources[0]._id,
"_rev": datasources[0]._rev, "_rev": datasources[0]._rev,
...TEST_DATASOURCE ...basicDatasource()
} }
]); ]);
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "GET", method: "GET",
url: `/api/datasources`, url: `/api/datasources`,
appId: appId,
}) })
}) })
}); });
describe("destroy", () => { describe("destroy", () => {
let datasource; let datasource
beforeEach(async () => { beforeEach(async () => {
datasource = await createDatasource() datasource = await config.createDatasource()
}); });
afterEach(() => { afterEach(() => {
@ -119,16 +75,16 @@ describe("/datasources", () => {
}); });
it("deletes queries for the datasource after deletion and returns a success message", async () => { it("deletes queries for the datasource after deletion and returns a success message", async () => {
await createQuery(datasource.id) await config.createQuery()
await request await request
.delete(`/api/datasources/${datasource.id}/${datasource.rev}`) .delete(`/api/datasources/${datasource._id}/${datasource._rev}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect(200) .expect(200)
const res = await request const res = await request
.get(`/api/datasources`) .get(`/api/datasources`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -136,11 +92,10 @@ describe("/datasources", () => {
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "DELETE", method: "DELETE",
url: `/api/datasources/${datasource._id}/${datasource._rev}`, url: `/api/datasources/${datasource._id}/${datasource._rev}`,
appId: appId,
}) })
}) })

View File

@ -1,46 +1,30 @@
const {
createApplication,
createTable,
createRow,
supertest,
defaultHeaders,
addPermission,
publicHeaders,
makeBasicRow,
} = require("./couchTestUtils")
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
const setup = require("./utilities")
const { basicRow } = require("./utilities/structures")
const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC
const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC
describe("/permission", () => { describe("/permission", () => {
let server let request = setup.getRequest()
let request let config = setup.getConfig()
let appId
let table let table
let perms let perms
let row let row
beforeAll(async () => { afterAll(setup.afterAll)
;({ request, server } = await supertest())
})
afterAll(() => {
server.close()
})
beforeEach(async () => { beforeEach(async () => {
let app = await createApplication(request) await config.init()
appId = app.instance._id table = await config.createTable()
table = await createTable(request, appId) row = await config.createRow()
perms = await addPermission(request, appId, STD_ROLE_ID, table._id) perms = await config.addPermission(STD_ROLE_ID, table._id)
row = await createRow(request, appId, table._id)
}) })
async function getTablePermissions() { async function getTablePermissions() {
return request return request
.get(`/api/permission/${table._id}`) .get(`/api/permission/${table._id}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
} }
@ -49,7 +33,7 @@ describe("/permission", () => {
it("should be able to get levels", async () => { it("should be able to get levels", async () => {
const res = await request const res = await request
.get(`/api/permission/levels`) .get(`/api/permission/levels`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body).toBeDefined() expect(res.body).toBeDefined()
@ -68,7 +52,7 @@ describe("/permission", () => {
it("should get the resource permissions", async () => { it("should get the resource permissions", async () => {
const res = await request const res = await request
.get(`/api/permission/${table._id}`) .get(`/api/permission/${table._id}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body["read"]).toEqual(STD_ROLE_ID) expect(res.body["read"]).toEqual(STD_ROLE_ID)
@ -76,13 +60,13 @@ describe("/permission", () => {
}) })
it("should get resource permissions with multiple roles", async () => { it("should get resource permissions with multiple roles", async () => {
perms = await addPermission(request, appId, HIGHER_ROLE_ID, table._id, "write") perms = await config.addPermission(HIGHER_ROLE_ID, table._id, "write")
const res = await getTablePermissions() const res = await getTablePermissions()
expect(res.body["read"]).toEqual(STD_ROLE_ID) expect(res.body["read"]).toEqual(STD_ROLE_ID)
expect(res.body["write"]).toEqual(HIGHER_ROLE_ID) expect(res.body["write"]).toEqual(HIGHER_ROLE_ID)
const allRes = await request const allRes = await request
.get(`/api/permission`) .get(`/api/permission`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID) expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID)
@ -94,7 +78,7 @@ describe("/permission", () => {
it("should be able to remove the permission", async () => { it("should be able to remove the permission", async () => {
const res = await request const res = await request
.delete(`/api/permission/${STD_ROLE_ID}/${table._id}/read`) .delete(`/api/permission/${STD_ROLE_ID}/${table._id}/read`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body[0]._id).toEqual(STD_ROLE_ID) expect(res.body[0]._id).toEqual(STD_ROLE_ID)
@ -107,7 +91,7 @@ describe("/permission", () => {
it("should be able to read the row", async () => { it("should be able to read the row", async () => {
const res = await request const res = await request
.get(`/api/${table._id}/rows`) .get(`/api/${table._id}/rows`)
.set(publicHeaders(appId)) .set(config.publicHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body[0]._id).toEqual(row._id) expect(res.body[0]._id).toEqual(row._id)
@ -116,8 +100,8 @@ describe("/permission", () => {
it("shouldn't allow writing from a public user", async () => { it("shouldn't allow writing from a public user", async () => {
const res = await request const res = await request
.post(`/api/${table._id}/rows`) .post(`/api/${table._id}/rows`)
.send(makeBasicRow(table._id)) .send(basicRow(table._id))
.set(publicHeaders(appId)) .set(config.publicHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(403) .expect(403)
expect(res.status).toEqual(403) expect(res.status).toEqual(403)

View File

@ -1,77 +1,35 @@
const { const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
supertest, const { basicQuery } = require("./utilities/structures")
createApplication, const setup = require("./utilities")
defaultHeaders,
builderEndpointShouldBlockNormalUsers,
getDocument,
insertDocument,
} = require("./couchTestUtils")
let { generateDatasourceID, generateQueryID } = require("../../../db/utils")
const DATASOURCE_ID = generateDatasourceID()
const TEST_DATASOURCE = {
_id: DATASOURCE_ID,
type: "datasource",
name: "Test",
source: "POSTGRES",
config: {},
type: "datasource",
}
const TEST_QUERY = {
_id: generateQueryID(DATASOURCE_ID),
datasourceId: DATASOURCE_ID,
name: "New Query",
parameters: [],
fields: {},
schema: {},
queryVerb: "read",
}
describe("/queries", () => { describe("/queries", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let app
let appId
let datasource
let query
beforeAll(async () => { afterAll(setup.afterAll)
;({ request, server } = await supertest())
})
afterAll(() => {
server.close()
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id
}) })
async function createDatasource() {
return await insertDocument(appId, TEST_DATASOURCE)
}
async function createQuery() {
return await insertDocument(appId, TEST_QUERY)
}
describe("create", () => { describe("create", () => {
it("should create a new query", async () => { it("should create a new query", async () => {
const { _id } = await config.createDatasource()
const query = basicQuery(_id)
const res = await request const res = await request
.post(`/api/queries`) .post(`/api/queries`)
.send(TEST_QUERY) .send(query)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual( expect(res.res.statusMessage).toEqual(
`Query ${TEST_QUERY.name} saved successfully.` `Query ${query.name} saved successfully.`
) )
expect(res.body).toEqual({ expect(res.body).toEqual({
_rev: res.body._rev, _rev: res.body._rev,
...TEST_QUERY, _id: res.body._id,
...query,
}) })
}) })
}) })
@ -80,7 +38,7 @@ describe("/queries", () => {
let datasource let datasource
beforeEach(async () => { beforeEach(async () => {
datasource = await createDatasource() datasource = await config.createDatasource()
}) })
afterEach(() => { afterEach(() => {
@ -88,29 +46,29 @@ describe("/queries", () => {
}) })
it("returns all the queries from the server", async () => { it("returns all the queries from the server", async () => {
const query = await createQuery() const query = await config.createQuery()
const res = await request const res = await request
.get(`/api/queries`) .get(`/api/queries`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
const queries = res.body const queries = res.body
expect(queries).toEqual([ expect(queries).toEqual([
{ {
_rev: query.rev, _rev: query._rev,
...TEST_QUERY, _id: query._id,
...basicQuery(datasource._id),
readable: true, readable: true,
}, },
]) ])
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "GET", method: "GET",
url: `/api/datasources`, url: `/api/datasources`,
appId: appId,
}) })
}) })
}) })
@ -119,7 +77,7 @@ describe("/queries", () => {
let datasource let datasource
beforeEach(async () => { beforeEach(async () => {
datasource = await createDatasource() datasource = await config.createDatasource()
}) })
afterEach(() => { afterEach(() => {
@ -127,16 +85,16 @@ describe("/queries", () => {
}) })
it("deletes a query and returns a success message", async () => { it("deletes a query and returns a success message", async () => {
const query = await createQuery() const query = await config.createQuery()
await request await request
.delete(`/api/queries/${query.id}/${query.rev}`) .delete(`/api/queries/${query._id}/${query._rev}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect(200) .expect(200)
const res = await request const res = await request
.get(`/api/queries`) .get(`/api/queries`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -144,11 +102,10 @@ describe("/queries", () => {
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "DELETE", method: "DELETE",
url: `/api/datasources/${datasource._id}/${datasource._rev}`, url: `/api/datasources/${datasource._id}/${datasource._rev}`,
appId: appId,
}) })
}) })
}) })

View File

@ -1,43 +1,26 @@
const {
createApplication,
supertest,
defaultHeaders,
} = require("./couchTestUtils")
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
const { const {
BUILTIN_PERMISSION_IDS, BUILTIN_PERMISSION_IDS,
} = require("../../../utilities/security/permissions") } = require("../../../utilities/security/permissions")
const { basicRole } = require("./utilities/structures")
const roleBody = { const setup = require("./utilities")
name: "NewRole",
inherits: BUILTIN_ROLE_IDS.BASIC,
permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY,
}
describe("/roles", () => { describe("/roles", () => {
let server let request = setup.getRequest()
let request let config = setup.getConfig()
let appId
beforeAll(async () => { afterAll(setup.afterAll)
;({ request, server } = await supertest())
})
afterAll(() => {
server.close()
})
beforeEach(async () => { beforeEach(async () => {
let app = await createApplication(request) await config.init()
appId = app.instance._id
}) })
describe("create", () => { describe("create", () => {
it("returns a success message when role is successfully created", async () => { it("returns a success message when role is successfully created", async () => {
const res = await request const res = await request
.post(`/api/roles`) .post(`/api/roles`)
.send(roleBody) .send(basicRole())
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -53,8 +36,8 @@ describe("/roles", () => {
it("should list custom roles, plus 2 default roles", async () => { it("should list custom roles, plus 2 default roles", async () => {
const createRes = await request const createRes = await request
.post(`/api/roles`) .post(`/api/roles`)
.send(roleBody) .send(basicRole())
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -62,7 +45,7 @@ describe("/roles", () => {
const res = await request const res = await request
.get(`/api/roles`) .get(`/api/roles`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -92,7 +75,7 @@ describe("/roles", () => {
const createRes = await request const createRes = await request
.post(`/api/roles`) .post(`/api/roles`)
.send({ name: "user", permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY }) .send({ name: "user", permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -100,12 +83,12 @@ describe("/roles", () => {
await request await request
.delete(`/api/roles/${customRole._id}/${customRole._rev}`) .delete(`/api/roles/${customRole._id}/${customRole._rev}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect(200) .expect(200)
await request await request
.get(`/api/roles/${customRole._id}`) .get(`/api/roles/${customRole._id}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect(404) .expect(404)
}) })
}) })

View File

@ -1,66 +1,45 @@
const {
createApplication,
createTable,
supertest,
defaultHeaders,
createLinkedTable,
createAttachmentTable,
makeBasicRow,
} = require("./couchTestUtils");
const { outputProcessing } = require("../../../utilities/rowProcessor") const { outputProcessing } = require("../../../utilities/rowProcessor")
const env = require("../../../environment") const env = require("../../../environment")
const { basicRow } = require("./utilities/structures")
const setup = require("./utilities")
describe("/rows", () => { describe("/rows", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let appId
let table let table
let row let row
let app
beforeAll(async () => { afterAll(setup.afterAll)
({ request, server } = await supertest())
});
afterAll(() => {
server.close()
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id table = await config.createTable()
table = await createTable(request, appId) row = basicRow(table._id)
row = makeBasicRow(table._id)
}) })
const createRow = async r =>
await request
.post(`/api/${r ? r.tableId : row.tableId}/rows`)
.send(r || row)
.set(defaultHeaders(appId))
.expect('Content-Type', /json/)
.expect(200)
const loadRow = async id => const loadRow = async id =>
await request await request
.get(`/api/${table._id}/rows/${id}`) .get(`/api/${table._id}/rows/${id}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
describe("save, load, update, delete", () => { describe("save, load, update, delete", () => {
it("returns a success message when the row is created", async () => { it("returns a success message when the row is created", async () => {
const res = await createRow() const res = await request
.post(`/api/${row.tableId}/rows`)
.send(row)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`) expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`)
expect(res.body.name).toEqual("Test Contact") expect(res.body.name).toEqual("Test Contact")
expect(res.body._rev).toBeDefined() expect(res.body._rev).toBeDefined()
}) })
it("updates a row successfully", async () => { it("updates a row successfully", async () => {
const rec = await createRow() const existing = await config.createRow()
const existing = rec.body
const res = await request const res = await request
.post(`/api/${table._id}/rows`) .post(`/api/${table._id}/rows`)
@ -70,7 +49,7 @@ describe("/rows", () => {
tableId: table._id, tableId: table._id,
name: "Updated Name", name: "Updated Name",
}) })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -79,12 +58,11 @@ describe("/rows", () => {
}) })
it("should load a row", async () => { it("should load a row", async () => {
const rec = await createRow() const existing = await config.createRow()
const existing = rec.body
const res = await request const res = await request
.get(`/api/${table._id}/rows/${existing._id}`) .get(`/api/${table._id}/rows/${existing._id}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -102,12 +80,12 @@ describe("/rows", () => {
name: "Second Contact", name: "Second Contact",
status: "new" status: "new"
} }
await createRow() await config.createRow()
await createRow(newRow) await config.createRow(newRow)
const res = await request const res = await request
.get(`/api/${table._id}/rows`) .get(`/api/${table._id}/rows`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -117,10 +95,10 @@ describe("/rows", () => {
}) })
it("load should return 404 when row does not exist", async () => { it("load should return 404 when row does not exist", async () => {
await createRow() await config.createRow()
await request await request
.get(`/api/${table._id}/rows/not-a-valid-id`) .get(`/api/${table._id}/rows/not-a-valid-id`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(404) .expect(404)
}) })
@ -132,7 +110,7 @@ describe("/rows", () => {
const number = {type:"number", constraints: { type: "number", presence: false }} const number = {type:"number", constraints: { type: "number", presence: false }}
const datetime = {type:"datetime", constraints: { type: "string", presence: false, datetime: {earliest:"", latest: ""} }} const datetime = {type:"datetime", constraints: { type: "string", presence: false, datetime: {earliest:"", latest: ""} }}
table = await createTable(request, appId, { table = await config.createTable({
name: "TestTable2", name: "TestTable2",
type: "table", type: "table",
key: "name", key: "name",
@ -187,7 +165,7 @@ describe("/rows", () => {
attachmentEmpty : "", attachmentEmpty : "",
} }
const id = (await createRow(row)).body._id const id = (await config.createRow(row))._id
const saved = (await loadRow(id)).body const saved = (await loadRow(id)).body
@ -217,8 +195,7 @@ describe("/rows", () => {
describe("patch", () => { describe("patch", () => {
it("should update only the fields that are supplied", async () => { it("should update only the fields that are supplied", async () => {
const rec = await createRow() const existing = await config.createRow()
const existing = rec.body
const res = await request const res = await request
.patch(`/api/${table._id}/rows/${existing._id}`) .patch(`/api/${table._id}/rows/${existing._id}`)
@ -228,7 +205,7 @@ describe("/rows", () => {
tableId: table._id, tableId: table._id,
name: "Updated Name", name: "Updated Name",
}) })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -249,7 +226,7 @@ describe("/rows", () => {
const result = await request const result = await request
.post(`/api/${table._id}/rows/validate`) .post(`/api/${table._id}/rows/validate`)
.send({ name: "ivan" }) .send({ name: "ivan" })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -261,7 +238,7 @@ describe("/rows", () => {
const result = await request const result = await request
.post(`/api/${table._id}/rows/validate`) .post(`/api/${table._id}/rows/validate`)
.send({ name: 1 }) .send({ name: 1 })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -273,19 +250,19 @@ describe("/rows", () => {
describe("enrich row unit test", () => { describe("enrich row unit test", () => {
it("should allow enriching some linked rows", async () => { it("should allow enriching some linked rows", async () => {
const table = await createLinkedTable(request, appId) const table = await config.createLinkedTable()
const firstRow = (await createRow({ const firstRow = await config.createRow({
name: "Test Contact", name: "Test Contact",
description: "original description", description: "original description",
tableId: table._id tableId: table._id
})).body })
const secondRow = (await createRow({ const secondRow = await config.createRow({
name: "Test 2", name: "Test 2",
description: "og desc", description: "og desc",
link: [{_id: firstRow._id}], link: [{_id: firstRow._id}],
tableId: table._id, tableId: table._id,
})).body })
const enriched = await outputProcessing(appId, table, [secondRow]) const enriched = await outputProcessing(config.getAppId(), table, [secondRow])
expect(enriched[0].link.length).toBe(1) expect(enriched[0].link.length).toBe(1)
expect(enriched[0].link[0]._id).toBe(firstRow._id) expect(enriched[0].link[0]._id).toBe(firstRow._id)
expect(enriched[0].link[0].primaryDisplay).toBe("Test Contact") expect(enriched[0].link[0].primaryDisplay).toBe("Test Contact")
@ -293,20 +270,20 @@ describe("/rows", () => {
}) })
it("should allow enriching attachment rows", async () => { it("should allow enriching attachment rows", async () => {
const table = await createAttachmentTable(request, appId) const table = await config.createAttachmentTable()
const row = (await createRow({ const row = await config.createRow({
name: "test", name: "test",
description: "test", description: "test",
attachment: [{ attachment: [{
url: "/test/thing", url: "/test/thing",
}], }],
tableId: table._id, tableId: table._id,
})).body })
// the environment needs configured for this // the environment needs configured for this
env.CLOUD = 1 env.CLOUD = 1
env.SELF_HOSTED = 1 env.SELF_HOSTED = 1
const enriched = await outputProcessing(appId, table, [row]) const enriched = await outputProcessing(config.getAppId(), table, [row])
expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${appId}/test/thing`) expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${config.getAppId()}/test/thing`)
// remove env config // remove env config
env.CLOUD = undefined env.CLOUD = undefined
env.SELF_HOSTED = undefined env.SELF_HOSTED = undefined

View File

@ -1,30 +1,15 @@
const { const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
createTable, const setup = require("./utilities")
supertest,
createApplication,
defaultHeaders,
builderEndpointShouldBlockNormalUsers,
getDocument
} = require("./couchTestUtils")
describe("/tables", () => { describe("/tables", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let app
let appId
beforeAll(async () => { afterAll(setup.afterAll)
({ request, server } = await supertest())
});
afterAll(() => {
server.close()
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id })
});
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", done => {
@ -37,25 +22,25 @@ describe("/tables", () => {
name: { type: "string" } name: { type: "string" }
} }
}) })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (err, res) => { .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(); 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 createTable(request, appId); const testTable = await config.createTable()
const testRow = await request const testRow = await request
.post(`/api/${testTable._id}/rows`) .post(`/api/${testTable._id}/rows`)
.send({ .send({
name: "test" name: "test"
}) })
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -74,29 +59,28 @@ describe("/tables", () => {
updatedName: { type: "string" } updatedName: { type: "string" }
} }
}) })
.set(defaultHeaders(appId)) .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.res.statusMessage).toEqual("Table TestTable saved successfully.")
expect(updatedTable.body.name).toEqual("TestTable"); expect(updatedTable.body.name).toEqual("TestTable")
const res = await request const res = await request
.get(`/api/${testTable._id}/rows/${testRow.body._id}`) .get(`/api/${testTable._id}/rows/${testRow.body._id}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body.updatedName).toEqual("test"); expect(res.body.updatedName).toEqual("test")
expect(res.body.name).toBeUndefined(); expect(res.body.name).toBeUndefined()
}); })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "POST", method: "POST",
url: `/api/tables`, url: `/api/tables`,
appId: appId,
body: { body: {
name: "TestTable", name: "TestTable",
key: "name", key: "name",
@ -106,68 +90,67 @@ describe("/tables", () => {
} }
}) })
}) })
}); })
describe("fetch", () => { describe("fetch", () => {
let testTable let testTable
beforeEach(async () => { beforeEach(async () => {
testTable = await createTable(request, appId, testTable) testTable = await config.createTable(testTable)
}); })
afterEach(() => { afterEach(() => {
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", done => {
request request
.get(`/api/tables`) .get(`/api/tables`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { .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(); done()
}); })
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "GET", method: "GET",
url: `/api/tables`, url: `/api/tables`,
appId: appId,
}) })
}) })
}); })
describe("destroy", () => { describe("destroy", () => {
let testTable; let testTable
beforeEach(async () => { beforeEach(async () => {
testTable = await createTable(request, appId, testTable) testTable = await config.createTable(testTable)
}); })
afterEach(() => { afterEach(() => {
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 done => {
request request
.delete(`/api/tables/${testTable._id}/${testTable._rev}`) .delete(`/api/tables/${testTable._id}/${testTable._rev}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { .end(async (_, res) => {
expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`); expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`)
done(); done()
}); })
}) })
it("deletes linked references to the table after deletion", async done => { it("deletes linked references to the table after deletion", async done => {
const linkedTable = await createTable(request, appId, { const linkedTable = await config.createTable({
name: "LinkedTable", name: "LinkedTable",
type: "table", type: "table",
key: "name", key: "name",
@ -190,25 +173,24 @@ describe("/tables", () => {
request request
.delete(`/api/tables/${testTable._id}/${testTable._rev}`) .delete(`/api/tables/${testTable._id}/${testTable._rev}`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { .end(async (_, res) => {
expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`); expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`)
const dependentTable = await getDocument(appId, linkedTable._id) const dependentTable = await config.getTable(linkedTable._id)
expect(dependentTable.schema.TestTable).not.toBeDefined(); expect(dependentTable.schema.TestTable).not.toBeDefined()
done(); done()
}); })
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await builderEndpointShouldBlockNormalUsers({ await checkBuilderEndpoint({
request, config,
method: "DELETE", method: "DELETE",
url: `/api/tables/${testTable._id}/${testTable._rev}`, url: `/api/tables/${testTable._id}/${testTable._rev}`,
appId: appId,
}) })
}) })
}); })
}); })

View File

@ -1 +0,0 @@
module.exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))

View File

@ -1,46 +1,25 @@
const {
createApplication,
supertest,
defaultHeaders,
createUser,
testPermissionsForEndpoint,
} = require("./couchTestUtils")
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
const { cloneDeep } = require("lodash/fp") const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
const { basicUser } = require("./utilities/structures")
const baseBody = { const setup = require("./utilities")
email: "bill@bill.com",
password: "yeeooo",
roleId: BUILTIN_ROLE_IDS.POWER,
}
describe("/users", () => { describe("/users", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let app
let appId
beforeAll(async () => { afterAll(setup.afterAll)
;({ request, server } = await supertest(server))
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id
})
afterAll(() => {
server.close()
server.destroy()
}) })
describe("fetch", () => { describe("fetch", () => {
it("returns a list of users from an instance db", async () => { it("returns a list of users from an instance db", async () => {
await createUser(request, appId, "brenda@brenda.com", "brendas_password") await config.createUser("brenda@brenda.com", "brendas_password")
await createUser(request, appId, "pam@pam.com", "pam_password") await config.createUser("pam@pam.com", "pam_password")
const res = await request const res = await request
.get(`/api/users`) .get(`/api/users`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -50,12 +29,12 @@ describe("/users", () => {
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await createUser(request, appId, "brenda@brenda.com", "brendas_password") await config.createUser("brenda@brenda.com", "brendas_password")
await testPermissionsForEndpoint({ await checkPermissionsEndpoint({
config,
request, request,
method: "GET", method: "GET",
url: `/api/users`, url: `/api/users`,
appId: appId,
passRole: BUILTIN_ROLE_IDS.ADMIN, passRole: BUILTIN_ROLE_IDS.ADMIN,
failRole: BUILTIN_ROLE_IDS.PUBLIC, failRole: BUILTIN_ROLE_IDS.PUBLIC,
}) })
@ -64,11 +43,11 @@ describe("/users", () => {
describe("create", () => { describe("create", () => {
it("returns a success message when a user is successfully created", async () => { it("returns a success message when a user is successfully created", async () => {
const body = cloneDeep(baseBody) const body = basicUser(BUILTIN_ROLE_IDS.POWER)
body.email = "bill@budibase.com" body.email = "bill@budibase.com"
const res = await request const res = await request
.post(`/api/users`) .post(`/api/users`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.send(body) .send(body)
.expect(200) .expect(200)
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
@ -78,14 +57,13 @@ describe("/users", () => {
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
const body = cloneDeep(baseBody) const body = basicUser(BUILTIN_ROLE_IDS.POWER)
body.email = "brandNewUser@user.com" body.email = "brandNewUser@user.com"
await testPermissionsForEndpoint({ await checkPermissionsEndpoint({
request, config,
method: "POST", method: "POST",
body, body,
url: `/api/users`, url: `/api/users`,
appId: appId,
passRole: BUILTIN_ROLE_IDS.ADMIN, passRole: BUILTIN_ROLE_IDS.ADMIN,
failRole: BUILTIN_ROLE_IDS.PUBLIC, failRole: BUILTIN_ROLE_IDS.PUBLIC,
}) })

View File

@ -0,0 +1,248 @@
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles")
const jwt = require("jsonwebtoken")
const env = require("../../../../environment")
const {
basicTable,
basicRow,
basicRole,
basicAutomation,
basicDatasource,
basicQuery,
} = require("./structures")
const controllers = require("./controllers")
const supertest = require("supertest")
const EMAIL = "babs@babs.com"
const PASSWORD = "babs_password"
class TestConfiguration {
constructor() {
env.PORT = 4002
this.server = require("../../../../app")
// we need the request for logging in, involves cookies, hard to fake
this.request = supertest(this.server)
this.appId = null
}
getRequest() {
return this.request
}
getAppId() {
return this.appId
}
async _req(config, params, controlFunc) {
const request = {}
// fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.appId = this.appId
request.user = { appId: this.appId }
request.request = {
body: config,
}
if (params) {
request.params = params
}
await controlFunc(request)
return request.body
}
async init(appName = "test_application") {
return this.createApp(appName)
}
end() {
this.server.close()
}
defaultHeaders() {
const builderUser = {
userId: "BUILDER",
roleId: BUILTIN_ROLE_IDS.BUILDER,
}
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
const headers = {
Accept: "application/json",
Cookie: [`budibase:builder:local=${builderToken}`],
}
if (this.appId) {
headers["x-budibase-app-id"] = this.appId
}
return headers
}
publicHeaders() {
const headers = {
Accept: "application/json",
}
if (this.appId) {
headers["x-budibase-app-id"] = this.appId
}
return headers
}
async createApp(appName) {
this.app = await this._req({ name: appName }, null, controllers.app.create)
this.appId = this.app._id
return this.app
}
async updateTable(config = null) {
config = config || basicTable()
this.table = await this._req(config, null, controllers.table.save)
return this.table
}
async createTable(config = null) {
if (config != null && config._id) {
delete config._id
}
return this.updateTable(config)
}
async getTable(tableId = null) {
tableId = tableId || this.table._id
return this._req(null, { id: tableId }, controllers.table.find)
}
async createLinkedTable() {
if (!this.table) {
throw "Must have created a table first."
}
const tableConfig = basicTable()
tableConfig.primaryDisplay = "name"
tableConfig.schema.link = {
type: "link",
fieldName: "link",
tableId: this.table._id,
}
const linkedTable = await this.createTable(tableConfig)
this.linkedTable = linkedTable
return linkedTable
}
async createAttachmentTable() {
const table = basicTable()
table.schema.attachment = {
type: "attachment",
}
return this.createTable(table)
}
async createRow(config = null) {
if (!this.table) {
throw "Test requires table to be configured."
}
config = config || basicRow(this.table._id)
return this._req(config, { tableId: this.table._id }, controllers.row.save)
}
async createRole(config = null) {
config = config || basicRole()
return this._req(config, null, controllers.role.save)
}
async addPermission(roleId, resourceId, level = "read") {
return this._req(
null,
{
roleId,
resourceId,
level,
},
controllers.perms.addPermission
)
}
async createView(config) {
if (!this.table) {
throw "Test requires table to be configured."
}
const view = config || {
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
tableId: this.table._id,
}
return this._req(view, null, controllers.view.save)
}
async createAutomation(config) {
config = config || basicAutomation()
if (config._rev) {
delete config._rev
}
this.automation = (
await this._req(config, null, controllers.automation.create)
).automation
return this.automation
}
async getAllAutomations() {
return this._req(null, null, controllers.automation.fetch)
}
async deleteAutomation(automation = null) {
automation = automation || this.automation
if (!automation) {
return
}
return this._req(
null,
{ id: automation._id, rev: automation._rev },
controllers.automation.destroy
)
}
async createDatasource(config = null) {
config = config || basicDatasource()
this.datasource = await this._req(config, null, controllers.datasource.save)
return this.datasource
}
async createQuery(config = null) {
if (!this.datasource && !config) {
throw "No data source created for query."
}
config = config || basicQuery(this.datasource._id)
return this._req(config, null, controllers.query.save)
}
async createUser(
email = EMAIL,
password = PASSWORD,
roleId = BUILTIN_ROLE_IDS.POWER
) {
return this._req(
{
email,
password,
roleId,
},
null,
controllers.user.create
)
}
async login(email, password) {
if (!email || !password) {
await this.createUser()
email = EMAIL
password = PASSWORD
}
const result = await this.request
.post(`/api/authenticate`)
.set({
"x-budibase-app-id": this.appId,
})
.send({ email, password })
// returning necessary request headers
return {
Accept: "application/json",
Cookie: result.headers["set-cookie"],
}
}
}
module.exports = TestConfiguration

View File

@ -0,0 +1,79 @@
const rowController = require("../../../controllers/row")
const appController = require("../../../controllers/application")
function Request(appId, params) {
this.user = { appId }
this.params = params
}
exports.getAllTableRows = async config => {
const req = new Request(config.appId, { tableId: config.table._id })
await rowController.fetchTableRows(req)
return req.body
}
exports.clearAllApps = async () => {
const req = {}
await appController.fetch(req)
const apps = req.body
if (!apps || apps.length <= 0) {
return
}
for (let app of apps) {
const appId = app._id
await appController.delete(new Request(null, { appId }))
}
}
exports.clearAllAutomations = async config => {
const automations = await config.getAllAutomations()
for (let auto of automations) {
await config.deleteAutomation(auto)
}
}
exports.createRequest = (request, method, url, body) => {
let req
if (method === "POST") req = request.post(url).send(body)
else if (method === "GET") req = request.get(url)
else if (method === "DELETE") req = request.delete(url)
else if (method === "PATCH") req = request.patch(url).send(body)
else if (method === "PUT") req = request.put(url).send(body)
return req
}
exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
const headers = await config.login()
await exports
.createRequest(config.request, method, url, body)
.set(headers)
.expect(403)
}
exports.checkPermissionsEndpoint = async ({
config,
method,
url,
body,
passRole,
failRole,
}) => {
const password = "PASSWORD"
await config.createUser("passUser@budibase.com", password, passRole)
const passHeader = await config.login("passUser@budibase.com", password)
await exports
.createRequest(config.request, method, url, body)
.set(passHeader)
.expect(200)
await config.createUser("failUser@budibase.com", password, failRole)
const failHeader = await config.login("failUser@budibase.com", password)
await exports
.createRequest(config.request, method, url, body)
.set(failHeader)
.expect(403)
}

View File

@ -0,0 +1,12 @@
module.exports = {
table: require("../../../controllers/table"),
row: require("../../../controllers/row"),
role: require("../../../controllers/role"),
perms: require("../../../controllers/permission"),
view: require("../../../controllers/view"),
app: require("../../../controllers/application"),
user: require("../../../controllers/user"),
automation: require("../../../controllers/automation"),
datasource: require("../../../controllers/datasource"),
query: require("../../../controllers/query"),
}

View File

@ -0,0 +1,32 @@
const TestConfig = require("./TestConfiguration")
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))
let request, config
exports.beforeAll = () => {
config = new TestConfig()
request = config.getRequest()
}
exports.afterAll = () => {
if (config) {
config.end()
}
request = null
config = null
}
exports.getRequest = () => {
if (!request) {
exports.beforeAll()
}
return request
}
exports.getConfig = () => {
if (!config) {
exports.beforeAll()
}
return config
}

View File

@ -0,0 +1,87 @@
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles")
const {
BUILTIN_PERMISSION_IDS,
} = require("../../../../utilities/security/permissions")
exports.basicTable = () => {
return {
name: "TestTable",
type: "table",
key: "name",
schema: {
name: {
type: "string",
constraints: {
type: "string",
},
},
description: {
type: "string",
constraints: {
type: "string",
},
},
},
}
}
exports.basicAutomation = () => {
return {
name: "My Automation",
screenId: "kasdkfldsafkl",
live: true,
uiTree: {},
definition: {
trigger: {
inputs: {},
},
steps: [],
},
type: "automation",
}
}
exports.basicRow = tableId => {
return {
name: "Test Contact",
description: "original description",
status: "new",
tableId: tableId,
}
}
exports.basicRole = () => {
return {
name: "NewRole",
inherits: BUILTIN_ROLE_IDS.BASIC,
permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY,
}
}
exports.basicDatasource = () => {
return {
type: "datasource",
name: "Test",
source: "POSTGRES",
config: {},
}
}
exports.basicQuery = datasourceId => {
return {
datasourceId: datasourceId,
name: "New Query",
parameters: [],
fields: {},
schema: {},
queryVerb: "read",
}
}
exports.basicUser = role => {
return {
email: "bill@bill.com",
password: "yeeooo",
roleId: role,
}
}

View File

@ -1,65 +1,56 @@
const { const setup = require("./utilities")
createApplication,
createTable,
supertest,
defaultHeaders,
getDocument
} = require("./couchTestUtils")
describe("/views", () => { describe("/views", () => {
let request let request = setup.getRequest()
let server let config = setup.getConfig()
let app
let appId
let table let table
const createView = async (config = { afterAll(setup.afterAll)
name: "TestView",
field: "Price",
calculation: "stats",
tableId: table._id
}) =>
await request
.post(`/api/views`)
.send(config)
.set(defaultHeaders(appId))
.expect('Content-Type', /json/)
.expect(200)
const createRow = async row => request
.post(`/api/${table._id}/rows`)
.send(row)
.set(defaultHeaders(appId))
.expect('Content-Type', /json/)
.expect(200)
beforeAll(async () => {
({ request, server } = await supertest())
})
beforeEach(async () => { beforeEach(async () => {
app = await createApplication(request) await config.init()
appId = app.instance._id
})
afterAll(() => {
server.close()
}) })
describe("create", () => { describe("create", () => {
beforeEach(async () => { beforeEach(async () => {
table = await createTable(request, appId); table = await config.createTable()
}) })
it("returns a success message when the view is successfully created", async () => { it("returns a success message when the view is successfully created", async () => {
const res = await createView() const res = await request
expect(res.res.statusMessage).toEqual("View TestView saved successfully."); .post(`/api/views`)
.send({
name: "TestView",
field: "Price",
calculation: "stats",
tableId: table._id,
})
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.res.statusMessage).toEqual(
"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 () => {
const res = await createView() const res = await request
expect(res.res.statusMessage).toEqual("View TestView saved successfully."); .post(`/api/views`)
const updatedTable = await getDocument(appId, table._id) .send({
name: "TestView",
field: "Price",
calculation: "stats",
tableId: table._id,
})
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.res.statusMessage).toEqual(
"View TestView saved successfully."
)
const updatedTable = await config.getTable(table._id)
expect(updatedTable.views).toEqual({ expect(updatedTable.views).toEqual({
TestView: { TestView: {
field: "Price", field: "Price",
@ -88,88 +79,98 @@ describe("/views", () => {
field: { field: {
type: "string", type: "string",
}, },
} },
} },
}); })
})
}) })
});
describe("fetch", () => { describe("fetch", () => {
beforeEach(async () => { beforeEach(async () => {
table = await createTable(request, appId); table = await config.createTable()
}); })
it("returns only custom views", async () => { it("returns only custom views", async () => {
await createView() await config.createView({
name: "TestView",
field: "Price",
calculation: "stats",
tableId: table._id,
})
const res = await request const res = await request
.get(`/api/views`) .get(`/api/views`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toBe(1) expect(res.body.length).toBe(1)
expect(res.body.find(({ name }) => name === "TestView")).toBeDefined() expect(res.body.find(({ name }) => name === "TestView")).toBeDefined()
}) })
}); })
describe("query", () => { describe("query", () => {
beforeEach(async () => { beforeEach(async () => {
table = await createTable(request, appId); table = await config.createTable()
}); })
it("returns data for the created view", async () => { it("returns data for the created view", async () => {
await createView() await config.createView({
await createRow({ name: "TestView",
field: "Price",
calculation: "stats",
tableId: table._id, tableId: table._id,
Price: 1000
}) })
await createRow({ await config.createRow({
tableId: table._id, tableId: table._id,
Price: 2000 Price: 1000,
}) })
await createRow({ await config.createRow({
tableId: table._id, tableId: table._id,
Price: 4000 Price: 2000,
})
await config.createRow({
tableId: table._id,
Price: 4000,
}) })
const res = await request const res = await request
.get(`/api/views/TestView?calculation=stats`) .get(`/api/views/TestView?calculation=stats`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toBe(1) expect(res.body.length).toBe(1)
expect(res.body).toMatchSnapshot() expect(res.body).toMatchSnapshot()
}) })
it("returns data for the created view using a group by", async () => { it("returns data for the created view using a group by", async () => {
await createView({ await config.createView({
calculation: "stats", calculation: "stats",
name: "TestView", name: "TestView",
field: "Price", field: "Price",
groupBy: "Category", groupBy: "Category",
tableId: table._id tableId: table._id,
}) })
await createRow({ await config.createRow({
tableId: table._id, tableId: table._id,
Price: 1000, Price: 1000,
Category: "One" Category: "One",
}) })
await createRow({ await config.createRow({
tableId: table._id, tableId: table._id,
Price: 2000, Price: 2000,
Category: "One" Category: "One",
}) })
await createRow({ await config.createRow({
tableId: table._id, tableId: table._id,
Price: 4000, Price: 4000,
Category: "Two" Category: "Two",
}) })
const res = await request const res = await request
.get(`/api/views/TestView?calculation=stats&group=Category`) .get(`/api/views/TestView?calculation=stats&group=Category`)
.set(defaultHeaders(appId)) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.length).toBe(2) expect(res.body.length).toBe(2)
expect(res.body).toMatchSnapshot() expect(res.body).toMatchSnapshot()
}) })
}); })
}); })

View File

@ -49,6 +49,12 @@ const TYPE_TRANSFORM_MAP = {
"": null, "": null,
[undefined]: undefined, [undefined]: undefined,
[null]: null, [null]: null,
parse: date => {
if (date instanceof Date) {
return date.toISOString()
}
return date
},
}, },
[FieldTypes.ATTACHMENT]: { [FieldTypes.ATTACHMENT]: {
"": [], "": [],