Skip to content

Commit a55c49e

Browse files
committed
add setup logs
1 parent c2a5ea3 commit a55c49e

File tree

1 file changed

+114
-23
lines changed
  • internal-packages/testcontainers/src

1 file changed

+114
-23
lines changed

internal-packages/testcontainers/src/index.ts

Lines changed: 114 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { StartedRedisContainer } from "@testcontainers/redis";
33
import { PrismaClient } from "@trigger.dev/database";
44
import { RedisOptions } from "ioredis";
55
import { Network, type StartedNetwork } from "testcontainers";
6-
import { test } from "vitest";
6+
import { TaskContext, test } from "vitest";
77
import { createElectricContainer, createPostgresContainer, createRedisContainer } from "./utils";
88
import { x } from "tinyexec";
9-
import { isCI } from "std-env";
9+
import { isCI, env } from "std-env";
1010

1111
export { assertNonNullable } from "./utils";
1212
export { StartedRedisContainer };
@@ -41,7 +41,11 @@ let activeCleanups = 0;
4141
* @param resource - The resource that is being cleaned up.
4242
* @param promise - The cleanup promise to await..
4343
*/
44-
async function logCleanup(resource: string, promise: Promise<unknown>) {
44+
async function logCleanup(
45+
resource: string,
46+
promise: Promise<unknown>,
47+
metadata: Record<string, unknown> = {}
48+
) {
4549
const start = new Date();
4650
const order = cleanupOrder++;
4751
const activeAtStart = ++activeCleanups;
@@ -66,7 +70,7 @@ async function logCleanup(resource: string, promise: Promise<unknown>) {
6670
let dockerDiagnostics: DockerDiagnostics = {};
6771

6872
// Only run docker diagnostics if there was an error or cleanup took longer than 5s
69-
if (error || durationMs > 5000) {
73+
if (error || durationMs > 5000 || env.DOCKER_DIAGNOSTICS) {
7074
try {
7175
dockerDiagnostics = await getDockerDiagnostics();
7276
} catch (diagnosticErr) {
@@ -85,6 +89,7 @@ async function logCleanup(resource: string, promise: Promise<unknown>) {
8589
error,
8690
activeAtStart,
8791
activeAtEnd,
92+
...metadata,
8893
...dockerDiagnostics,
8994
})
9095
);
@@ -121,7 +126,7 @@ async function getDockerContainers(): Promise<string[]> {
121126
type DockerResource = { id: string; name: string };
122127

123128
type DockerNetworkAttachment = DockerResource & {
124-
containers: DockerResource[];
129+
containers: string[];
125130
};
126131

127132
export async function getDockerNetworkAttachments(): Promise<DockerNetworkAttachment[]> {
@@ -153,15 +158,11 @@ export async function getDockerNetworkAttachments(): Promise<DockerNetworkAttach
153158
"network",
154159
"inspect",
155160
"--format",
156-
"{{range $k, $v := .Containers}}{{$k}} {{$v.Name}}\n{{end}}",
161+
'{{range $k, $v := .Containers}}{{$k | printf "%.12s"}} {{$v.Name}}\n{{end}}',
157162
id,
158163
]);
159-
const lines = stringToLines(containersResult.stdout);
160164

161-
const containers: DockerResource[] = lines.map((line) => {
162-
const [id, name] = lineToWords(line);
163-
return { id, name };
164-
});
165+
const containers = stringToLines(containersResult.stdout);
165166

166167
attachments.push({ id, name, containers });
167168
} catch (err) {
@@ -204,11 +205,11 @@ export async function getDockerContainerNetworks(): Promise<DockerContainerNetwo
204205
const inspectResult = await x("docker", [
205206
"inspect",
206207
"--format",
207-
"{{ range $k, $v := .NetworkSettings.Networks }}{{ $k }}{{ end }}",
208+
'{{ range $k, $v := .NetworkSettings.Networks }}{{ $k | printf "%.12s" }} {{ $v.Name }}\n{{ end }}',
208209
id,
209210
]);
210211

211-
const networks = inspectResult.stdout.trim().split(/\s+/);
212+
const networks = stringToLines(inspectResult.stdout);
212213

213214
results.push({ id, name, networks });
214215
} catch (err) {
@@ -243,34 +244,68 @@ async function getDockerDiagnostics(): Promise<DockerDiagnostics> {
243244
};
244245
}
245246

246-
const network = async ({}, use: Use<StartedNetwork>) => {
247+
const network = async ({ task }: TaskContext, use: Use<StartedNetwork>) => {
248+
const testName = task.name;
249+
250+
logSetup("network: starting", { testName });
251+
252+
const start = Date.now();
247253
const network = await new Network().start();
254+
const startDurationMs = Date.now() - start;
255+
256+
const metadata = {
257+
testName,
258+
networkId: network.getId().slice(0, 12),
259+
networkName: network.getName(),
260+
startDurationMs,
261+
};
262+
263+
logSetup("network: started", metadata);
264+
248265
try {
249266
await use(network);
250267
} finally {
251268
// Make sure to stop the network after use
252-
await logCleanup("network", network.stop());
269+
await logCleanup("network", network.stop(), metadata);
253270
}
254271
};
255272

256273
const postgresContainer = async (
257-
{ network }: { network: StartedNetwork },
274+
{ network, task }: { network: StartedNetwork } & TaskContext,
258275
use: Use<StartedPostgreSqlContainer>
259276
) => {
277+
const testName = task.name;
278+
279+
logSetup("postgresContainer: starting", { testName });
280+
281+
const start = Date.now();
260282
const { container } = await createPostgresContainer(network);
283+
const startDurationMs = Date.now() - start;
284+
285+
const metadata = {
286+
testName,
287+
containerId: container.getId().slice(0, 12),
288+
containerName: container.getName(),
289+
containerNetworkNames: container.getNetworkNames(),
290+
startDurationMs,
291+
};
292+
293+
logSetup("postgresContainer: started", metadata);
294+
261295
try {
262296
await use(container);
263297
} finally {
264298
// WARNING: Testcontainers by default will not wait until the container has stopped. It will simply issue the stop command and return immediately.
265299
// If you need to wait for the container to be stopped, you can provide a timeout. The unit of timeout option here is second
266-
await logCleanup("postgresContainer", container.stop({ timeout: 30 }));
300+
await logCleanup("postgresContainer", container.stop({ timeout: 30 }), metadata);
267301
}
268302
};
269303

270304
const prisma = async (
271-
{ postgresContainer }: { postgresContainer: StartedPostgreSqlContainer },
305+
{ postgresContainer, task }: { postgresContainer: StartedPostgreSqlContainer } & TaskContext,
272306
use: Use<PrismaClient>
273307
) => {
308+
const testName = task.name;
274309
const url = postgresContainer.getConnectionUri();
275310

276311
console.log("Initializing Prisma with URL:", url);
@@ -285,26 +320,65 @@ const prisma = async (
285320
try {
286321
await use(prisma);
287322
} finally {
288-
await logCleanup("prisma", prisma.$disconnect());
323+
await logCleanup("prisma", prisma.$disconnect(), { testName });
289324
}
290325
};
291326

292327
export const postgresTest = test.extend<PostgresContext>({ network, postgresContainer, prisma });
293328

329+
let setupOrder = 0;
330+
331+
function logSetup(resource: string, metadata: Record<string, unknown>) {
332+
const order = setupOrder++;
333+
334+
if (!isCI) {
335+
return;
336+
}
337+
338+
console.log(
339+
JSON.stringify({
340+
type: "setup",
341+
order,
342+
resource,
343+
timestamp: new Date().toISOString(),
344+
...metadata,
345+
})
346+
);
347+
}
348+
294349
const redisContainer = async (
295-
{ network }: { network: StartedNetwork },
350+
{ network, task }: { network: StartedNetwork } & TaskContext,
296351
use: Use<StartedRedisContainer>
297352
) => {
353+
const testName = task.name;
354+
355+
logSetup("redisContainer: starting", { testName });
356+
357+
const start = Date.now();
358+
298359
const { container } = await createRedisContainer({
299360
port: 6379,
300361
network,
301362
});
363+
364+
const startDurationMs = Date.now() - start;
365+
366+
const metadata = {
367+
containerName: container.getName(),
368+
containerId: container.getId().slice(0, 12),
369+
containerNetworkNames: container.getNetworkNames(),
370+
startDurationMs,
371+
testName,
372+
};
373+
374+
logSetup("redisContainer: started", metadata);
375+
302376
try {
303377
await use(container);
304378
} finally {
305379
// WARNING: Testcontainers by default will not wait until the container has stopped. It will simply issue the stop command and return immediately.
306380
// If you need to wait for the container to be stopped, you can provide a timeout. The unit of timeout option here is second
307-
await logCleanup("redisContainer", container.stop({ timeout: 30 }));
381+
await logCleanup("redisContainer", container.stop({ timeout: 30 }), metadata);
308382
}
309383
};
310384

@@ -347,16 +421,33 @@ const electricOrigin = async (
347421
{
348422
postgresContainer,
349423
network,
350-
}: { postgresContainer: StartedPostgreSqlContainer; network: StartedNetwork },
424+
task,
425+
}: { postgresContainer: StartedPostgreSqlContainer; network: StartedNetwork } & TaskContext,
351426
use: Use<string>
352427
) => {
428+
const testName = task.name;
429+
430+
logSetup("electricOrigin: starting", { testName });
431+
432+
const start = Date.now();
353433
const { origin, container } = await createElectricContainer(postgresContainer, network);
434+
const startDurationMs = Date.now() - start;
435+
436+
const metadata = {
437+
testName,
438+
containerId: container.getId().slice(0, 12),
439+
containerName: container.getName(),
440+
startDurationMs,
441+
};
442+
443+
logSetup("electricOrigin: started", metadata);
444+
354445
try {
355446
await use(origin);
356447
} finally {
357448
// WARNING: Testcontainers by default will not wait until the container has stopped. It will simply issue the stop command and return immediately.
358449
// If you need to wait for the container to be stopped, you can provide a timeout. The unit of timeout option here is second
359-
await logCleanup("electricContainer", container.stop({ timeout: 30 }));
450+
await logCleanup("electricContainer", container.stop({ timeout: 30 }), metadata);
360451
}
361452
};
362453

0 commit comments

Comments
 (0)