From c2c5f1728c25ce79456b4423a0e53afb41a1bafa Mon Sep 17 00:00:00 2001 From: alexion Date: Tue, 14 Jul 2026 10:09:51 -0400 Subject: [PATCH] test: poll for indexer consistency in search e2e (task 0016) Gitea's issue/PR search endpoint is backed by an asynchronous, eventually- consistent indexer (bleve by default), so a PR opened moments earlier in the e2e `beforeAll` was not yet searchable when `search prs` ran, and the live assertion saw zero matches. Both live-search tests now poll the search via `expect.poll` until the freshly-created content is indexed before asserting on the exact locator-schema output. Record the eventual-consistency behaviour as a project gotcha. --- CLAUDE.md | 4 ++++ test/e2e/search.test.ts | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 7a779eb..7ee913c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,3 +16,7 @@ Fall back to `tea pr create --login alexion --base main --head ` only fo Task branches are merged into `main` on the remote, so the local `main` goes stale. Always `git fetch origin` and cut a task branch from `origin/main`, not from whatever local `main` happens to point at. + +Gitea's issue/PR search endpoint (`GET /repos/issues/search`, behind `search issues`/`search prs`) is backed by an **asynchronous, eventually-consistent issue indexer** (bleve by default). +Content created moments earlier may not be searchable yet, so end-to-end assertions that create an issue/PR and then search for it must poll (e.g. `expect.poll`) until it is indexed rather than searching once. +The fixture tier is unaffected — it stubs the endpoint — so this bites only the live `test/e2e` tier. diff --git a/test/e2e/search.test.ts b/test/e2e/search.test.ts index 438d9c3..929a426 100644 --- a/test/e2e/search.test.ts +++ b/test/e2e/search.test.ts @@ -45,6 +45,16 @@ describe.skipIf(!E2E_URL)("end-to-end: search commands", () => { }, 150_000); it("returns live issue matches under the locator schema", async () => { + // Gitea's search endpoint is backed by an eventually-consistent issue + // indexer, so poll until the seeded issue has been indexed and surfaces + // under the locator-schema header before asserting on the exact output. + await expect + .poll( + async () => (await runCliTest(["search", "issues", "issue"], { env: env() })).stdout, + { timeout: 20_000, interval: 500 }, + ) + .toMatch(/^issues\[\d+\]\{number,title,state,author,created\}:$/m); + const { stdout, exitCode } = await runCliTest(["search", "issues", "issue"], { env: env(), }); @@ -61,6 +71,15 @@ describe.skipIf(!E2E_URL)("end-to-end: search commands", () => { }); it("returns live pull-request matches under the locator schema", async () => { + // The PR was opened moments ago in beforeAll; the indexer needs a beat to + // catch up, so poll the search until the PR surfaces before asserting. + await expect + .poll( + async () => (await runCliTest(["search", "prs", "search"], { env: env() })).stdout, + { timeout: 20_000, interval: 500 }, + ) + .toMatch(/^pull_requests\[\d+\]\{number,title,state,author,created\}:$/m); + const { stdout, exitCode } = await runCliTest(["search", "prs", "search"], { env: env(), });