mirror of https://github.com/Budibase/budibase.git
committed by
GitHub
20 changed files with 816 additions and 921 deletions
@ -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) |
|||
} |
|||
@ -1 +0,0 @@ |
|||
module.exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) |
|||
@ -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 |
|||
@ -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) |
|||
} |
|||
@ -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"), |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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, |
|||
} |
|||
} |
|||
Loading…
Reference in new issue