350 lines
12 KiB
JavaScript
350 lines
12 KiB
JavaScript
const {
|
|
checkBuilderEndpoint,
|
|
getAllTableRows,
|
|
clearAllAutomations,
|
|
testAutomation,
|
|
} = require("./utilities/TestFunctions")
|
|
const setup = require("./utilities")
|
|
const { basicAutomation, newAutomation, automationTrigger, automationStep } = setup.structures
|
|
const MAX_RETRIES = 4
|
|
const { TRIGGER_DEFINITIONS, BUILTIN_ACTION_DEFINITIONS } = require("../../../automations")
|
|
const { events } = require("@budibase/backend-core")
|
|
|
|
|
|
jest.setTimeout(30000)
|
|
|
|
describe("/automations", () => {
|
|
let request = setup.getRequest()
|
|
let config = setup.getConfig()
|
|
|
|
afterAll(setup.afterAll)
|
|
|
|
beforeAll(async () => {
|
|
await config.init()
|
|
})
|
|
|
|
beforeEach(() => {
|
|
events.automation.deleted.mockClear()
|
|
})
|
|
|
|
describe("get definitions", () => {
|
|
it("returns a list of definitions for actions", async () => {
|
|
const res = await request
|
|
.get(`/api/automations/action/list`)
|
|
.set(config.defaultHeaders())
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
expect(Object.keys(res.body).length).not.toEqual(0)
|
|
})
|
|
|
|
it("returns a list of definitions for triggerInfo", async () => {
|
|
const res = await request
|
|
.get(`/api/automations/trigger/list`)
|
|
.set(config.defaultHeaders())
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
expect(Object.keys(res.body).length).not.toEqual(0)
|
|
})
|
|
|
|
it("returns all of the definitions in one", async () => {
|
|
const res = await request
|
|
.get(`/api/automations/definitions/list`)
|
|
.set(config.defaultHeaders())
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
let definitionsLength = Object.keys(BUILTIN_ACTION_DEFINITIONS).length
|
|
definitionsLength-- // OUTGOING_WEBHOOK is deprecated
|
|
|
|
expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(definitionsLength)
|
|
expect(Object.keys(res.body.trigger).length).toEqual(Object.keys(TRIGGER_DEFINITIONS).length)
|
|
})
|
|
})
|
|
|
|
describe("create", () => {
|
|
it("creates an automation with no steps", async () => {
|
|
const automation = newAutomation()
|
|
automation.definition.steps = []
|
|
|
|
const res = await request
|
|
.post(`/api/automations`)
|
|
.set(config.defaultHeaders())
|
|
.send(automation)
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
expect(res.body.message).toEqual("Automation created successfully")
|
|
expect(res.body.automation.name).toEqual("My Automation")
|
|
expect(res.body.automation._id).not.toEqual(null)
|
|
expect(events.automation.created).toBeCalledTimes(1)
|
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
})
|
|
|
|
it("creates an automation with steps", async () => {
|
|
const automation = newAutomation()
|
|
automation.definition.steps.push(automationStep())
|
|
jest.clearAllMocks()
|
|
|
|
const res = await request
|
|
.post(`/api/automations`)
|
|
.set(config.defaultHeaders())
|
|
.send(automation)
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
expect(res.body.message).toEqual("Automation created successfully")
|
|
expect(res.body.automation.name).toEqual("My Automation")
|
|
expect(res.body.automation._id).not.toEqual(null)
|
|
expect(events.automation.created).toBeCalledTimes(1)
|
|
expect(events.automation.stepCreated).toBeCalledTimes(2)
|
|
})
|
|
|
|
it("should apply authorization to endpoint", async () => {
|
|
const automation = newAutomation()
|
|
await checkBuilderEndpoint({
|
|
config,
|
|
method: "POST",
|
|
url: `/api/automations`,
|
|
body: automation
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("find", () => {
|
|
it("should be able to find the automation", async () => {
|
|
const automation = await config.createAutomation()
|
|
const res = await request
|
|
.get(`/api/automations/${automation._id}`)
|
|
.set(config.defaultHeaders())
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
expect(res.body._id).toEqual(automation._id)
|
|
expect(res.body._rev).toEqual(automation._rev)
|
|
})
|
|
})
|
|
|
|
describe("test", () => {
|
|
it("tests the automation successfully", async () => {
|
|
let table = await config.createTable()
|
|
let automation = newAutomation()
|
|
automation.definition.trigger.inputs.tableId = table._id
|
|
automation.definition.steps[0].inputs = {
|
|
row: {
|
|
name: "{{trigger.row.name}}",
|
|
description: "{{trigger.row.description}}",
|
|
tableId: table._id
|
|
}
|
|
}
|
|
automation.appId = config.appId
|
|
automation = await config.createAutomation(automation)
|
|
await setup.delay(500)
|
|
const res = await testAutomation(config, automation)
|
|
expect(events.automation.tested).toBeCalledTimes(1)
|
|
// 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
|
|
// 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
|
|
for (let tries = 0; tries < MAX_RETRIES; tries++) {
|
|
expect(res.body).toBeDefined()
|
|
await setup.delay(500)
|
|
let elements = await getAllTableRows(config)
|
|
// don't test it unless there are values to test
|
|
if (elements.length > 1) {
|
|
expect(elements.length).toBeGreaterThanOrEqual(MAX_RETRIES)
|
|
expect(elements[0].name).toEqual("Test")
|
|
expect(elements[0].description).toEqual("TEST")
|
|
return
|
|
}
|
|
}
|
|
throw "Failed to find the rows"
|
|
})
|
|
})
|
|
|
|
describe("update", () => {
|
|
|
|
const update = async (automation) => {
|
|
return request
|
|
.put(`/api/automations`)
|
|
.set(config.defaultHeaders())
|
|
.send(automation)
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
}
|
|
|
|
const updateWithPost = async (automation) => {
|
|
return request
|
|
.post(`/api/automations`)
|
|
.set(config.defaultHeaders())
|
|
.send(automation)
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
}
|
|
|
|
it("updates a automations name", async () => {
|
|
let automation = newAutomation()
|
|
await config.createAutomation(automation)
|
|
automation.name = "Updated Name"
|
|
jest.clearAllMocks()
|
|
|
|
const res = await update(automation)
|
|
|
|
const automationRes = res.body.automation
|
|
const message = res.body.message
|
|
|
|
// doc attributes
|
|
expect(automationRes._id).toEqual(automation._id)
|
|
expect(automationRes._rev).toBeDefined()
|
|
expect(automationRes._rev).not.toEqual(automation._rev)
|
|
// content updates
|
|
expect(automationRes.name).toEqual("Updated Name")
|
|
expect(message).toEqual(`Automation ${automation._id} updated successfully.`)
|
|
// events
|
|
expect(events.automation.created).not.toBeCalled()
|
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
})
|
|
|
|
|
|
it("updates a automations name using POST request", async () => {
|
|
let automation = newAutomation()
|
|
await config.createAutomation(automation)
|
|
automation.name = "Updated Name"
|
|
jest.clearAllMocks()
|
|
|
|
// the POST request will defer to the update
|
|
// when an id has been supplied.
|
|
const res = await updateWithPost(automation)
|
|
|
|
const automationRes = res.body.automation
|
|
const message = res.body.message
|
|
// doc attributes
|
|
expect(automationRes._id).toEqual(automation._id)
|
|
expect(automationRes._rev).toBeDefined()
|
|
expect(automationRes._rev).not.toEqual(automation._rev)
|
|
// content updates
|
|
expect(automationRes.name).toEqual("Updated Name")
|
|
expect(message).toEqual(`Automation ${automation._id} updated successfully.`)
|
|
// events
|
|
expect(events.automation.created).not.toBeCalled()
|
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
})
|
|
|
|
it("updates an automation trigger", async () => {
|
|
let automation = newAutomation()
|
|
automation = await config.createAutomation(automation)
|
|
automation.definition.trigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK)
|
|
jest.clearAllMocks()
|
|
|
|
await update(automation)
|
|
|
|
// events
|
|
expect(events.automation.created).not.toBeCalled()
|
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
|
expect(events.automation.triggerUpdated).toBeCalledTimes(1)
|
|
})
|
|
|
|
it("adds automation steps", async () => {
|
|
let automation = newAutomation()
|
|
automation = await config.createAutomation(automation)
|
|
automation.definition.steps.push(automationStep())
|
|
automation.definition.steps.push(automationStep())
|
|
jest.clearAllMocks()
|
|
|
|
// check the post request honours updates with same id
|
|
await update(automation)
|
|
|
|
// events
|
|
expect(events.automation.stepCreated).toBeCalledTimes(2)
|
|
expect(events.automation.created).not.toBeCalled()
|
|
expect(events.automation.stepDeleted).not.toBeCalled()
|
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
})
|
|
|
|
|
|
it("removes automation steps", async () => {
|
|
let automation = newAutomation()
|
|
automation.definition.steps.push(automationStep())
|
|
automation = await config.createAutomation(automation)
|
|
automation.definition.steps = []
|
|
jest.clearAllMocks()
|
|
|
|
// check the post request honours updates with same id
|
|
await update(automation)
|
|
|
|
// events
|
|
expect(events.automation.stepDeleted).toBeCalledTimes(2)
|
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
expect(events.automation.created).not.toBeCalled()
|
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
})
|
|
|
|
it("adds and removes automation steps", async () => {
|
|
let automation = newAutomation()
|
|
automation = await config.createAutomation(automation)
|
|
automation.definition.steps = [automationStep(), automationStep()]
|
|
jest.clearAllMocks()
|
|
|
|
// check the post request honours updates with same id
|
|
await update(automation)
|
|
|
|
// events
|
|
expect(events.automation.stepCreated).toBeCalledTimes(2)
|
|
expect(events.automation.stepDeleted).toBeCalledTimes(1)
|
|
expect(events.automation.created).not.toBeCalled()
|
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
})
|
|
})
|
|
|
|
describe("fetch", () => {
|
|
it("return all the automations for an instance", async () => {
|
|
await clearAllAutomations(config)
|
|
const autoConfig = basicAutomation()
|
|
automation = await config.createAutomation(autoConfig)
|
|
const res = await request
|
|
.get(`/api/automations`)
|
|
.set(config.defaultHeaders())
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
expect(res.body[0]).toEqual(expect.objectContaining(autoConfig))
|
|
})
|
|
|
|
it("should apply authorization to endpoint", async () => {
|
|
await checkBuilderEndpoint({
|
|
config,
|
|
method: "GET",
|
|
url: `/api/automations`,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("destroy", () => {
|
|
it("deletes a automation by its ID", async () => {
|
|
const automation = await config.createAutomation()
|
|
const res = await request
|
|
.delete(`/api/automations/${automation.id}/${automation.rev}`)
|
|
.set(config.defaultHeaders())
|
|
.expect('Content-Type', /json/)
|
|
.expect(200)
|
|
|
|
expect(res.body.id).toEqual(automation._id)
|
|
expect(events.automation.deleted).toBeCalledTimes(1)
|
|
})
|
|
|
|
it("should apply authorization to endpoint", async () => {
|
|
const automation = await config.createAutomation()
|
|
await checkBuilderEndpoint({
|
|
config,
|
|
method: "DELETE",
|
|
url: `/api/automations/${automation.id}/${automation._rev}`,
|
|
})
|
|
})
|
|
})
|
|
})
|