UNPKG

@budibase/server

Version:
236 lines (193 loc) • 6.87 kB
import { Header } from "@budibase/backend-core" import * as setup from "../../api/routes/tests/utilities" import { setEnv } from "../../environment" import { WorkspaceMigration, checkMissingMigrations, getLatestEnabledMigrationId, } from "../index" import * as migrations from "../migrations" import { getWorkspaceMigrationVerions, updateWorkspaceMigrationMetadata, } from "../workspaceMigrationMetadata" jest.mock<typeof migrations>("../migrations", () => ({ MIGRATIONS: [ { id: "20231211101320_test", func: migrationLogic(), }, ], })) function migrationLogic(executionMS = 0): () => Promise<void> { return () => new Promise(r => setTimeout(r, executionMS)) } describe("migrations", () => { beforeAll(() => setEnv({ DISABLE_WORKSPACE_MIGRATIONS: false })) it("new workspaces are created with the latest workspace migration version set", async () => { const config = setup.getConfig() await config.init() await config.doInContext(config.getDevWorkspaceId(), async () => { const migrationVersion = await getWorkspaceMigrationVerions( config.getDevWorkspaceId() ) expect(migrationVersion).toEqual("20231211101320_test") }) }) it("accessing a workspace that has no pending migrations will not attach the migrating header", async () => { const config = setup.getConfig() await config.init() const appId = config.getDevWorkspaceId() await config.api.workspace.get(appId, { headersNotPresent: [Header.MIGRATING_APP], }) }) it("accessing a workspace that has pending migrations will attach the migrating header", async () => { const config = setup.getConfig() await config.init() const appId = config.getDevWorkspaceId() migrations.MIGRATIONS.push({ id: "20231211105812_new-test", func: migrationLogic(5000), }) await config.api.workspace.get(appId, { headers: { [Header.MIGRATING_APP]: appId, }, }) }) it("should disable all migrations after one that is disabled", () => { const MIGRATION_ID1 = "20231211105810_new-test", MIGRATION_ID2 = "20231211105812_new-test", MIGRATION_ID3 = "20231211105814_new-test" // create some migrations to test with const migrations: WorkspaceMigration[] = [ { id: MIGRATION_ID1, func: migrationLogic(), }, { id: MIGRATION_ID2, func: migrationLogic(), }, { id: MIGRATION_ID3, func: migrationLogic(), }, ] expect(getLatestEnabledMigrationId(migrations)).toBe(MIGRATION_ID3) migrations[1].disabled = true expect(getLatestEnabledMigrationId(migrations)).toBe(MIGRATION_ID1) }) describe("index-based migration comparison", () => { beforeEach(() => { // Reset migrations array to known state migrations.MIGRATIONS.length = 0 migrations.MIGRATIONS.push( { id: "20231211101320_test", func: migrationLogic(1000) }, { id: "20231211101330_test2", func: migrationLogic(1000) }, { id: "20231211101340_test3", func: migrationLogic(1000) } ) }) it("should trigger migration when there are future migrations based on the current version", async () => { const config = setup.getConfig() await config.init() const workspaceId = config.getDevWorkspaceId() // Set app to first migration await config.doInContext(workspaceId, async () => { await updateWorkspaceMigrationMetadata({ workspaceId, version: "20231211101320_test", }) }) const mockNext = jest.fn() const ctx = { response: { set: jest.fn() } } as any await config.doInContext(workspaceId, () => checkMissingMigrations(ctx, mockNext, workspaceId) ) expect(ctx.response.set).toHaveBeenCalledWith( Header.MIGRATING_APP, workspaceId ) expect(mockNext).toHaveBeenCalled() }) it("should not trigger migration when current version is latest", async () => { const config = setup.getConfig() await config.init() const workspaceId = config.getDevWorkspaceId() // Set app to latest migration await config.doInContext(workspaceId, async () => { await updateWorkspaceMigrationMetadata({ workspaceId, version: "20231211101340_test3", }) }) const mockNext = jest.fn() const ctx = { response: { set: jest.fn() } } as any await config.doInContext(workspaceId, () => checkMissingMigrations(ctx, mockNext, workspaceId) ) expect(ctx.response.set).not.toHaveBeenCalled() expect(mockNext).toHaveBeenCalled() }) it("should trigger migration when current version not found in array", async () => { const config = setup.getConfig() await config.init() const workspaceId = config.getDevWorkspaceId() // Set app to non-existent migration await config.doInContext(workspaceId, async () => { await updateWorkspaceMigrationMetadata({ workspaceId, version: "nonexistent_migration", }) }) const mockNext = jest.fn() const ctx = { response: { set: jest.fn() } } as any await config.doInContext(workspaceId, () => checkMissingMigrations(ctx, mockNext, workspaceId) ) expect(ctx.response.set).toHaveBeenCalledWith( Header.MIGRATING_APP, workspaceId ) expect(mockNext).toHaveBeenCalled() }) }) it("should handle rapid migration completion", async () => { migrations.MIGRATIONS.length = 0 migrations.MIGRATIONS.push({ id: "20250626103320_test", func: migrationLogic(), }) const config = setup.getConfig() await config.init() const appId = config.getDevWorkspaceId() const mockNext = jest.fn() const ctx = { response: { set: jest.fn() } } as any await config.doInContext(appId, () => checkMissingMigrations(ctx, mockNext, appId) ) expect(ctx.response.set).not.toHaveBeenCalled() expect(mockNext).toHaveBeenCalled() }) describe("workspace existence check", () => { beforeEach(() => { migrations.MIGRATIONS.length = 0 migrations.MIGRATIONS.push({ id: "20231211101320_test", func: migrationLogic(), }) }) it("should skip migrations when workspace does not exist", async () => { const config = setup.getConfig() await config.init() // Use a non-existent app ID const nonExistentAppId = "app_nonexistent123456" const mockNext = jest.fn() const ctx = { response: { set: jest.fn() } } as any await checkMissingMigrations(ctx, mockNext, nonExistentAppId) expect(ctx.response.set).not.toHaveBeenCalled() expect(mockNext).toHaveBeenCalled() }) }) })