History

Make the test suite parallel-safe and speed up CI (#1273)

* Make self-host tests CI-safe * Await cloud db teardown * Give local e2e boots more headroom * Keep main CI runs alive * Cache CI dependencies and image layers * Wait for recorded introspection requests in graphql plugin tests * Size self-host test budget for loaded runners * Probe test http servers for readiness before use * Die on recorder wait timeout instead of typing a string error * Type the recorder wait timeout as a tagged error * Update e2e locators for the integration copy renames * Keep stale data rendered while a refresh is waiting * Guard create-org input against hydration clobbering * Match the new sign-in heading in the selfhost callback test

Rhys Sullivan committed 4 months ago

Commit 8652c991 parent 9c7e4a627 files · +252 −102Browse files

Showing the changes to packages/plugins (2 of 27 files). Show all

Unified · Split

2 files
  1. packages/plugins/graphql/src/sdk/plugin.test.ts +6 −5
  2. packages/plugins/graphql/src/testing/index.ts +23 −0
packages/plugins/graphql/src/sdk/plugin.test.ts+6−5
@@ -32,6 +32,7 @@ import { · 31 unmodified lines
32 makeGreetingGraphqlSchema,32 makeGreetingGraphqlSchema,
33 serveGraphqlFailureTestServer,33 serveGraphqlFailureTestServer,
34 serveGraphqlTestServer,34 serveGraphqlTestServer,
35 waitForRecordedRequests,
35} from "../testing";36} from "../testing";
36 37
37// removed: v1 secret browser-handoff, credential-binding scopes, usagesForSecret/38// removed: v1 secret browser-handoff, credential-binding scopes, usagesForSecret/
@@ -313,9 +314,8 @@ describe("graphqlPlugin real protocol server", () => { · 275 unmodified lines
313 value: "unused",314 value: "unused",
314 });315 });
315 316
316 const afterConnect = yield* server.requests;317 yield* waitForRecordedRequests(server.requests, (requests) =>
317 expect(afterConnect.some((request) => request.payload.query?.includes("__schema"))).toBe(318 requests.some((request) => request.payload.query?.includes("__schema")),
318 true,
319 );319 );
320 320
321 const tools = yield* executor.tools.list();321 const tools = yield* executor.tools.list();
@@ -550,11 +550,12 @@ describe("graphqlPlugin real protocol server", () => { · 228 unmodified lines
550 value: "connect-token",550 value: "connect-token",
551 });551 });
552 552
553 const afterConnect = yield* server.requests;553 const afterConnect = yield* waitForRecordedRequests(server.requests, (requests) =>
554 requests.some((request) => request.payload.query?.includes("__schema")),
555 );
554 const introspectionRequests = afterConnect.filter((request) =>556 const introspectionRequests = afterConnect.filter((request) =>
555 request.payload.query?.includes("__schema"),557 request.payload.query?.includes("__schema"),
556 );558 );
557 expect(introspectionRequests.length).toBeGreaterThan(0);
558 expect(introspectionRequests[0]?.headers.authorization).toBe("Bearer connect-token");559 expect(introspectionRequests[0]?.headers.authorization).toBe("Bearer connect-token");
559 560
560 // The introspected operations become per-connection tools.561 // The introspected operations become per-connection tools.
packages/plugins/graphql/src/testing/index.ts+23−0
@@ -5,6 +5,7 @@ import { · 4 unmodified lines
5 Layer,5 Layer,
6 Predicate,6 Predicate,
7 Ref,7 Ref,
8 Schedule,
8 Schema as EffectSchema,9 Schema as EffectSchema,
9 Scope,10 Scope,
10} from "effect";11} from "effect";
@@ -54,6 +55,28 @@ export interface GraphqlTestServerShape { · 43 unmodified lines
54 readonly clearRequests: Effect.Effect<void>;55 readonly clearRequests: Effect.Effect<void>;
55}56}
56 57
58export class GraphqlTestRecorderTimeoutError extends Data.TaggedError(
59 "GraphqlTestRecorderTimeoutError",
60)<{
61 readonly message: string;
62}> {}
63
64/** Poll the request recorder until `predicate` matches. Yoga captures requests
65 * asynchronously (`captureRequest` runs via `Effect.runPromise` in the handler
66 * context), so connect/invoke can resolve before the ref updates under load. */
67export const waitForRecordedRequests = (
68 requests: Effect.Effect<readonly GraphqlTestRequest[]>,
69 predicate: (requests: readonly GraphqlTestRequest[]) => boolean,
70 message = "timed out waiting for matching recorded GraphQL requests",
71): Effect.Effect<readonly GraphqlTestRequest[], GraphqlTestRecorderTimeoutError> =>
72 requests.pipe(
73 Effect.filterOrFail(
74 (all) => predicate(all),
75 () => new GraphqlTestRecorderTimeoutError({ message }),
76 ),
77 Effect.retry(Schedule.both(Schedule.spaced("50 millis"), Schedule.recurs(100))),
78 );
79
57class GraphqlTestServerAddressError extends Data.TaggedError("GraphqlTestServerAddressError")<{80class GraphqlTestServerAddressError extends Data.TaggedError("GraphqlTestServerAddressError")<{
58 readonly address: unknown;81 readonly address: unknown;
59}> {}82}> {}