Skip to main content
Glama
migrate-utils.test.ts•7.65 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { loadTestConfig } from '../config/loader'; import { closeDatabase, DatabaseMode, getDatabasePool, initDatabase } from '../database'; import { indexDefinitionsEqual } from './migrate'; import { doubleEscapeSingleQuotes, escapeMixedCaseIdentifier, escapeUnicode, getColumns, getFunctionDefinition, parseIndexColumns, parseIndexDefinition, splitIndexColumnNames, tsVectorExpression, } from './migrate-utils'; import type { IndexDefinition } from './types'; describe('migration-utils', () => { beforeAll(async () => { const config = await loadTestConfig(); config.database.runMigrations = false; config.database.disableRunPostDeployMigrations = true; await initDatabase(config); }); afterAll(async () => { await closeDatabase(); }); test('escapeMixedCaseIdentifier', () => { expect(escapeMixedCaseIdentifier('system')).toEqual('system'); expect(escapeMixedCaseIdentifier('System')).toEqual('"System"'); expect(escapeMixedCaseIdentifier('system__display')).toEqual('system__display'); expect(escapeMixedCaseIdentifier('system__Display')).toEqual('"system__Display"'); }); test('doubleEscapeSingleQuotes', () => { expect(doubleEscapeSingleQuotes("to_tsvector('simple'::regconfig, value)")).toEqual( "to_tsvector(\\'simple\\'::regconfig, value)" ); }); test('tsVectorExpression', () => { expect(tsVectorExpression('simple', 'all_lower')).toEqual("to_tsvector('simple'::regconfig, all_lower)"); expect(tsVectorExpression('simple', 'someUpper')).toEqual('to_tsvector(\'simple\'::regconfig, "someUpper")'); }); test('splitIndexColumnNames', () => { expect(splitIndexColumnNames('col1__col2___col3')).toEqual(['col1', '_col2', '__col3']); }); test.each([ ["system, to_tsvector('english'::regconfig, display)", ['system', "to_tsvector('english'::regconfig, display)"]], ])('parseIndexColumns %s', (input, expected) => { expect(parseIndexColumns(input)).toEqual(expected); }); test('escapeUnicode', () => { expect(escapeUnicode('lighthouse')).toEqual('lighthouse'); expect(escapeUnicode('\t')).toEqual('\t'); expect(escapeUnicode('apple\tbanana\tcherry')).toEqual('apple\tbanana\tcherry'); expect(escapeUnicode('\x01')).toEqual('\\x01'); expect(escapeUnicode('\x7F')).toEqual('\\x7f'); expect(escapeUnicode('🦄')).toEqual('\\ud83e\\udd84'); expect(escapeUnicode('\u{1F984}')).toEqual('\\ud83e\\udd84'); expect(escapeUnicode('\ud83e\udd84')).toEqual('\\ud83e\\udd84'); }); test('getColumns', async () => { const client = await getDatabasePool(DatabaseMode.WRITER); const result = await getColumns(client, 'DatabaseMigration'); expect(result).toEqual([ { defaultValue: null, name: 'id', notNull: true, primaryKey: true, type: 'INTEGER', }, { defaultValue: null, name: 'version', notNull: true, primaryKey: false, type: 'INTEGER', }, { defaultValue: null, name: 'dataVersion', notNull: true, primaryKey: false, type: 'INTEGER', }, { defaultValue: 'false', name: 'firstBoot', notNull: true, primaryKey: false, type: 'BOOLEAN', }, ]); }); describe('getFunctionDefinition', () => { test('getFunctionDefinition', async () => { const client = await getDatabasePool(DatabaseMode.WRITER); const result = await getFunctionDefinition(client, 'token_array_to_text'); expect(result).toEqual({ name: 'token_array_to_text', createQuery: expect.stringContaining('CREATE OR REPLACE FUNCTION public.token_array_to_text'), }); }); test('getFunctionDefinition', async () => { const client = await getDatabasePool(DatabaseMode.WRITER); const result = await getFunctionDefinition(client, 'non_existent_function'); expect(result).toBeUndefined(); }); }); describe('parseIndexDefinition', () => { test('parse index', () => { const indexdef = 'CREATE INDEX "DomainConfiguration_compartments_idx" ON public."DomainConfiguration" USING gin (compartments)'; const def = parseIndexDefinition(indexdef); const expected: IndexDefinition = { columns: ['compartments'], indexType: 'gin', unique: false, indexdef, }; expect(def).toStrictEqual(expected); expect(indexDefinitionsEqual(def, expected)).toBeTruthy(); }); test('parse UNIQUE index', () => { const indexdef = 'CREATE UNIQUE INDEX "Coding_pkey" ON public."Coding" USING btree (id)'; const def = parseIndexDefinition(indexdef); const expected: IndexDefinition = { columns: ['id'], indexType: 'btree', unique: true, indexdef, }; expect(def).toStrictEqual(expected); expect(indexDefinitionsEqual(def, expected)).toBeTruthy(); }); test('parse expressions', () => { const indexdef = 'CREATE INDEX "Address_address_idx_tsv" ON public."Address" USING gin (to_tsvector(\'simple\'::regconfig, address))'; const def = parseIndexDefinition(indexdef); const expected: IndexDefinition = { columns: [{ expression: "to_tsvector('simple'::regconfig, address)", name: 'address' }], indexType: 'gin', unique: false, indexdef, }; expect(def).toStrictEqual(expected); expect(indexDefinitionsEqual(def, expected)).toBeTruthy(); }); test('parse INCLUDE', () => { const indexdef = 'CREATE INDEX "Patient_Token_code_system_value_idx" ON public."Patient_Token" USING btree (code, system, value) INCLUDE ("resourceId")'; const def = parseIndexDefinition(indexdef); const expected: IndexDefinition = { indexType: 'btree', columns: ['code', 'system', 'value'], include: ['resourceId'], unique: false, indexdef, }; expect(def).toStrictEqual(expected); expect(indexDefinitionsEqual(def, expected)).toBeTruthy(); }); test('parse WHERE', () => { const indexdef = 'CREATE INDEX "Coding_Property_target_property_coding_idx" ON public."Coding_Property" USING btree (target, property, coding) WHERE (target IS NOT NULL)'; const def = parseIndexDefinition(indexdef); const expected: IndexDefinition = { columns: ['target', 'property', 'coding'], indexType: 'btree', where: 'target IS NOT NULL', unique: false, indexdef, }; expect(def).toStrictEqual(expected); expect(indexDefinitionsEqual(def, expected)).toBeTruthy(); }); test('missing index type and columns', () => { const indexdef = 'CREATE INDEX "DomainConfiguration_compartments_idx" ON "DomainConfiguration"'; expect(() => parseIndexDefinition(indexdef)).toThrow('Could not parse index type and expressions from'); }); test('missing index name', () => { const indexdef = 'CREATE INDEX ON "DomainConfiguration" USING gin ((some_column > 5))'; expect(() => parseIndexDefinition(indexdef)).toThrow('Could not parse index name from'); }); test('parse unsupported index type', () => { const indexdef = 'CREATE INDEX "DomainConfiguration_compartments_idx" ON "DomainConfiguration" USING bad_type (compartments)'; expect(() => parseIndexDefinition(indexdef)).toThrow('Invalid index type: bad_type'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server