Adding test cases for all formula response types.

This commit is contained in:
mike12345567 2024-11-07 17:01:42 +00:00
parent 4c7103e5fd
commit db3c6c36fc
1 changed files with 92 additions and 16 deletions

View File

@ -11,9 +11,9 @@ import emitter from "../../../../src/events"
import { outputProcessing } from "../../../utilities/rowProcessor"
import {
context,
features,
InternalTable,
tenancy,
features,
utils,
} from "@budibase/backend-core"
import { quotas } from "@budibase/pro"
@ -21,27 +21,28 @@ import {
AIOperationEnum,
AttachmentFieldMetadata,
AutoFieldSubType,
BBReferenceFieldSubType,
Datasource,
DateFieldMetadata,
DeleteRow,
FieldSchema,
FieldType,
BBReferenceFieldSubType,
FormulaResponseType,
FormulaType,
INTERNAL_TABLE_SOURCE_ID,
JsonFieldSubType,
NumberFieldMetadata,
QuotaUsageType,
RelationSchemaField,
RelationshipType,
Row,
RowExportFormat,
SaveTableRequest,
StaticQuotaName,
Table,
TableSchema,
TableSourceType,
UpdatedRowEventEmitter,
TableSchema,
JsonFieldSubType,
RowExportFormat,
RelationSchemaField,
} from "@budibase/types"
import { generator, mocks } from "@budibase/backend-core/tests"
import _, { merge } from "lodash"
@ -50,6 +51,7 @@ import { Knex } from "knex"
import { InternalTables } from "../../../db/utils"
import { withEnv } from "../../../environment"
import { JsTimeoutError } from "@budibase/string-templates"
import { isDate } from "../../../utilities"
jest.mock("@budibase/pro", () => ({
...jest.requireActual("@budibase/pro"),
@ -89,6 +91,10 @@ async function waitForEvent(
return await p
}
function encodeJS(binding: string) {
return `{{ js "${Buffer.from(binding).toString("base64")}"}}`
}
describe.each([
["lucene", undefined],
["sqs", undefined],
@ -3476,7 +3482,7 @@ describe.each([
describe("Formula fields", () => {
let table: Table
let otherTable: Table
let relatedRow: Row
let relatedRow: Row, mainRow: Row
beforeAll(async () => {
otherTable = await config.api.table.save(defaultTable())
@ -3504,7 +3510,7 @@ describe.each([
name: generator.word(),
description: generator.paragraph(),
})
await config.api.row.save(table._id!, {
mainRow = await config.api.row.save(table._id!, {
name: generator.word(),
description: generator.paragraph(),
tableId: table._id!,
@ -3512,6 +3518,25 @@ describe.each([
})
})
async function updateFormulaColumn(
formula: string,
opts?: { responseType?: FormulaResponseType; formulaType?: FormulaType }
) {
table = await config.api.table.save({
...table,
schema: {
...table.schema,
formula: {
name: "formula",
type: FieldType.FORMULA,
formula: formula,
responseType: opts?.responseType,
formulaType: opts?.formulaType || FormulaType.DYNAMIC,
},
},
})
}
it("should be able to search for rows containing formulas", async () => {
const { rows } = await config.api.row.search(table._id!)
expect(rows.length).toBe(1)
@ -3519,12 +3544,63 @@ describe.each([
const row = rows[0]
expect(row.formula).toBe(relatedRow.name)
})
it("should coerce - number response type", async () => {
await updateFormulaColumn(encodeJS("return 1"), {
responseType: FieldType.NUMBER,
})
const { rows } = await config.api.row.search(table._id!)
expect(rows[0].formula).toBe(1)
})
it("should coerce - boolean response type", async () => {
await updateFormulaColumn(encodeJS("return true"), {
responseType: FieldType.BOOLEAN,
})
const { rows } = await config.api.row.search(table._id!)
expect(rows[0].formula).toBe(true)
})
it("should coerce - datetime response type", async () => {
await updateFormulaColumn(encodeJS("return new Date()"), {
responseType: FieldType.DATETIME,
})
const { rows } = await config.api.row.search(table._id!)
expect(isDate(rows[0].formula)).toBe(true)
})
it("should coerce - datetime with invalid value", async () => {
await updateFormulaColumn(encodeJS("return 'a'"), {
responseType: FieldType.DATETIME,
})
const { rows } = await config.api.row.search(table._id!)
expect(rows[0].formula).toBeUndefined()
})
it("should coerce handlebars", async () => {
await updateFormulaColumn("{{ add 1 1 }}", {
responseType: FieldType.NUMBER,
})
const { rows } = await config.api.row.search(table._id!)
expect(rows[0].formula).toBe(2)
})
it("should coerce a static handlebars formula", async () => {
await updateFormulaColumn(encodeJS("return 1"), {
responseType: FieldType.NUMBER,
formulaType: FormulaType.STATIC,
})
// save the row to store the static value
await config.api.row.save(table._id!, mainRow)
const { rows } = await config.api.row.search(table._id!)
expect(rows[0].formula).toBe(1)
})
})
describe("Formula JS protection", () => {
it("should time out JS execution if a single cell takes too long", async () => {
await withEnv({ JS_PER_INVOCATION_TIMEOUT_MS: 40 }, async () => {
const js = Buffer.from(
const js = encodeJS(
`
let i = 0;
while (true) {
@ -3532,7 +3608,7 @@ describe.each([
}
return i;
`
).toString("base64")
)
const table = await config.api.table.save(
saveTableRequest({
@ -3544,7 +3620,7 @@ describe.each([
formula: {
name: "formula",
type: FieldType.FORMULA,
formula: `{{ js "${js}"}}`,
formula: js,
formulaType: FormulaType.DYNAMIC,
},
},
@ -3567,7 +3643,7 @@ describe.each([
JS_PER_REQUEST_TIMEOUT_MS: 80,
},
async () => {
const js = Buffer.from(
const js = encodeJS(
`
let i = 0;
while (true) {
@ -3575,7 +3651,7 @@ describe.each([
}
return i;
`
).toString("base64")
)
const table = await config.api.table.save(
saveTableRequest({
@ -3587,7 +3663,7 @@ describe.each([
formula: {
name: "formula",
type: FieldType.FORMULA,
formula: `{{ js "${js}"}}`,
formula: js,
formulaType: FormulaType.DYNAMIC,
},
},
@ -3629,7 +3705,7 @@ describe.each([
})
it("should not carry over context between formulas", async () => {
const js = Buffer.from(`return $("[text]");`).toString("base64")
const js = encodeJS(`return $("[text]");`)
const table = await config.api.table.save(
saveTableRequest({
schema: {
@ -3640,7 +3716,7 @@ describe.each([
formula: {
name: "formula",
type: FieldType.FORMULA,
formula: `{{ js "${js}"}}`,
formula: js,
formulaType: FormulaType.DYNAMIC,
},
},