Merge branch 'master' into BUDI-9038/fix-js-completions

This commit is contained in:
Adria Navarro 2025-02-12 16:46:08 +01:00 committed by GitHub
commit e5bc6a6fef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 101 additions and 136 deletions

View File

@ -46,6 +46,7 @@ async function init() {
HTTP_LOGGING: "0",
VERSION: "0.0.0+local",
PASSWORD_MIN_LENGTH: "1",
OPENAI_API_KEY: "sk-abcdefghijklmnopqrstuvwxyz1234567890abcd",
}
config = { ...config, ...existingConfig }

View File

@ -1,100 +0,0 @@
jest.mock("../../threads/automation")
jest.mock("../../utilities/redis", () => ({
init: jest.fn(),
checkTestFlag: () => {
return false
},
shutdown: jest.fn(),
}))
jest.spyOn(global.console, "error")
import "../../environment"
import * as automation from "../index"
import * as thread from "../../threads/automation"
import * as triggers from "../triggers"
import { basicAutomation } from "../../tests/utilities/structures"
import { wait } from "../../utilities"
import { makePartial } from "../../tests/utilities"
import { cleanInputValues } from "../automationUtils"
import { Automation } from "@budibase/types"
import TestConfiguration from "../../tests/utilities/TestConfiguration"
describe("Run through some parts of the automations system", () => {
const config = new TestConfiguration()
beforeAll(async () => {
await automation.init()
await config.init()
})
afterAll(async () => {
await automation.shutdown()
config.end()
})
it("should be able to init in builder", async () => {
const automation: Automation = {
...basicAutomation(),
appId: config.appId!,
}
const fields: any = { a: 1, appId: config.appId }
await triggers.externalTrigger(automation, fields)
await wait(100)
expect(thread.execute).toHaveBeenCalled()
})
it("should check coercion", async () => {
const table = await config.createTable()
const automation: any = basicAutomation()
automation.definition.trigger.inputs.tableId = table._id
automation.definition.trigger.stepId = "APP"
automation.definition.trigger.inputs.fields = { a: "number" }
const fields: any = {
appId: config.getAppId(),
fields: {
a: "1",
},
}
await triggers.externalTrigger(automation, fields)
await wait(100)
expect(thread.execute).toHaveBeenCalledWith(
makePartial({
data: {
event: {
fields: {
a: 1,
},
},
},
}),
expect.any(Function)
)
})
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)
})
})

View File

@ -119,5 +119,31 @@ describe("automationUtils", () => {
schema,
})
})
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)
})
})
})

View File

@ -1,17 +1,13 @@
import * as automation from "../../index"
import * as triggers from "../../triggers"
import { basicTable, loopAutomation } from "../../../tests/utilities/structures"
import { context } from "@budibase/backend-core"
import { basicTable } from "../../../tests/utilities/structures"
import {
Table,
LoopStepType,
AutomationResults,
ServerLogStepOutputs,
CreateRowStepOutputs,
FieldType,
} from "@budibase/types"
import * as loopUtils from "../../loopUtils"
import { LoopInput } from "../../../definitions/automations"
import { createAutomationBuilder } from "../utilities/AutomationTestBuilder"
import TestConfiguration from "../../../tests/utilities/TestConfiguration"
@ -34,41 +30,46 @@ describe("Attempt to run a basic loop automation", () => {
config.end()
})
async function runLoop(loopOpts?: LoopInput): Promise<AutomationResults> {
const appId = config.getAppId()
return await context.doInAppContext(appId, async () => {
const params = { fields: { appId } }
const result = await triggers.externalTrigger(
loopAutomation(table._id!, loopOpts),
params,
{ getResponses: true }
)
if ("outputs" in result && !result.outputs.success) {
throw new Error("Unable to proceed - failed to return anything.")
}
return result as AutomationResults
})
}
it("attempt to run a basic loop", async () => {
const resp = await runLoop()
expect(resp.steps[2].outputs.iterations).toBe(1)
const result = await createAutomationBuilder(config)
.onAppAction()
.queryRows({
tableId: table._id!,
})
.loop({
option: LoopStepType.ARRAY,
binding: "{{ steps.1.rows }}",
})
.serverLog({ text: "log statement" })
.test({ fields: {} })
expect(result.steps[1].outputs.iterations).toBe(1)
})
it("test a loop with a string", async () => {
const resp = await runLoop({
option: LoopStepType.STRING,
binding: "a,b,c",
})
expect(resp.steps[2].outputs.iterations).toBe(3)
const result = await createAutomationBuilder(config)
.onAppAction()
.loop({
option: LoopStepType.STRING,
binding: "a,b,c",
})
.serverLog({ text: "log statement" })
.test({ fields: {} })
expect(result.steps[0].outputs.iterations).toBe(3)
})
it("test a loop with a binding that returns an integer", async () => {
const resp = await runLoop({
option: LoopStepType.ARRAY,
binding: "{{ 1 }}",
})
expect(resp.steps[2].outputs.iterations).toBe(1)
const result = await createAutomationBuilder(config)
.onAppAction()
.loop({
option: LoopStepType.ARRAY,
binding: "{{ 1 }}",
})
.serverLog({ text: "log statement" })
.test({ fields: {} })
expect(result.steps[0].outputs.iterations).toBe(1)
})
it("should run an automation with a trigger, loop, and create row step", async () => {

View File

@ -42,4 +42,33 @@ describe("app action trigger", () => {
})
)
})
it("should correct coerce values based on the schema", async () => {
const { automation } = await createAutomationBuilder(config)
.onAppAction({
fields: { text: "string", number: "number", boolean: "boolean" },
})
.serverLog({
text: "{{ fields.text }} {{ fields.number }} {{ fields.boolean }}",
})
.save()
await config.api.application.publish()
const jobs = await captureAutomationResults(automation, async () => {
await config.withProdApp(async () => {
await config.api.automation.trigger(automation._id!, {
fields: { text: "1", number: "2", boolean: "true" },
timeout: 1000,
})
})
})
expect(jobs).toHaveLength(1)
expect(jobs[0].data.event.fields).toEqual({
text: "1",
number: 2,
boolean: true,
})
})
})

View File

@ -145,13 +145,13 @@ class StepBuilder<
TStep extends AutomationTriggerStepId
> extends BranchStepBuilder<TStep> {
private readonly config: TestConfiguration
private readonly trigger: AutomationTrigger
private readonly _trigger: AutomationTrigger
private _name: string | undefined = undefined
constructor(config: TestConfiguration, trigger: AutomationTrigger) {
super()
this.config = config
this.trigger = trigger
this._trigger = trigger
}
name(n: string): this {
@ -165,7 +165,7 @@ class StepBuilder<
name,
definition: {
steps: this.steps,
trigger: this.trigger,
trigger: this._trigger,
stepNames: this.stepNames,
},
type: "automation",
@ -182,6 +182,13 @@ class StepBuilder<
const runner = await this.save()
return await runner.test(triggerOutput)
}
async trigger(
request: TriggerAutomationRequest
): Promise<TriggerAutomationResponse> {
const runner = await this.save()
return await runner.trigger(request)
}
}
class AutomationRunner<TStep extends AutomationTriggerStepId> {

View File

@ -15,6 +15,7 @@ export interface AutomationDataEvent {
oldRow?: Row
user?: UserBindings
timestamp?: number
fields?: Record<string, any>
}
export interface AutomationData {