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 SafeTracer from '../utils/SafeTracer.js';
11import { type IDataWarehouse } from '../storage/dataWarehouse/IDataWarehouse.js';
12import type { IDataWarehouseAnalytics } from '../storage/dataWarehouse/IDataWarehouseAnalytics.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 better-mutation/no-mutation */
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 better-mutation/no-mutation */
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 /* eslint-disable better-mutation/no-mutation */
44 const tracer = new SafeTracer(new otel.ProxyTracerProvider().getTracer('noop'));
45
46 const queryMock = jest.fn(
47 async (
48 _query: string,
49 _tracer: SafeTracer,
50 _binds?: readonly unknown[],
51 ) => [] as unknown[],
52 ) as jest.MockedFunction<IDataWarehouse['query']>;
53
54 const transactionImpl: IDataWarehouse['transaction'] = async (fn) =>
55 fn(async (sql, binds) =>
56 queryMock(sql, tracer, binds as readonly unknown[] | undefined),
57 );
58
59 const startMock = jest.fn(() => {}) as IDataWarehouse['start'];
60 const closeMock = jest.fn(async () => {}) as IDataWarehouse['close'];
61 const getProviderMock = jest.fn(() => 'clickhouse') as IDataWarehouse['getProvider'];
62
63 const dataWarehouseMock: IDataWarehouse = {
64 query: queryMock,
65 transaction: transactionImpl,
66 start: startMock,
67 close: closeMock,
68 getProvider: getProviderMock,
69 };
70
71 const analyticsMock = {
72 bulkWrite: jest.fn(async () => {}),
73 createCDCStream: jest.fn(async () => {}),
74 consumeCDCChanges: jest.fn(async () => {}),
75 supportsCDC: jest.fn(() => false),
76 flushPendingWrites: jest.fn(async () => {}),
77 close: jest.fn(async () => {}),
78 } as unknown as jest.Mocked<IDataWarehouseAnalytics>;
79 /* eslint-enable better-mutation/no-mutation */
80
81 bottle.value('DataWarehouse', dataWarehouseMock);
82 bottle.value(
83 'DataWarehouseAnalytics',
84 analyticsMock,
85 );
86 bottle.value('Tracer', tracer);
87 return bottle.container as unknown as Omit<
88 Dependencies,
89 'DataWarehouse' | 'DataWarehouseAnalytics'
90 > & {
91 DataWarehouse: typeof dataWarehouseMock;
92 DataWarehouseAnalytics: typeof analyticsMock;
93 };
94}