Finishing off automation test cases, above 90% coverage for automations codebase.
This commit is contained in:
parent
b63599d024
commit
ece948e4ef
|
@ -33,7 +33,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest --testPathIgnorePatterns=routes && npm run test:integration",
|
"test": "jest --testPathIgnorePatterns=routes && npm run test:integration",
|
||||||
"test:integration": "jest --runInBand --coverage",
|
"test:integration": "jest --coverage --detectOpenHandles",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"run:docker": "node src/index",
|
"run:docker": "node src/index",
|
||||||
"dev:builder": "cross-env PORT=4001 nodemon src/index.js",
|
"dev:builder": "cross-env PORT=4001 nodemon src/index.js",
|
||||||
|
|
|
@ -66,7 +66,7 @@ module.exports = server.listen(env.PORT || 0, async () => {
|
||||||
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
|
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
|
||||||
env._set("PORT", server.address().port)
|
env._set("PORT", server.address().port)
|
||||||
eventEmitter.emitPort(env.PORT)
|
eventEmitter.emitPort(env.PORT)
|
||||||
automations.init()
|
await automations.init()
|
||||||
// only init the self hosting DB info in the Pouch, not needed in self hosting prod
|
// only init the self hosting DB info in the Pouch, not needed in self hosting prod
|
||||||
if (!env.CLOUD) {
|
if (!env.CLOUD) {
|
||||||
await selfhost.init()
|
await selfhost.init()
|
||||||
|
|
|
@ -37,12 +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 */
|
/* istanbul ignore next */
|
||||||
function buildBundleName(pkgName, version) {
|
function buildBundleName(pkgName, version) {
|
||||||
return `${pkgName}@${version}.min.js`
|
return `${pkgName}@${version}.min.js`
|
||||||
}
|
}
|
||||||
|
|
||||||
/* instanbul ignore next */
|
/* istanbul 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}`,
|
||||||
|
@ -51,6 +51,7 @@ async function downloadPackage(name, version, bundleName) {
|
||||||
return require(join(AUTOMATION_DIRECTORY, bundleName))
|
return require(join(AUTOMATION_DIRECTORY, bundleName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
module.exports.getAction = async function(actionName) {
|
module.exports.getAction = async function(actionName) {
|
||||||
if (BUILTIN_ACTIONS[actionName] != null) {
|
if (BUILTIN_ACTIONS[actionName] != null) {
|
||||||
return BUILTIN_ACTIONS[actionName]
|
return BUILTIN_ACTIONS[actionName]
|
||||||
|
|
|
@ -30,23 +30,22 @@ async function updateQuota(automation) {
|
||||||
/**
|
/**
|
||||||
* This module is built purely to kick off the worker farm and manage the inputs/outputs
|
* This module is built purely to kick off the worker farm and manage the inputs/outputs
|
||||||
*/
|
*/
|
||||||
module.exports.init = function() {
|
module.exports.init = async function() {
|
||||||
actions.init().then(() => {
|
await actions.init()
|
||||||
triggers.automationQueue.process(async job => {
|
triggers.automationQueue.process(async job => {
|
||||||
try {
|
try {
|
||||||
if (env.CLOUD && job.data.automation && !env.SELF_HOSTED) {
|
if (env.CLOUD && job.data.automation && !env.SELF_HOSTED) {
|
||||||
job.data.automation.apiKey = await updateQuota(job.data.automation)
|
job.data.automation.apiKey = await updateQuota(job.data.automation)
|
||||||
}
|
|
||||||
if (env.BUDIBASE_ENVIRONMENT === "PRODUCTION") {
|
|
||||||
await runWorker(job)
|
|
||||||
} else {
|
|
||||||
await singleThread(job)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(
|
|
||||||
`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
if (env.BUDIBASE_ENVIRONMENT === "PRODUCTION") {
|
||||||
|
await runWorker(job)
|
||||||
|
} else {
|
||||||
|
await singleThread(job)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,151 @@
|
||||||
const automation = require("../index")
|
const automation = require("../index")
|
||||||
const usageQuota = require("../../utilities/usageQuota")
|
const usageQuota = require("../../utilities/usageQuota")
|
||||||
|
const thread = require("../thread")
|
||||||
|
const triggers = require("../triggers")
|
||||||
|
const { basicAutomation, basicTable } = require("../../tests/utilities/structures")
|
||||||
|
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
||||||
|
const { wait } = require("../../utilities")
|
||||||
|
const env = require("../../environment")
|
||||||
|
const { makePartial } = require("../../tests/utilities")
|
||||||
|
const { cleanInputValues } = require("../automationUtils")
|
||||||
|
|
||||||
|
let workerJob
|
||||||
|
|
||||||
jest.mock("../../utilities/usageQuota")
|
jest.mock("../../utilities/usageQuota")
|
||||||
|
usageQuota.getAPIKey.mockReturnValue({ apiKey: "test" })
|
||||||
|
jest.mock("../thread")
|
||||||
|
jest.spyOn(global.console, "error")
|
||||||
|
jest.mock("worker-farm", () => {
|
||||||
|
return () => {
|
||||||
|
const value = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValueOnce(undefined)
|
||||||
|
.mockReturnValueOnce("Error")
|
||||||
|
return (input, callback) => {
|
||||||
|
workerJob = input
|
||||||
|
if (callback) {
|
||||||
|
callback(value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Run through some parts of the automations system", () => {
|
||||||
|
let config = new TestConfig(false)
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await automation.init()
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
describe("Check the primary input functions to automations", () => {
|
|
||||||
it("should be able to init in builder", async () => {
|
it("should be able to init in builder", async () => {
|
||||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 })
|
||||||
|
await wait(100)
|
||||||
|
expect(workerJob).toBeUndefined()
|
||||||
|
expect(thread).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to init in cloud", async () => {
|
it("should be able to init in cloud", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "PRODUCTION"
|
||||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 })
|
||||||
|
await wait(100)
|
||||||
|
// haven't added a mock implementation so getAPIKey of usageQuota just returns undefined
|
||||||
|
expect(usageQuota.update).toHaveBeenCalledWith("test", "automationRuns", 1)
|
||||||
|
expect(workerJob).toBeDefined()
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "JEST"
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it("try error scenario", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "PRODUCTION"
|
||||||
|
// the second call will throw an error
|
||||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 })
|
||||||
|
await wait(100)
|
||||||
|
expect(console.error).toHaveBeenCalled()
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "JEST"
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to check triggering row filling", async () => {
|
||||||
|
const automation = basicAutomation()
|
||||||
|
let table = basicTable()
|
||||||
|
table.schema.boolean = {
|
||||||
|
type: "boolean",
|
||||||
|
constraints: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table.schema.number = {
|
||||||
|
type: "number",
|
||||||
|
constraints: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table.schema.datetime = {
|
||||||
|
type: "datetime",
|
||||||
|
constraints: {
|
||||||
|
type: "datetime",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table = await config.createTable(table)
|
||||||
|
automation.definition.trigger.inputs.tableId = table._id
|
||||||
|
const params = await triggers.fillRowOutput(automation, { appId: config.getAppId() })
|
||||||
|
expect(params.row).toBeDefined()
|
||||||
|
const date = new Date(params.row.datetime)
|
||||||
|
expect(typeof params.row.name).toBe("string")
|
||||||
|
expect(typeof params.row.boolean).toBe("boolean")
|
||||||
|
expect(typeof params.row.number).toBe("number")
|
||||||
|
expect(date.getFullYear()).toBe(1970)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check coercion", async () => {
|
||||||
|
const table = await config.createTable()
|
||||||
|
const automation = basicAutomation()
|
||||||
|
automation.definition.trigger.inputs.tableId = table._id
|
||||||
|
automation.definition.trigger.stepId = "APP"
|
||||||
|
automation.definition.trigger.inputs.fields = { a: "number" }
|
||||||
|
await triggers.externalTrigger(automation, {
|
||||||
|
appId: config.getAppId(),
|
||||||
|
fields: {
|
||||||
|
a: "1"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await wait(100)
|
||||||
|
expect(thread).toHaveBeenCalledWith(makePartial({
|
||||||
|
data: {
|
||||||
|
event: {
|
||||||
|
fields: {
|
||||||
|
a: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to clean inputs with the utilities", () => {
|
||||||
|
// can't clean without a schema
|
||||||
|
let output = cleanInputValues({a: "1"})
|
||||||
|
expect(output.a).toBe("1")
|
||||||
|
output = cleanInputValues({a: "1", b: "true", c: "false", d: 1, e: "help"}, {
|
||||||
|
properties: {
|
||||||
|
a: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
type: "boolean",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(output.a).toBe(1)
|
||||||
|
expect(output.b).toBe(true)
|
||||||
|
expect(output.c).toBe(false)
|
||||||
|
expect(output.d).toBe(1)
|
||||||
|
expect(output.e).toBe("help")
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -225,6 +225,7 @@ async function queueRelevantRowAutomations(event, eventType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.on("row:save", async function(event) {
|
emitter.on("row:save", async function(event) {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (!event || !event.row || !event.row.tableId) {
|
if (!event || !event.row || !event.row.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -232,6 +233,7 @@ emitter.on("row:save", async function(event) {
|
||||||
})
|
})
|
||||||
|
|
||||||
emitter.on("row:update", async function(event) {
|
emitter.on("row:update", async function(event) {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (!event || !event.row || !event.row.tableId) {
|
if (!event || !event.row || !event.row.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -239,6 +241,7 @@ emitter.on("row:update", async function(event) {
|
||||||
})
|
})
|
||||||
|
|
||||||
emitter.on("row:delete", async function(event) {
|
emitter.on("row:delete", async function(event) {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (!event || !event.row || !event.row.tableId) {
|
if (!event || !event.row || !event.row.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -272,6 +275,7 @@ async function fillRowOutput(automation, params) {
|
||||||
}
|
}
|
||||||
params.row = row
|
params.row = row
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
/* istanbul ignore next */
|
||||||
throw "Failed to find table for trigger"
|
throw "Failed to find table for trigger"
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
|
@ -297,6 +301,7 @@ module.exports.externalTrigger = async function(automation, params) {
|
||||||
automationQueue.add({ automation, event: params })
|
automationQueue.add({ automation, event: params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.fillRowOutput = fillRowOutput
|
||||||
module.exports.automationQueue = automationQueue
|
module.exports.automationQueue = automationQueue
|
||||||
|
|
||||||
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS
|
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
exports.makePartial = obj => {
|
||||||
|
const newObj = {}
|
||||||
|
for (let key of Object.keys(obj)) {
|
||||||
|
if (typeof obj[key] === "object") {
|
||||||
|
newObj[key] = exports.makePartial(obj[key])
|
||||||
|
} else {
|
||||||
|
newObj[key] = obj[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expect.objectContaining(newObj)
|
||||||
|
}
|
Loading…
Reference in New Issue