Create tests for the row saved trigger.

This commit is contained in:
Sam Rose 2025-02-10 13:06:13 +00:00
parent 18b296a850
commit af188e1c1a
No known key found for this signature in database
6 changed files with 115 additions and 19 deletions

View File

@ -199,6 +199,12 @@ class InMemoryQueue implements Partial<Queue> {
return this as unknown as Queue
}
off(event: string, callback: (...args: any[]) => void): Queue {
// @ts-expect-error - this callback can be one of many types
this._emitter.off(event, callback)
return this as unknown as Queue
}
async count() {
return this._messages.length
}

View File

@ -182,7 +182,7 @@ if (descriptions.length) {
},
})
await config.api.application.publish(config.getAppId())
await config.api.application.publish()
const prodQuery = await config.api.query.getProd(query._id!)
expect(prodQuery._id).toEqual(query._id)

View File

@ -1,7 +1,6 @@
import { createAutomationBuilder } from "../utilities/AutomationTestBuilder"
import TestConfiguration from "../../../tests/utilities/TestConfiguration"
import { getQueue } from "../.."
import { Job } from "bull"
import { captureAutomationRuns } from "../utilities"
describe("cron trigger", () => {
const config = new TestConfiguration()
@ -15,15 +14,6 @@ describe("cron trigger", () => {
})
it("should queue a Bull cron job", async () => {
const queue = getQueue()
expect(await queue.getCompletedCount()).toEqual(0)
const jobPromise = new Promise<Job>(resolve => {
queue.on("completed", async job => {
resolve(job)
})
})
await createAutomationBuilder(config)
.onCron({ cron: "* * * * *" })
.serverLog({
@ -31,12 +21,12 @@ describe("cron trigger", () => {
})
.save()
await config.api.application.publish(config.getAppId())
const jobs = await captureAutomationRuns(() =>
config.api.application.publish()
)
expect(jobs).toHaveLength(1)
expect(await queue.getCompletedCount()).toEqual(1)
const job = await jobPromise
const repeat = job.opts?.repeat
const repeat = jobs[0].opts?.repeat
if (!repeat || !("cron" in repeat)) {
throw new Error("Expected cron repeat")
}

View File

@ -0,0 +1,52 @@
import { createAutomationBuilder } from "../utilities/AutomationTestBuilder"
import TestConfiguration from "../../../tests/utilities/TestConfiguration"
import { Table } from "@budibase/types"
import { basicTable } from "../../../tests/utilities/structures"
import { captureAutomationRuns } from "../utilities"
describe("row saved trigger", () => {
const config = new TestConfiguration()
let table: Table
beforeAll(async () => {
await config.init()
table = await config.api.table.save(basicTable())
await createAutomationBuilder(config)
.onRowSaved({ tableId: table._id! })
.serverLog({ text: "Row created!" })
.save()
await config.api.application.publish()
})
afterAll(() => {
config.end()
})
it("should queue a Bull job when a row is created", async () => {
const jobs = await captureAutomationRuns(() =>
config.withProdApp(() => config.api.row.save(table._id!, { name: "foo" }))
)
expect(jobs).toHaveLength(1)
expect(jobs[0].data.event).toEqual(
expect.objectContaining({
tableId: table._id!,
row: expect.objectContaining({ name: "foo" }),
})
)
})
it("should not fire for rows created in other tables", async () => {
const otherTable = await config.api.table.save(basicTable())
await config.api.application.publish()
const jobs = await captureAutomationRuns(() =>
config.withProdApp(() =>
config.api.row.save(otherTable._id!, { name: "foo" })
)
)
expect(jobs).toBeEmpty()
})
})

View File

@ -3,8 +3,15 @@ import { context } from "@budibase/backend-core"
import { BUILTIN_ACTION_DEFINITIONS, getAction } from "../../actions"
import emitter from "../../../events/index"
import env from "../../../environment"
import { AutomationActionStepId, Datasource } from "@budibase/types"
import {
AutomationActionStepId,
AutomationData,
Datasource,
} from "@budibase/types"
import { Knex } from "knex"
import { getQueue } from "../.."
import { Job } from "bull"
import { helpers } from "@budibase/shared-core"
let config: TestConfiguration
@ -63,6 +70,44 @@ export async function runStep(
}
}
/**
* Capture all automation runs that occur during the execution of a function.
* This function will wait for all messages to be processed before returning.
*/
export async function captureAutomationRuns(
f: () => Promise<unknown>
): Promise<Job<AutomationData>[]> {
const runs: Job<AutomationData>[] = []
const queue = getQueue()
let messagesReceived = 0
const completedListener = async (job: Job<AutomationData>) => {
runs.push(job)
messagesReceived--
}
const messageListener = async () => {
messagesReceived++
}
queue.on("message", messageListener)
queue.on("completed", completedListener)
try {
await f()
// Queue messages tend to be send asynchronously in API handlers, so there's
// no guarantee that awaiting this function will have queued anything yet.
// We wait here to make sure we're queued _after_ any existing async work.
await helpers.wait(100)
} finally {
// eslint-disable-next-line no-unmodified-loop-condition
while (messagesReceived > 0) {
await helpers.wait(50)
}
queue.off("completed", completedListener)
queue.off("message", messageListener)
}
return runs
}
export async function saveTestQuery(
config: TestConfiguration,
client: Knex,

View File

@ -34,9 +34,12 @@ export class ApplicationAPI extends TestAPI {
}
publish = async (
appId: string,
appId?: string,
expectations?: Expectations
): Promise<PublishResponse> => {
if (!appId) {
appId = this.config.getAppId()
}
return await this._post<PublishResponse>(
`/api/applications/${appId}/publish`,
{