UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

310 lines (260 loc) 7.32 kB
process.env.MONGOMS_DOWNLOAD_URL = process.env.MONGOMS_DOWNLOAD_URL || 'https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian12-8.0.3.tgz'; process.env.OTEL_PROMETHEUS_EXPORTER_PREVENT_SERVER_START = 'true'; import type { DatastoreConfig, ModelConfig, Services } from './typings'; import type { Express } from 'express'; import type { Server } from 'http'; import { MongoDbConnector } from '@getanthill/mongodb-connector'; /* @ts-ignore */ import jestMongodb from '@shelf/jest-mongodb/jest-preset'; import { merge } from 'lodash'; import util from 'util'; import net from 'net'; import config from './config'; import { init, Models } from './models'; import App from './App'; import { build } from './services'; import Datastore from './sdk/Datastore'; // Set the default ACCESS_TOKENS for the tests: process.env.ADMIN_ACCESS_TOKENS = 'token'; export default async function setup() { console.log('Global Setup'); process.env.SKIP_MONGODB_IN_MEMORY !== 'true' && (await setupMongodbInMemory()); } async function setupMongodbInMemory(attempt = 0): Promise<void> { try { const { default: setupMongoDb } = await import(jestMongodb.globalSetup); await setupMongoDb({ rootDir: '.', }); } catch (err) { if (attempt < 10) { console.warn(err); console.log('Retrying the MongoDB setup'); await new Promise((resolve) => setTimeout(resolve, 1000)); setupMongodbInMemory(attempt + 1); return; } console.error(err); process.exit(1); } } async function teardownMongoDbInMemory() { try { const { default: teardownMongoDb } = await import( jestMongodb.globalTeardown ); process.env.SKIP_MONGODB_IN_MEMORY !== 'true' && (await teardownMongoDb({ rootDir: '.', })); } catch (err) { console.warn(err); } } setup.setupMongodbInMemory = setupMongodbInMemory; setup.teardownMongoDbInMemory = teardownMongoDbInMemory; setup.uuid = function uuid() { return 'uuid' + (Math.random() * 1e16).toFixed(0); }; const DEFAULT_SERVICES_CONFIG = config; async function getPort(): Promise<number> { return new Promise((resolve) => { const srv = net.createServer(); srv.listen(0, () => { // @ts-ignore const port = srv.address()?.port as number; srv.close(() => { resolve(port); }); }); }); } setup.build = async (config: Partial<DatastoreConfig> = {}): Promise<App> => { const dbName = `datastore_test_${new Date().getTime()}`; const _config = merge( {}, DEFAULT_SERVICES_CONFIG, { mode: 'development', port: await getPort(), features: { initInternalModels: false, }, mongodb: { databases: [ { ...DEFAULT_SERVICES_CONFIG.mongodb.databases[0], url: ( process.env.MONGO_URL || DEFAULT_SERVICES_CONFIG.mongodb.databases[0].url ) .replace('/?', '/' + dbName + '?') .replace('/datastore', `/${dbName}`), }, { ...DEFAULT_SERVICES_CONFIG.mongodb.databases[1], url: ( process.env.MONGO_URL || DEFAULT_SERVICES_CONFIG.mongodb.databases[1].url ) .replace('/?', '/' + dbName + '?') .replace('/datastore', `/${dbName}`), }, ], ensureIndexInBackground: true, }, }, config, ); const services = build(_config); if (typeof jest !== 'undefined') { /* @ts-ignore */ global.debugMock = jest .spyOn(services.telemetry.logger, 'debug') /* @ts-ignore */ .mockImplementation(() => null); /* @ts-ignore */ global.infoMock = jest .spyOn(services.telemetry.logger, 'info') /* @ts-ignore */ .mockImplementation(() => null); /* @ts-ignore */ global.warnMock = jest .spyOn(services.telemetry.logger, 'warn') /* @ts-ignore */ .mockImplementation(() => null); /* @ts-ignore */ global.errorMock = jest .spyOn(services.telemetry.logger, 'error') /* @ts-ignore */ .mockImplementation(() => null); } await services.mongodb.connect(); await services.mongodb.db('datastore_write').dropDatabase(); await services.fhe.connect(); return new App(services); }; setup.cleanModel = async function cleanModel( services: Services, modelName: string, ) { // Remove internal models await Promise.all([ services.mongodb.db('datastore_write').collection(modelName).deleteMany({}), services.mongodb .db('datastore_write') .collection(`${modelName}_events`) .deleteMany({}), services.mongodb .db('datastore_write') .collection(`${modelName}_snapshots`) .deleteMany({}), ]); }; setup.cleanModels = async function cleanModels(services: Services) { await setup.cleanModel(services, 'internal_models'); }; setup.initModels = async function initModels( services: Services, modelConfigs: ModelConfig[] = [], ) { const models = init( { models: [], }, services, ); await setup.cleanModels(services); await Promise.all( modelConfigs.map((modelConfig) => models.createModel({ is_enabled: true, ...modelConfig, }), ), ); await models.reload(); await Promise.all( modelConfigs.map((modelConfig) => models.createModelIndexes(modelConfig)), ); models.services = services; services.models = models; return models; }; setup.teardownDb = async function teardownDb(mongodb: MongoDbConnector) { try { if (!mongodb.db('datastore_write')) { return; } await mongodb.db('datastore_write').dropDatabase(); await mongodb.disconnect(); } catch (err) { console.error(err); } }; setup.startApi = async function startApi( _config: Partial<DatastoreConfig> = {}, modelConfigs: ModelConfig[] = [], ): Promise< [ DatastoreConfig, MongoDbConnector, Models, Express | null, Server | null, Datastore, Services, App, ] > { const app = await setup.build(_config); app.services.models = await setup.initModels(app.services, modelConfigs); try { await app.services.models.initInternalModels(); } catch { await app.services.models.initInternalModels(); } await app.start(); const sdk = new Datastore({ baseUrl: `http://localhost:${app.services.config.port}`, // Set to true to inspect SDK responses: debug: process.env.DATASTORE_DEBUG === 'true', token: 'token', timeout: 300000, telemetry: app.services.telemetry, }); let res = { state: 'down' }; do { const { data } = await sdk.heartbeat(); res = data; } while ( res.state !== 'up' && (await new Promise((resolve) => setTimeout(resolve, 10))) ); return [ app.services.config, app.services.mongodb, app.services.models, app.express, app.server, sdk, app.services, app, ]; }; setup.stopApi = async function stopApi(app: App) { await app.stop(); }; setup.restartApi = async function restartApi( app: App, _config: Partial<DatastoreConfig> = {}, ) { await setup.stopApi(app); return setup.startApi(_config); }; setup.inspect = (obj: any) => { console.log(util.inspect(obj, false, null, true)); };