Mirror of https://github.com/roostorg/coop
github.com/roostorg/coop
1// NB: This file can only be imported from within a jest test (as the jest
2// runtime actually makes the global jest variable available, which we're
3// relying on here).
4
5import otel from '@opentelemetry/api';
6import * as superTest from 'supertest';
7
8import getBottle, { type Dependencies } from '../iocContainer/index.js';
9import makeServer from '../server.js';
10import { type IDataWarehouse } from '../storage/dataWarehouse/IDataWarehouse.js';
11import type { IDataWarehouseAnalytics } from '../storage/dataWarehouse/IDataWarehouseAnalytics.js';
12import SafeTracer from '../utils/SafeTracer.js';
13
14/**
15 * Occassionally, we make a request that's supposed to error, so this function
16 * lets us temporarily suppress console messages, to keep our output a bit nicer.
17 */
18export function disableConsoleLogging() {
19 /* eslint-disable functional/immutable-data */
20 const noop = () => {};
21 const { log, error } = console;
22 console.log = noop;
23 console.error = noop;
24 return () => {
25 console.log = log;
26 console.error = error;
27 };
28 /* eslint-enable functional/immutable-data */
29}
30
31export async function makeMockedServer() {
32 const deps = await getBottleContainerWithIOMocks();
33 const { app: server, shutdown } = await makeServer(deps);
34 const request = superTest.agent(server);
35 return { deps, server, shutdown, request };
36}
37
38export async function getBottleContainerWithIOMocks() {
39 const bottle = await getBottle();
40
41 // The mutation rule below is a false positive, as we're just doing
42 // initial setup on this mock object before exposing it.
43
44 const tracer = new SafeTracer(
45 new otel.ProxyTracerProvider().getTracer('noop'),
46 );
47
48 const queryMock = jest.fn(
49 async (_query: string, _tracer: SafeTracer, _binds?: readonly unknown[]) =>
50 [] as unknown[],
51 ) as jest.MockedFunction<IDataWarehouse['query']>;
52
53 const transactionImpl: IDataWarehouse['transaction'] = async (fn) =>
54 fn(async (sql, binds) =>
55 queryMock(sql, tracer, binds as readonly unknown[] | undefined),
56 );
57
58 const startMock = jest.fn(() => {}) as IDataWarehouse['start'];
59 const closeMock = jest.fn(async () => {}) as IDataWarehouse['close'];
60 const getProviderMock = jest.fn(
61 () => 'clickhouse',
62 ) as IDataWarehouse['getProvider'];
63
64 const dataWarehouseMock: IDataWarehouse = {
65 query: queryMock,
66 transaction: transactionImpl,
67 start: startMock,
68 close: closeMock,
69 getProvider: getProviderMock,
70 };
71
72 const analyticsMock = {
73 bulkWrite: jest.fn(async () => {}),
74 createCDCStream: jest.fn(async () => {}),
75 consumeCDCChanges: jest.fn(async () => {}),
76 supportsCDC: jest.fn(() => false),
77 flushPendingWrites: jest.fn(async () => {}),
78 close: jest.fn(async () => {}),
79 } as unknown as jest.Mocked<IDataWarehouseAnalytics>;
80
81 bottle.value('DataWarehouse', dataWarehouseMock);
82 bottle.value('DataWarehouseAnalytics', analyticsMock);
83 bottle.value('Tracer', tracer);
84 return bottle.container as unknown as Omit<
85 Dependencies,
86 'DataWarehouse' | 'DataWarehouseAnalytics'
87 > & {
88 DataWarehouse: typeof dataWarehouseMock;
89 DataWarehouseAnalytics: typeof analyticsMock;
90 };
91}