1import { retryContainerPublication } from "@/Cloudflare/Containers/ContainerPublication.ts";
2import {
3 DockerRegistryBlobUnknown,
4 DockerRegistryUnavailable,
5} from "@/Docker/RegistryError.ts";
6import { describe, expect, it } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import * as Fiber from "effect/Fiber";
9import { PlatformError, SystemError } from "effect/PlatformError";
10import * as TestClock from "effect/testing/TestClock";
11
12const cause = new PlatformError(
13 new SystemError({
14 _tag: "Unknown",
15 module: "Docker",
16 method: "buildx.build",
17 description: "publication failed",
18 }),
19);
20
21describe(
22 "container publication retries",
23 {
24 tags: [
25 "unit",
26 "provider:cloudflare",
27 "provider:cloudflare:container",
28 "local",
29 ],
30 },
31 () => {
32 for (const error of [
33 new DockerRegistryBlobUnknown({ cause }),
34 new DockerRegistryUnavailable({ cause }),
35 ]) {
36 it.effect(
37 `recovers from ${error._tag}`,
38 () =>
39 Effect.gen(function* () {
40 let attempts = 0;
41 const fiber = yield* Effect.suspend(() =>
42 ++attempts < 3 ? Effect.fail(error) : Effect.succeed("published"),
43 ).pipe(retryContainerPublication, Effect.forkChild);
44 yield* TestClock.adjust("15 seconds");
45 expect(yield* Fiber.join(fiber)).toBe("published");
46 expect(attempts).toBe(3);
47 }),
48 { tags: ["provider:docker", "provider:docker:registry"] },
49 );
50
51 it.effect(
52 `bounds retries for ${error._tag}`,
53 () =>
54 Effect.gen(function* () {
55 let attempts = 0;
56 const fiber = yield* Effect.suspend(() => {
57 attempts++;
58 return Effect.fail(error);
59 }).pipe(retryContainerPublication, Effect.flip, Effect.forkChild);
60 yield* TestClock.adjust("1 minute");
61 expect(yield* Fiber.join(fiber)).toBe(error);
62 expect(attempts).toBe(6);
63 }),
64 { tags: ["provider:docker", "provider:docker:registry"] },
65 );
66 }
67
68 it.effect("propagates other Docker errors without retrying", () =>
69 Effect.gen(function* () {
70 let attempts = 0;
71 const error = yield* Effect.suspend(() => {
72 attempts++;
73 return Effect.fail(cause);
74 }).pipe(retryContainerPublication, Effect.flip);
75 expect(error).toBe(cause);
76 expect(attempts).toBe(1);
77 }),
78 );
79 },
80);