Skip to content

Commit 571de52

Browse files
committed
Create unique branch for each app e2e test run
1 parent f8ce27c commit 571de52

File tree

4 files changed

+92
-10
lines changed

4 files changed

+92
-10
lines changed

src/common/and-call.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { FnLike } from '@seedcompany/common';
2+
import { isPromise } from 'node:util/types';
3+
import { ConditionalKeys } from 'type-fest';
4+
5+
export const andCall = <
6+
T,
7+
K extends ConditionalKeys<T, FnLike>,
8+
X extends T[K] & FnLike,
9+
>(
10+
thing: T,
11+
methodName: K,
12+
add: X,
13+
) => {
14+
const orig = (thing[methodName] as FnLike).bind(thing);
15+
(thing[methodName] as FnLike) = () => {
16+
const res = orig();
17+
return isPromise(res) ? res.then((x) => add(x)) : add(res);
18+
};
19+
};

src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { Many, many, maybeMany, JsonSet, ArrayItem } from '@seedcompany/common';
22

3+
export * from './and-call';
34
export * from './temporal';
45
export * from './calculated.decorator';
56
export * from './context.type';

test/utility/create-app.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { faker } from '@faker-js/faker';
22
import { INestApplication } from '@nestjs/common';
33
import { Test } from '@nestjs/testing';
4+
import { andCall } from '~/common';
45
import { AppModule } from '../../src/app.module';
56
import { LogLevel } from '../../src/core/logger';
67
import { LevelMatcher } from '../../src/core/logger/level-matcher';
78
import {
89
createGraphqlClient,
910
GraphQLTestClient,
1011
} from './create-graphql-client';
12+
import { ephemeralEdgeDB } from './edgedb-setup';
1113

1214
// Patch faker email to be more unique
1315
const origEmail = faker.internet.email.bind(faker.internet);
@@ -19,16 +21,29 @@ export interface TestApp extends INestApplication {
1921
}
2022

2123
export const createTestApp = async () => {
22-
const moduleFixture = await Test.createTestingModule({
23-
imports: [AppModule],
24-
})
25-
.overrideProvider(LevelMatcher)
26-
.useValue(new LevelMatcher([], LogLevel.ERROR))
27-
.compile();
24+
const db = await ephemeralEdgeDB();
2825

29-
const app = moduleFixture.createNestApplication<TestApp>();
30-
await app.init();
31-
app.graphql = await createGraphqlClient(app);
26+
try {
27+
const moduleFixture = await Test.createTestingModule({
28+
imports: [AppModule],
29+
})
30+
.overrideProvider(LevelMatcher)
31+
.useValue(new LevelMatcher([], LogLevel.ERROR))
32+
.overrideProvider('EDGEDB_CONNECT')
33+
.useValue(db?.options)
34+
.compile();
3235

33-
return app;
36+
const app = moduleFixture.createNestApplication<TestApp>();
37+
await app.init();
38+
app.graphql = await createGraphqlClient(app);
39+
40+
andCall(app, 'close', async () => {
41+
await db?.cleanup();
42+
});
43+
44+
return app;
45+
} catch (e) {
46+
await db?.cleanup();
47+
throw e;
48+
}
3449
};

test/utility/edgedb-setup.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Client, ConnectOptions, createClient } from 'edgedb';
2+
import { DateTime } from 'luxon';
3+
4+
export const ephemeralEdgeDB = async () => {
5+
if (process.env.DATABASE !== 'edgedb') {
6+
return undefined;
7+
}
8+
9+
const db = createClient();
10+
11+
await dropStale(db);
12+
13+
const branch = `test_${Date.now()}`;
14+
15+
await db.execute(`create schema branch ${branch} from main`);
16+
17+
const cleanup = async () => {
18+
await db.execute(`drop branch ${branch}`);
19+
await db.close();
20+
};
21+
22+
const options: ConnectOptions = { branch };
23+
24+
return { options, cleanup };
25+
};
26+
27+
async function dropStale(db: Client) {
28+
const branches = await db.query<string>('select sys::Database.name');
29+
30+
const stale = branches.flatMap((name) => {
31+
if (!name.startsWith('test_')) {
32+
return [];
33+
}
34+
const ts = Number(name.slice(5));
35+
if (isNaN(ts)) {
36+
return [];
37+
}
38+
const createdAt = DateTime.fromMillis(ts);
39+
return createdAt.diffNow().as('hours') > 4 ? name : [];
40+
});
41+
if (stale.length === 0) {
42+
return;
43+
}
44+
await db.execute('drop branch array_unpack(<array<string>>$branches)', {
45+
branches: stale,
46+
});
47+
}

0 commit comments

Comments
 (0)