diff --git a/.github/workflows/ts_test.yml b/.github/workflows/ts_test.yml index c15b009ff1..bb306b2815 100644 --- a/.github/workflows/ts_test.yml +++ b/.github/workflows/ts_test.yml @@ -2,7 +2,7 @@ name: TS tests on: push: - branches: ['main'] + branches: ["main"] pull_request: permissions: @@ -47,7 +47,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 20 - cache: 'pnpm' + cache: "pnpm" - run: pnpm install --frozen-lockfile - run: pnpm -r --filter "$(jq '.name' -r package.json)^..." build - run: pnpm run stylecheck @@ -69,7 +69,7 @@ jobs: - uses: erlef/setup-beam@v1 with: version-type: strict - version-file: '.tool-versions' + version-file: ".tool-versions" - uses: pnpm/action-setup@v4 with: version: 9 @@ -105,7 +105,7 @@ jobs: mix run --no-halt & wait-on: | - http://localhost:3000 + http-get://localhost:3000/v1/health tail: true log-output-resume: stderr diff --git a/integration-tests/tests/_macros.luxinc b/integration-tests/tests/_macros.luxinc index 659e90b937..f1f76304fc 100644 --- a/integration-tests/tests/_macros.luxinc +++ b/integration-tests/tests/_macros.luxinc @@ -31,11 +31,17 @@ [shell pg_lifecycle] # This timeout is needed until https://github.com/electric-sql/electric/issues/1632 is fixed. !docker stop -t 1 $pg_container_name + ?$PS1 + + [shell pg] + ??database system is shut down + [sleep 1] [endmacro] [macro resume_pg] [shell pg_lifecycle] !docker start $pg_container_name + ?$PS1 [shell pg] !docker attach $pg_container_name diff --git a/packages/react-hooks/test/react-hooks.test.tsx b/packages/react-hooks/test/react-hooks.test.tsx index 9e1d7ef6b7..84df2860b6 100644 --- a/packages/react-hooks/test/react-hooks.test.tsx +++ b/packages/react-hooks/test/react-hooks.test.tsx @@ -119,11 +119,13 @@ describe(`useShape`, () => { // Add an item. const [id2] = await insertIssues({ title: `other row` }) - await waitFor(() => - expect(result.current.data).toEqual([ - { id: id, title: `test row` }, - { id: id2, title: `other row` }, - ]) + await waitFor( + () => + expect(result.current.data).toEqual([ + { id: id, title: `test row` }, + { id: id2, title: `other row` }, + ]), + { timeout: 4000 } ) }) diff --git a/packages/react-hooks/test/support/global-setup.ts b/packages/react-hooks/test/support/global-setup.ts index 6d6077a37f..a5aba0c2ce 100644 --- a/packages/react-hooks/test/support/global-setup.ts +++ b/packages/react-hooks/test/support/global-setup.ts @@ -1,5 +1,4 @@ import type { GlobalSetupContext } from 'vitest/node' -import { FetchError } from '@electric-sql/client' import { makePgClient } from './test-helpers' const url = process.env.ELECTRIC_URL ?? `http://localhost:3000` @@ -22,13 +21,37 @@ declare module 'vitest' { } } +function waitForElectric(url: string): Promise { + return new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(`Timed out waiting for Electric to be active`), + 10000 + ) + + const tryHealth = async () => + fetch(`${url}/v1/health`) + .then(async (res): Promise => { + if (!res.ok) return tryHealth() + const { status } = (await res.json()) as { status: string } + if (status !== `active`) return tryHealth() + clearTimeout(timeout) + resolve() + }) + .catch((err) => { + clearTimeout(timeout) + reject(err) + }) + + return tryHealth() + }) +} + /** * Global setup for the test suite. Validates that our server is running, and creates and tears down a * special schema in Postgres to ensure clean slate between runs. */ export default async function ({ provide }: GlobalSetupContext) { - const response = await fetch(url) - if (!response.ok) throw FetchError.fromResponse(response, url) + await waitForElectric(url) const client = makePgClient() await client.connect() diff --git a/packages/react-hooks/test/support/test-context.ts b/packages/react-hooks/test/support/test-context.ts index dce531fb01..a3f3c44d2c 100644 --- a/packages/react-hooks/test/support/test-context.ts +++ b/packages/react-hooks/test/support/test-context.ts @@ -66,7 +66,7 @@ export const testWithIssuesTable = testWithDbClient.extend<{ clearIssuesShape: ClearIssuesShapeFn }>({ issuesTableSql: async ({ dbClient, task }, use) => { - const tableName = `"issues for ${task.id}"` + const tableName = `"issues for ${task.id}_${Math.random().toString(16)}"` await dbClient.query(` DROP TABLE IF EXISTS ${tableName}; CREATE TABLE ${tableName} ( diff --git a/packages/typescript-client/test/integration.test.ts b/packages/typescript-client/test/integration.test.ts index 962103ca9e..441097d9b1 100644 --- a/packages/typescript-client/test/integration.test.ts +++ b/packages/typescript-client/test/integration.test.ts @@ -92,13 +92,16 @@ describe(`HTTP Sync`, () => { }, 1000) }) - // first request was -1, second should be something else - expect(urlsRequested).toHaveLength(3) + // first request was -1, last requests should be live ones + const numRequests = urlsRequested.length + expect(numRequests).toBeGreaterThan(2) expect(urlsRequested[0].searchParams.get(`offset`)).toBe(`-1`) expect(urlsRequested[0].searchParams.has(`live`)).false - expect(urlsRequested[2].searchParams.get(`offset`)).not.toBe(`-1`) - expect(urlsRequested[2].searchParams.has(`live`)).true - expect(urlsRequested[2].searchParams.has(`cursor`)).true + expect(urlsRequested[numRequests - 1].searchParams.get(`offset`)).not.toBe( + `-1` + ) + expect(urlsRequested[numRequests - 1].searchParams.has(`live`)).true + expect(urlsRequested[numRequests - 1].searchParams.has(`cursor`)).true // first request comes back immediately and is up to date, second one // should hang while waiting for updates diff --git a/packages/typescript-client/test/support/global-setup.ts b/packages/typescript-client/test/support/global-setup.ts index e73615e6d5..039b4e0ce0 100644 --- a/packages/typescript-client/test/support/global-setup.ts +++ b/packages/typescript-client/test/support/global-setup.ts @@ -1,5 +1,4 @@ import type { GlobalSetupContext } from 'vitest/node' -import { FetchError } from '../../src/error' import { makePgClient } from './test-helpers' const url = process.env.ELECTRIC_URL ?? `http://localhost:3000` @@ -22,13 +21,37 @@ declare module 'vitest' { } } +function waitForElectric(url: string): Promise { + return new Promise((resolve, reject) => { + const timeout = setTimeout( + () => reject(`Timed out waiting for Electric to be active`), + 10000 + ) + + const tryHealth = async () => + fetch(`${url}/v1/health`) + .then(async (res): Promise => { + if (!res.ok) return tryHealth() + const { status } = (await res.json()) as { status: string } + if (status !== `active`) return tryHealth() + clearTimeout(timeout) + resolve() + }) + .catch((err) => { + clearTimeout(timeout) + reject(err) + }) + + return tryHealth() + }) +} + /** * Global setup for the test suite. Validates that our server is running, and creates and tears down a * special schema in Postgres to ensure clean slate between runs. */ export default async function ({ provide }: GlobalSetupContext) { - const response = await fetch(url) - if (!response.ok) throw FetchError.fromResponse(response, url) + await waitForElectric(url) const client = makePgClient() await client.connect() diff --git a/packages/typescript-client/test/support/test-context.ts b/packages/typescript-client/test/support/test-context.ts index 6a593b0c55..3651211354 100644 --- a/packages/typescript-client/test/support/test-context.ts +++ b/packages/typescript-client/test/support/test-context.ts @@ -66,7 +66,7 @@ export const testWithIssuesTable = testWithDbClient.extend<{ clearIssuesShape: ClearIssuesShapeFn }>({ issuesTableSql: async ({ dbClient, task }, use) => { - const tableName = `"issues for ${task.id}"` + const tableName = `"issues for ${task.id}_${Math.random().toString(16)}"` await dbClient.query(` DROP TABLE IF EXISTS ${tableName}; CREATE TABLE ${tableName} ( @@ -124,7 +124,7 @@ export const testWithMultitypeTable = testWithDbClient.extend<{ tableUrl: string }>({ tableSql: async ({ dbClient, task }, use) => { - const tableName = `"multitype table for ${task.id}"` + const tableName = `"multitype table for ${task.id}_${Math.random().toString(16)}"` await dbClient.query(` DROP TABLE IF EXISTS ${tableName};