Testing some of the automation actions (still a few to complete) and adding quick test case for the self host stuff.

This commit is contained in:
mike12345567 2021-03-11 18:29:48 +00:00
parent f71b92e54f
commit 189a673c9c
27 changed files with 306 additions and 57 deletions

View File

@ -55,9 +55,8 @@
"!src/db/views/*.js", "!src/db/views/*.js",
"!src/api/routes/tests/**/*.js", "!src/api/routes/tests/**/*.js",
"!src/api/controllers/deploy/**/*.js", "!src/api/controllers/deploy/**/*.js",
"!src/api/controllers/static/templates/**/*", "!src/*.js",
"!src/api/controllers/static/selfhost/**/*", "!src/api/controllers/static/**/*"
"!src/*.js"
], ],
"coverageReporters": [ "coverageReporters": [
"lcov", "lcov",

View File

@ -3,8 +3,8 @@ const {
getAllTableRows, getAllTableRows,
clearAllAutomations, clearAllAutomations,
} = require("./utilities/TestFunctions") } = require("./utilities/TestFunctions")
const { basicAutomation } = require("./utilities/structures")
const setup = require("./utilities") const setup = require("./utilities")
const { basicAutomation } = setup.structures
const MAX_RETRIES = 4 const MAX_RETRIES = 4

View File

@ -1,6 +1,6 @@
let {basicDatasource} = require("./utilities/structures")
let {checkBuilderEndpoint} = require("./utilities/TestFunctions")
let setup = require("./utilities") let setup = require("./utilities")
let { basicDatasource } = setup.structures
let { checkBuilderEndpoint } = require("./utilities/TestFunctions")
describe("/datasources", () => { describe("/datasources", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -1,6 +1,6 @@
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicLayout } = require("./utilities/structures") const { basicLayout } = setup.structures
describe("/layouts", () => { describe("/layouts", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -1,6 +1,6 @@
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRow } = require("./utilities/structures") const { basicRow } = setup.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

View File

@ -1,9 +1,9 @@
// mock out postgres for this // mock out postgres for this
jest.mock("pg") jest.mock("pg")
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const { basicQuery, basicDatasource } = require("./utilities/structures")
const setup = require("./utilities") const setup = require("./utilities")
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const { basicQuery, basicDatasource } = setup.structures
describe("/queries", () => { describe("/queries", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -2,8 +2,8 @@ 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 setup = require("./utilities") const setup = require("./utilities")
const { basicRole } = setup.structures
describe("/roles", () => { describe("/roles", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -1,5 +1,5 @@
const setup = require("./utilities") const setup = require("./utilities")
const { basicScreen } = require("./utilities/structures") const { basicScreen } = setup.structures
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")

View File

@ -1,7 +1,6 @@
const { outputProcessing } = require("../../../utilities/rowProcessor") const { outputProcessing } = require("../../../utilities/rowProcessor")
const env = require("../../../environment")
const { basicRow } = require("./utilities/structures")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRow } = setup.structures
describe("/rows", () => { describe("/rows", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -1,6 +1,6 @@
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicScreen } = require("./utilities/structures") const { basicScreen } = setup.structures
describe("/screens", () => { describe("/screens", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -1,7 +1,7 @@
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions") const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
const { basicUser } = require("./utilities/structures")
const setup = require("./utilities") const setup = require("./utilities")
const { basicUser } = setup.structures
describe("/users", () => { describe("/users", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -1,15 +0,0 @@
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"),
screen: require("../../../controllers/screen"),
webhook: require("../../../controllers/webhook"),
layout: require("../../../controllers/layout"),
}

View File

@ -1,4 +1,5 @@
const TestConfig = require("./TestConfiguration") const TestConfig = require("../../../../tests/utilities/TestConfiguration")
const structures = require("../../../../tests/utilities/structures")
const env = require("../../../../environment") const env = require("../../../../environment")
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))
@ -51,3 +52,5 @@ exports.switchToCloudForFunction = async func => {
throw error throw error
} }
} }
exports.structures = structures

View File

@ -1,6 +1,6 @@
const setup = require("./utilities") const setup = require("./utilities")
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const { basicWebhook, basicAutomation } = require("./utilities/structures") const { basicWebhook, basicAutomation } = setup.structures
describe("/webhooks", () => { describe("/webhooks", () => {
let request = setup.getRequest() let request = setup.getRequest()

View File

@ -37,10 +37,12 @@ let AUTOMATION_BUCKET = env.AUTOMATION_BUCKET
let AUTOMATION_DIRECTORY = env.AUTOMATION_DIRECTORY let AUTOMATION_DIRECTORY = env.AUTOMATION_DIRECTORY
let MANIFEST = null let MANIFEST = null
/* instanbul ignore next */
function buildBundleName(pkgName, version) { function buildBundleName(pkgName, version) {
return `${pkgName}@${version}.min.js` return `${pkgName}@${version}.min.js`
} }
/* instanbul ignore next */
async function downloadPackage(name, version, bundleName) { async function downloadPackage(name, version, bundleName) {
await download( await download(
`${AUTOMATION_BUCKET}/${name}/${version}/${bundleName}`, `${AUTOMATION_BUCKET}/${name}/${version}/${bundleName}`,
@ -96,5 +98,6 @@ module.exports.init = async function() {
return MANIFEST return MANIFEST
} }
// definitions will have downloaded ones added to it, while builtin won't
module.exports.DEFINITIONS = BUILTIN_DEFINITIONS module.exports.DEFINITIONS = BUILTIN_DEFINITIONS
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS

View File

@ -59,15 +59,14 @@ module.exports.definition = {
} }
module.exports.run = async function({ inputs, appId, apiKey, emitter }) { module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
// TODO: better logging of when actions are missed due to missing parameters
if (inputs.row == null || inputs.row.tableId == null) { if (inputs.row == null || inputs.row.tableId == null) {
return return {
success: false,
response: {
message: "Invalid inputs",
},
}
} }
inputs.row = await automationUtils.cleanUpRow(
appId,
inputs.row.tableId,
inputs.row
)
// have to clean up the row, remove the table from it // have to clean up the row, remove the table from it
const ctx = { const ctx = {
params: { params: {
@ -81,6 +80,11 @@ module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
} }
try { try {
inputs.row = await automationUtils.cleanUpRow(
appId,
inputs.row.tableId,
inputs.row
)
if (env.CLOUD) { if (env.CLOUD) {
await usage.update(apiKey, usage.Properties.ROW, 1) await usage.update(apiKey, usage.Properties.ROW, 1)
} }

View File

@ -12,6 +12,9 @@ const PrettyLogicConditions = {
[LogicConditions.LESS_THAN]: "Less than", [LogicConditions.LESS_THAN]: "Less than",
} }
module.exports.LogicConditions = LogicConditions
module.exports.PrettyLogicConditions = PrettyLogicConditions
module.exports.definition = { module.exports.definition = {
name: "Filter", name: "Filter",
tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}", tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}",
@ -64,7 +67,7 @@ module.exports.run = async function filter({ inputs }) {
value = Date.parse(value) value = Date.parse(value)
field = Date.parse(field) field = Date.parse(field)
} }
let success let success = false
if (typeof field !== "object" && typeof value !== "object") { if (typeof field !== "object" && typeof value !== "object") {
switch (condition) { switch (condition) {
case LogicConditions.EQUAL: case LogicConditions.EQUAL:
@ -79,8 +82,6 @@ module.exports.run = async function filter({ inputs }) {
case LogicConditions.LESS_THAN: case LogicConditions.LESS_THAN:
success = field < value success = field < value
break break
default:
return
} }
} else { } else {
success = false success = false

View File

@ -0,0 +1,57 @@
const usageQuota = require("../../utilities/usageQuota")
const env = require("../../environment")
const setup = require("./utilities")
jest.mock("../../utilities/usageQuota")
describe("test the create row action", () => {
let table, row
let config = setup.getConfig()
beforeEach(async () => {
await config.init()
table = await config.createTable()
row = {
tableId: table._id,
name: "test",
description: "test",
}
})
afterAll(setup.afterAll)
it("should be able to run the action", async () => {
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {
row,
})
expect(res.id).toBeDefined()
expect(res.revision).toBeDefined()
const gottenRow = await config.getRow(table._id, res.id)
expect(gottenRow.name).toEqual("test")
expect(gottenRow.description).toEqual("test")
})
it("should return an error (not throw) when bad info provided", async () => {
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {
row: {
tableId: "invalid",
invalid: "invalid",
}
})
expect(res.success).toEqual(false)
})
it("check usage quota attempts", async () => {
env.CLOUD = true
await setup.runStep(setup.actions.CREATE_ROW.stepId, {
row
})
expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "rows", 1)
env.CLOUD = false
})
it("should check invalid inputs return an error", async () => {
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {})
expect(res.success).toEqual(false)
})
})

View File

@ -0,0 +1,43 @@
const usageQuota = require("../../utilities/usageQuota")
const env = require("../../environment")
const setup = require("./utilities")
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
const { ViewNames } = require("../../db/utils")
jest.mock("../../utilities/usageQuota")
describe("test the create user action", () => {
let config = setup.getConfig()
let user
beforeEach(async () => {
await config.init()
user = {
email: "test@test.com",
password: "password",
roleId: BUILTIN_ROLE_IDS.POWER
}
})
afterAll(setup.afterAll)
it("should be able to run the action", async () => {
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, user)
expect(res.id).toBeDefined()
expect(res.revision).toBeDefined()
const userDoc = await config.getRow(ViewNames.USERS, res.id)
expect(userDoc.email).toEqual(user.email)
})
it("should return an error if no inputs provided", async () => {
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, {})
expect(res.success).toEqual(false)
})
it("check usage quota attempts", async () => {
env.CLOUD = true
await setup.runStep(setup.actions.CREATE_USER.stepId, user)
expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "users", 1)
env.CLOUD = false
})
})

View File

@ -0,0 +1,12 @@
const setup = require("./utilities")
describe("test the delay action", () => {
it("should be able to run the delay", async () => {
const time = 100
const before = Date.now()
await setup.runStep(setup.logic.DELAY.stepId, { time: time })
const now = Date.now()
// divide by two just so that test will always pass as long as there was some sort of delay
expect(now - before).toBeGreaterThanOrEqual(time / 2)
})
})

View File

@ -0,0 +1,48 @@
const setup = require("./utilities")
const { LogicConditions } = require("../steps/filter")
describe("test the delay action", () => {
async function checkFilter(field, condition, value, pass = true) {
let res = await setup.runStep(setup.logic.FILTER.stepId,
{ field, condition, value }
)
expect(res.success).toEqual(pass)
}
it("should be able test equality", async () => {
await checkFilter("hello", LogicConditions.EQUAL, "hello", true)
await checkFilter("hello", LogicConditions.EQUAL, "no", false)
})
it("should be able to test greater than", async () => {
await checkFilter(10, LogicConditions.GREATER_THAN, 5, true)
await checkFilter(10, LogicConditions.GREATER_THAN, 15, false)
})
it("should be able to test less than", async () => {
await checkFilter(5, LogicConditions.LESS_THAN, 10, true)
await checkFilter(15, LogicConditions.LESS_THAN, 10, false)
})
it("should be able to in-equality", async () => {
await checkFilter("hello", LogicConditions.NOT_EQUAL, "no", true)
await checkFilter(10, LogicConditions.NOT_EQUAL, 10, false)
})
it("check number coercion", async () => {
await checkFilter("10", LogicConditions.GREATER_THAN, "5", true)
})
it("check date coercion", async () => {
await checkFilter(
(new Date()).toISOString(),
LogicConditions.GREATER_THAN,
(new Date(-10000)).toISOString(),
true
)
})
it("check objects always false", async () => {
await checkFilter({}, LogicConditions.EQUAL, {}, false)
})
})

View File

@ -0,0 +1,43 @@
const TestConfig = require("../../../tests/utilities/TestConfiguration")
const actions = require("../../actions")
const logic = require("../../logic")
const emitter = require("../../../events/index")
let config
exports.getConfig = () => {
if (!config) {
config = new TestConfig(false)
}
return config
}
exports.afterAll = () => {
config.end()
}
exports.runStep = async function runStep(stepId, inputs) {
let step
if (
Object.values(exports.actions)
.map(action => action.stepId)
.includes(stepId)
) {
step = await actions.getAction(stepId)
} else {
step = logic.getLogic(stepId)
}
expect(step).toBeDefined()
return step({
inputs,
appId: config ? config.getAppId() : null,
// don't really need an API key, mocked out usage quota, not being tested here
apiKey: exports.apiKey,
emitter,
})
}
exports.apiKey = "test"
exports.actions = actions.BUILTIN_DEFINITIONS
exports.logic = logic.BUILTIN_DEFINITIONS

View File

@ -3,7 +3,7 @@ const usageQuota = require("../../utilities/usageQuota")
const CouchDB = require("../../db") const CouchDB = require("../../db")
const env = require("../../environment") const env = require("../../environment")
jest.mock("../../db"); jest.mock("../../db")
jest.mock("../../utilities/usageQuota") jest.mock("../../utilities/usageQuota")
jest.mock("../../environment") jest.mock("../../environment")

View File

@ -0,0 +1,26 @@
const selfhost = require("..")
const env = require("../../environment")
describe("test the setup process", () => {
beforeAll(() => {
env.SELF_HOSTED = true
})
beforeEach(async () => {
await selfhost.init()
})
afterAll(() => {
env.SELF_HOSTED = false
})
it("getSelfHostInfo", async () => {
let info = await selfhost.getSelfHostInfo()
expect(info._id).toEqual("self-host-info")
})
it("getSelfHostAPIKey", async () => {
let apiKey = await selfhost.getSelfHostAPIKey()
expect(typeof apiKey).toEqual("string")
})
})

View File

@ -1,6 +1,6 @@
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const env = require("../../../../environment") const env = require("../../environment")
const { const {
basicTable, basicTable,
basicRow, basicRow,
@ -15,18 +15,20 @@ const {
const controllers = require("./controllers") const controllers = require("./controllers")
const supertest = require("supertest") const supertest = require("supertest")
const fs = require("fs") const fs = require("fs")
const { budibaseAppsDir } = require("../../../../utilities/budibaseDir") const { budibaseAppsDir } = require("../../utilities/budibaseDir")
const { join } = require("path") const { join } = require("path")
const EMAIL = "babs@babs.com" const EMAIL = "babs@babs.com"
const PASSWORD = "babs_password" const PASSWORD = "babs_password"
class TestConfiguration { class TestConfiguration {
constructor() { constructor(openServer = true) {
env.PORT = 4002 if (openServer) {
this.server = require("../../../../app") env.PORT = 4002
// we need the request for logging in, involves cookies, hard to fake this.server = require("../../app")
this.request = supertest(this.server) // we need the request for logging in, involves cookies, hard to fake
this.request = supertest(this.server)
}
this.appId = null this.appId = null
this.allApps = [] this.allApps = []
} }
@ -61,7 +63,9 @@ class TestConfiguration {
} }
end() { end() {
this.server.close() if (this.server) {
this.server.close()
}
const appDir = budibaseAppsDir() const appDir = budibaseAppsDir()
const files = fs.readdirSync(appDir) const files = fs.readdirSync(appDir)
for (let file of files) { for (let file of files) {
@ -163,6 +167,10 @@ class TestConfiguration {
return this._req(config, { tableId: this.table._id }, controllers.row.save) return this._req(config, { tableId: this.table._id }, controllers.row.save)
} }
async getRow(tableId, rowId) {
return this._req(null, { tableId, rowId }, controllers.row.find)
}
async createRole(config = null) { async createRole(config = null) {
config = config || basicRole() config = config || basicRole()
return this._req(config, null, controllers.role.save) return this._req(config, null, controllers.role.save)
@ -285,6 +293,9 @@ class TestConfiguration {
} }
async login(email, password) { async login(email, password) {
if (!this.request) {
throw "Server has not been opened, cannot login."
}
if (!email || !password) { if (!email || !password) {
await this.createUser() await this.createUser()
email = EMAIL email = EMAIL

View File

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

View File

@ -1,9 +1,9 @@
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 { createHomeScreen } = require("../../../../constants/screens") const { createHomeScreen } = require("../../constants/screens")
const { EMPTY_LAYOUT } = require("../../../../constants/layouts") const { EMPTY_LAYOUT } = require("../../constants/layouts")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
exports.basicTable = () => { exports.basicTable = () => {