From 79bec7d86d3945771d787975c3d10226d6afb53a Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 31 Mar 2023 10:32:13 +0100 Subject: [PATCH] Add force exit, -e flag and streaming to tests (#10151) * Add force exit, -e flag and streaming to tests * Temp remove script usage from worker tests * Revert "Temp remove script usage from worker tests" This reverts commit 5e0ed2e92d01bbc6d1434f1b7daa88a15f9dba3b. * Always fail test * Ignore server test package from coverage report * Unscope server only * Run all server tests again * Update backend core test script to fail when any command fails and force exit * Don't try to require client in test * Port memory limit fix from develop * Remove always failing test and commented out block for client require * Fixing some issues with test cases. * Revert assertions in internalSearch.spec.js --------- Co-authored-by: mike12345567 --- package.json | 2 +- packages/backend-core/scripts/test.sh | 9 +- packages/backend-core/tests/jestEnv.ts | 1 + packages/server/__mocks__/node-fetch.ts | 7 +- packages/server/jest.config.ts | 1 + packages/server/scripts/test.sh | 10 +- .../server/src/api/controllers/row/utils.ts | 7 +- .../src/api/routes/tests/appSync.spec.ts | 31 ++++++ .../api/routes/tests/internalSearch.spec.js | 3 +- .../src/integration-test/postgres.spec.ts | 104 +++++++++--------- .../integrations/tests/googlesheets.spec.ts | 22 ++-- packages/server/src/tests/jestEnv.ts | 1 + .../src/tests/utilities/TestConfiguration.ts | 11 ++ .../src/utilities/fileSystem/filesystem.ts | 4 - packages/shared-core/yarn.lock | 5 - packages/worker/scripts/test.sh | 9 +- packages/worker/src/tests/jestEnv.ts | 1 + 17 files changed, 133 insertions(+), 95 deletions(-) create mode 100644 packages/server/src/api/routes/tests/appSync.spec.ts diff --git a/package.json b/package.json index 815e470916..1ae713f96c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "dev": "yarn run kill-all && lerna link && lerna run --parallel dev:builder --concurrency 1", "dev:noserver": "yarn run kill-builder && lerna link && lerna run dev:stack:up && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", "dev:server": "yarn run kill-server && lerna run --parallel dev:builder --concurrency 1 --scope @budibase/backend-core --scope @budibase/worker --scope @budibase/server", - "test": "lerna run test", + "test": "lerna run test --stream", "test:pro": "bash scripts/pro/test.sh", "lint:eslint": "eslint packages && eslint qa-core", "lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --check \"qa-core/**/*.{js,ts,svelte}\"", diff --git a/packages/backend-core/scripts/test.sh b/packages/backend-core/scripts/test.sh index 4bf1900984..3d8240e65a 100644 --- a/packages/backend-core/scripts/test.sh +++ b/packages/backend-core/scripts/test.sh @@ -1,12 +1,13 @@ #!/bin/bash +set -e if [[ -n $CI ]] then # --runInBand performs better in ci where resources are limited - echo "jest --coverage --runInBand" - jest --coverage --runInBand + echo "jest --coverage --runInBand --forceExit" + jest --coverage --runInBand --forceExit else # --maxWorkers performs better in development - echo "jest --coverage" - jest --coverage + echo "jest --coverage --forceExit" + jest --coverage --forceExit fi \ No newline at end of file diff --git a/packages/backend-core/tests/jestEnv.ts b/packages/backend-core/tests/jestEnv.ts index ec8de2942e..3555973928 100644 --- a/packages/backend-core/tests/jestEnv.ts +++ b/packages/backend-core/tests/jestEnv.ts @@ -4,3 +4,4 @@ process.env.NODE_ENV = "jest" process.env.MOCK_REDIS = "1" process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error" process.env.ENABLE_4XX_HTTP_LOGGING = "0" +process.env.REDIS_PASSWORD = "budibase" diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts index fdf44d173d..acf0294e76 100644 --- a/packages/server/__mocks__/node-fetch.ts +++ b/packages/server/__mocks__/node-fetch.ts @@ -4,6 +4,7 @@ module FetchMock { // @ts-ignore const fetch = jest.requireActual("node-fetch") let failCount = 0 + let mockSearch = false const func = async (url: any, opts: any) => { function json(body: any, status = 200) { @@ -69,7 +70,7 @@ module FetchMock { }, 404 ) - } else if (url.includes("_search")) { + } else if (mockSearch && url.includes("_search")) { const body = opts.body const parts = body.split("tableId:") let tableId @@ -192,5 +193,9 @@ module FetchMock { func.Headers = fetch.Headers + func.mockSearch = () => { + mockSearch = true + } + module.exports = func } diff --git a/packages/server/jest.config.ts b/packages/server/jest.config.ts index 03eca3705b..7df9190af8 100644 --- a/packages/server/jest.config.ts +++ b/packages/server/jest.config.ts @@ -43,6 +43,7 @@ const config: Config.InitialOptions = { "../backend-core/src/**/*.{js,ts}", // The use of coverage with couchdb view functions breaks tests "!src/db/views/staticViews.*", + "!src/tests/**/*.{js,ts}", ], coverageReporters: ["lcov", "json", "clover"], } diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 2f3f54cb25..f3f679c4da 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -1,12 +1,14 @@ #!/bin/bash +set -e if [[ -n $CI ]] then # --runInBand performs better in ci where resources are limited - echo "jest --coverage --runInBand" - jest --coverage --runInBand + export NODE_OPTIONS="--max-old-space-size=4096" + echo "jest --coverage --runInBand --forceExit" + jest --coverage --runInBand --forceExit else # --maxWorkers performs better in development - echo "jest --coverage --maxWorkers=2" - jest --coverage --maxWorkers=2 + echo "jest --coverage --maxWorkers=2 --forceExit" + jest --coverage --maxWorkers=2 --forceExit fi \ No newline at end of file diff --git a/packages/server/src/api/controllers/row/utils.ts b/packages/server/src/api/controllers/row/utils.ts index 2e8f2f4536..2cf3b5472f 100644 --- a/packages/server/src/api/controllers/row/utils.ts +++ b/packages/server/src/api/controllers/row/utils.ts @@ -62,10 +62,11 @@ export async function validate({ } const errors: any = {} for (let fieldName of Object.keys(fetchedTable.schema)) { - const constraints = cloneDeep(fetchedTable.schema[fieldName].constraints) - const type = fetchedTable.schema[fieldName].type + const column = fetchedTable.schema[fieldName] + const constraints = cloneDeep(column.constraints) + const type = column.type // formulas shouldn't validated, data will be deleted anyway - if (type === FieldTypes.FORMULA) { + if (type === FieldTypes.FORMULA || column.autocolumn) { continue } // special case for options, need to always allow unselected (null) diff --git a/packages/server/src/api/routes/tests/appSync.spec.ts b/packages/server/src/api/routes/tests/appSync.spec.ts new file mode 100644 index 0000000000..f82f62405e --- /dev/null +++ b/packages/server/src/api/routes/tests/appSync.spec.ts @@ -0,0 +1,31 @@ +import * as setup from "./utilities" +import { roles, db as dbCore } from "@budibase/backend-core" + +describe("/api/applications/:appId/sync", () => { + let request = setup.getRequest() + let config = setup.getConfig() + let app + + afterAll(setup.afterAll) + + beforeAll(async () => { + app = await config.init() + // create some users which we will use throughout the tests + await config.createUser({ + email: "sync1@test.com", + roles: { + [app._id!]: roles.BUILTIN_ROLE_IDS.BASIC, + }, + }) + }) + + async function getUserMetadata() { + const { rows } = await config.searchRows(dbCore.InternalTable.USER_METADATA) + return rows + } + + it("make sure its empty initially", async () => { + const rows = await getUserMetadata() + expect(rows.length).toBe(1) + }) +}) diff --git a/packages/server/src/api/routes/tests/internalSearch.spec.js b/packages/server/src/api/routes/tests/internalSearch.spec.js index 8b821ce741..daef03c451 100644 --- a/packages/server/src/api/routes/tests/internalSearch.spec.js +++ b/packages/server/src/api/routes/tests/internalSearch.spec.js @@ -1,6 +1,7 @@ +const fetch = require("node-fetch") +fetch.mockSearch() const search = require("../../controllers/row/internalSearch") // this will be mocked out for _search endpoint -const fetch = require("node-fetch") const PARAMS = { tableId: "ta_12345679abcdef", version: "1", diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 2afff8b786..fab1fcf618 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -1,3 +1,6 @@ +import fetch from "node-fetch" +// @ts-ignore +fetch.mockSearch() import { generateMakeRequest, MakeRequestResponse, @@ -16,6 +19,7 @@ import _ from "lodash" import { generator } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" import { GenericContainer } from "testcontainers" +import { generateRowIdField } from "../integrations/utils" const config = setup.getConfig()! @@ -80,16 +84,10 @@ describe("row api - postgres", () => { name: "id", type: FieldType.AUTO, autocolumn: true, - constraints: { - presence: true, - }, }, title: { name: "title", type: FieldType.STRING, - constraints: { - presence: true, - }, }, }, sourceId: postgresDatasource._id, @@ -121,16 +119,10 @@ describe("row api - postgres", () => { name: "id", type: FieldType.AUTO, autocolumn: true, - constraints: { - presence: true, - }, }, name: { name: "name", type: FieldType.STRING, - constraints: { - presence: true, - }, }, description: { name: "description", @@ -144,7 +136,6 @@ describe("row api - postgres", () => { type: FieldType.LINK, constraints: { type: "array", - presence: false, }, fieldName: oneToManyRelationshipInfo.fieldName, name: "oneToManyRelation", @@ -156,7 +147,6 @@ describe("row api - postgres", () => { type: FieldType.LINK, constraints: { type: "array", - presence: false, }, fieldName: manyToOneRelationshipInfo.fieldName, name: "manyToOneRelation", @@ -168,7 +158,6 @@ describe("row api - postgres", () => { type: FieldType.LINK, constraints: { type: "array", - presence: false, }, fieldName: manyToManyRelationshipInfo.fieldName, name: "manyToManyRelation", @@ -309,9 +298,6 @@ describe("row api - postgres", () => { id: { name: "id", type: FieldType.AUTO, - constraints: { - presence: true, - }, }, }, sourceId: postgresDatasource._id, @@ -921,47 +907,55 @@ describe("row api - postgres", () => { foreignRows, x => x.relationshipType ) - expect(res.body).toEqual({ - ...rowData, - [`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]: - foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row.id, - [oneToManyRelationshipInfo.fieldName]: [ + const m2mFieldName = manyToManyRelationshipInfo.fieldName, + o2mFieldName = oneToManyRelationshipInfo.fieldName, + m2oFieldName = manyToOneRelationshipInfo.fieldName + const m2mRow1 = res.body[m2mFieldName].find( + (row: Row) => row.id === 1 + ) + const m2mRow2 = res.body[m2mFieldName].find( + (row: Row) => row.id === 2 + ) + expect(m2mRow1).toEqual({ + ...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row, + [m2mFieldName]: [ { - ...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row, - _id: expect.any(String), - _rev: expect.any(String), + _id: row._id, }, ], - [manyToOneRelationshipInfo.fieldName]: [ - { - ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row, - [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: - row.id, - }, - { - ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row, - [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: - row.id, - }, - { - ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row, - [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: - row.id, - }, - ], - [manyToManyRelationshipInfo.fieldName]: [ - { - ...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row, - }, - { - ...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][1].row, - }, - ], - id: row.id, - tableId: row.tableId, - _id: expect.any(String), - _rev: expect.any(String), }) + expect(m2mRow2).toEqual({ + ...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][1].row, + [m2mFieldName]: [ + { + _id: row._id, + }, + ], + }) + expect(res.body[m2oFieldName]).toEqual([ + { + ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row, + [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: + row.id, + }, + { + ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row, + [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: + row.id, + }, + { + ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row, + [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: + row.id, + }, + ]) + expect(res.body[o2mFieldName]).toEqual([ + { + ...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row, + _id: expect.any(String), + _rev: expect.any(String), + }, + ]) }) }) }) diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index 5d7c184abd..eb263bd850 100644 --- a/packages/server/src/integrations/tests/googlesheets.spec.ts +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -99,8 +99,8 @@ describe("Google Sheets Integration", () => { }) }) - test("removing an existing field will not remove the data from the spreadsheet", async () => { - await config.doInContext(structures.uuid(), async () => { + test("removing an existing field will remove the header from the google sheet", async () => { + const sheet = await config.doInContext(structures.uuid(), async () => { const tableColumns = ["name"] const table = createBasicTable(structures.uuid(), tableColumns) @@ -109,18 +109,14 @@ describe("Google Sheets Integration", () => { }) sheetsByTitle[table.name] = sheet await integration.updateTable(table) - - expect(sheet.loadHeaderRow).toBeCalledTimes(1) - expect(sheet.setHeaderRow).toBeCalledTimes(1) - expect(sheet.setHeaderRow).toBeCalledWith([ - "name", - "description", - "location", - ]) - - // No undefineds are sent - expect((sheet.setHeaderRow as any).mock.calls[0][0]).toHaveLength(3) + return sheet }) + expect(sheet.loadHeaderRow).toBeCalledTimes(1) + expect(sheet.setHeaderRow).toBeCalledTimes(1) + expect(sheet.setHeaderRow).toBeCalledWith(["name"]) + + // No undefined are sent + expect((sheet.setHeaderRow as any).mock.calls[0][0]).toHaveLength(1) }) }) }) diff --git a/packages/server/src/tests/jestEnv.ts b/packages/server/src/tests/jestEnv.ts index 7727bb6007..1f76bccd5f 100644 --- a/packages/server/src/tests/jestEnv.ts +++ b/packages/server/src/tests/jestEnv.ts @@ -9,3 +9,4 @@ process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error" process.env.ENABLE_4XX_HTTP_LOGGING = "0" process.env.MOCK_REDIS = "1" process.env.PLATFORM_URL = "http://localhost:10000" +process.env.REDIS_PASSWORD = "budibase" diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index cf0585efd1..80f804d219 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -46,6 +46,7 @@ import { Row, SourceName, Table, + SearchFilters, } from "@budibase/types" type DefaultUserValues = { @@ -568,6 +569,16 @@ class TestConfiguration { return this._req(null, { tableId }, controllers.row.fetch) } + async searchRows(tableId: string, searchParams: SearchFilters = {}) { + if (!tableId && this.table) { + tableId = this.table._id + } + const body = { + query: searchParams, + } + return this._req(body, { tableId }, controllers.row.search) + } + // ROLE async createRole(config?: any) { diff --git a/packages/server/src/utilities/fileSystem/filesystem.ts b/packages/server/src/utilities/fileSystem/filesystem.ts index 086eaa835a..6d3c6d48f6 100644 --- a/packages/server/src/utilities/fileSystem/filesystem.ts +++ b/packages/server/src/utilities/fileSystem/filesystem.ts @@ -24,10 +24,6 @@ export const init = () => { } } } - const clientLibPath = join(budibaseTempDir(), "budibase-client.js") - if (env.isTest() && !fs.existsSync(clientLibPath)) { - fs.copyFileSync(require.resolve("@budibase/client"), clientLibPath) - } } /** diff --git a/packages/shared-core/yarn.lock b/packages/shared-core/yarn.lock index c1578954fc..95d82a9a94 100644 --- a/packages/shared-core/yarn.lock +++ b/packages/shared-core/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@budibase/types@2.4.5-alpha.0": - version "2.4.5-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.5-alpha.0.tgz#70fea09b5e471fe8fa6a760a1a2dd0dd74caac3a" - integrity sha512-tVFM9XnKwcCOo7nw6v7C8ZsK9hQLQBv3kHDn7/MFWnDMFCj72pUdtP/iFrAKr2c3tE84lkkWJfNHIolMSktHZA== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" diff --git a/packages/worker/scripts/test.sh b/packages/worker/scripts/test.sh index 2f3f54cb25..d7659a9318 100644 --- a/packages/worker/scripts/test.sh +++ b/packages/worker/scripts/test.sh @@ -1,12 +1,13 @@ #!/bin/bash +set -e if [[ -n $CI ]] then # --runInBand performs better in ci where resources are limited - echo "jest --coverage --runInBand" - jest --coverage --runInBand + echo "jest --coverage --runInBand --forceExit" + jest --coverage --runInBand --forceExit else # --maxWorkers performs better in development - echo "jest --coverage --maxWorkers=2" - jest --coverage --maxWorkers=2 + echo "jest --coverage --maxWorkers=2 --forceExit" + jest --coverage --maxWorkers=2 --forceExit fi \ No newline at end of file diff --git a/packages/worker/src/tests/jestEnv.ts b/packages/worker/src/tests/jestEnv.ts index 061897451e..5784e44dcf 100644 --- a/packages/worker/src/tests/jestEnv.ts +++ b/packages/worker/src/tests/jestEnv.ts @@ -10,3 +10,4 @@ process.env.MINIO_SECRET_KEY = "test" process.env.PLATFORM_URL = "http://localhost:10000" process.env.INTERNAL_API_KEY = "tet" process.env.DISABLE_ACCOUNT_PORTAL = "0" +process.env.REDIS_PASSWORD = "budibase"