mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
d1c6f48c82
* test(preview): assert browse feed renders real content (model + image) The preview smoke suite only proves /models and /images LOAD; it never asserts the feed returns any items, so an empty-feed or broken-list regression ships a blank site while passing every preview spec. Add preview-feed.spec.ts (tester role, gate-passing): asserts the two queries backing the feeds — model.getAll (/models) and image.getInfinite (/images) — each return a non-empty, structurally valid page (>= 1 item with a numeric id). Driven via authed tRPC (same pattern as preview-engagement.spec.ts), tolerant on count, strict on structure against the prod-clone dev DB. Search is intentionally not asserted: preview search is browser-side InstantSearch pointed at the PROD Meilisearch (search-new.civitai.com), not a preview-local index — an external prod dependency that would flake this report-only spec and query a rotating index UID against prod data rather than the PR's own content. Documented inline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(preview): feed spec — drop flaky images-feed assertion (Meilisearch-routed) The live smoke run exposed that a broad image.getInfinite returns flaky/empty results in preview (3 items once, 0 on two consecutive runs). Root cause: getInfiniteImagesHandler routes an unfiltered feed query through getAllImagesIndex (the Meilisearch index path) whenever features.imageIndexFeed or the BitDex Flipt flag is on — the production-like default — and the preview doesn't populate that image search index. Same external-index limitation that already excludes site search from this suite. Drop the images sub-test rather than assert on infra the preview doesn't own. The DB-backed model.getAll (models feed) is the reliable feed-renders-content signal and stays. Documented the routing + a DB-path follow-up (modelId-scoped image.getInfinite forces getAllImages) for future reliable image coverage. Also removes a now-falsified header claim that image.getInfinite was verified returning items live — the authoritative smoke run returned 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: redeploy preview to validate 2-core CPU bump (heavy-SSR flake fix) Empty commit to retrigger pr-preview-2474 onto the new pr-deploy task (requests.cpu 1 / limits.cpu 2, talos-infra ed114e2b0) and confirm the /user/membership + download heavy-SSR specs no longer hit the 60s nav timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: redeploy preview (retry on fixed pr-deploy task) Prior empty-commit run failed because the CPU-bump comment broke shell quoting in pr-deploy (talos-infra 1d1bfbfc4 fixed it). Retrigger to deploy on the 2-core task and validate heavy-SSR specs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
103 lines
5.9 KiB
TypeScript
103 lines
5.9 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import { storageStatePath } from './preview-fixtures';
|
|
import { trpcQuery } from './preview-trpc';
|
|
|
|
/**
|
|
* Feed-content smoke: the core BROWSE path actually renders real content.
|
|
*
|
|
* preview-smoke.spec.ts only proves /models and /images LOAD (HTTP <400, not
|
|
* bounced to /login or /preview-restricted) — it never asserts the feed returns
|
|
* any items. An empty-feed or broken-list regression (a busted model.getAll
|
|
* handler, a query that 500s, a filter that nukes every row) passes every preview
|
|
* spec today while shipping a blank site. This closes that gap for the DB-backed
|
|
* models feed by asserting its backing query returns a non-empty, structurally
|
|
* valid page of content.
|
|
*
|
|
* Why tRPC instead of scraping cards out of the DOM:
|
|
* - The /models feed IS this query — model.getAll backs it
|
|
* (src/components/Model/model.utils.ts:145 `trpc.model.getAll.useInfiniteQuery`).
|
|
* Asserting the query result is asserting the same data the card grid renders,
|
|
* minus the flaky virtualized-list / lazy-image DOM surface (cards mount on
|
|
* scroll/intersection, have no stable testid) — a DOM-card count would be the
|
|
* brittle proxy here, not the source of truth.
|
|
* - Self-contained against the preview's OWN dev-clone DB: no external service,
|
|
* no scroll timing, no networkidle (which never settles — see gotchas below).
|
|
*
|
|
* Runs as `tester` (a free member that PASSES the preview gate). model.getAll is a
|
|
* publicProcedure, reachable for a gate-passing user. page.request carries the
|
|
* test's storageState auth cookie, and preview-auth.middleware gates /api/trpc/*
|
|
* (only /api/auth, /login, /preview-restricted, /_next, /favicon are exempt —
|
|
* matcher '/:path*'), so the authed cookie is what lets the call through the gate —
|
|
* same authed-tRPC pattern as preview-engagement.spec.ts.
|
|
*
|
|
* Verified tRPC shape (civitai repo, paths relative to civitai/src):
|
|
* - model.getAll publicProcedure, input getAllModelsSchema (minus `page`)
|
|
* (server/routers/model.router.ts:141). All fields optional with
|
|
* server defaults, so `{ limit }` is a valid input.
|
|
* getModelsInfiniteHandler returns { items, nextCursor }
|
|
* (server/controllers/model.controller.ts:27 `return { items,
|
|
* nextCursor }`); each item carries a numeric `id`.
|
|
*
|
|
* Tolerant on COUNT, strict on STRUCTURE: the prod-clone dev DB has real content
|
|
* but we never assume specific models/titles. We assert ">= 1 item with a numeric
|
|
* id" — the structural intent is "the feed produced a real, non-empty page of
|
|
* identifiable content", not any particular row. An empty browse feed is itself
|
|
* the regression this spec exists to catch, so keep the non-empty assertion.
|
|
*
|
|
* ── Why the IMAGES feed is NOT asserted here (deliberately scoped out) ──────────
|
|
* The /images feed (image.getInfinite, broad/unfiltered) does NOT reliably read
|
|
* the DB in a preview: getInfiniteImagesHandler routes a broad query (no
|
|
* postId/modelId/collection/reaction filter → requiresDbPath=false) through
|
|
* getAllImagesIndex — the MEILISEARCH-backed index path — whenever
|
|
* `features.imageIndexFeed` or the BitDex Flipt flag (BITDEX_IMAGE_SEARCH) is on,
|
|
* which is the production-like default (src/server/controllers/image.controller.ts
|
|
* :300-320). That path hits an image search index the preview doesn't populate and
|
|
* then hydrates from the dev clone, so a broad image.getInfinite returns flaky /
|
|
* empty results in preview (observed: 3 items once, 0 items on two consecutive
|
|
* smoke runs). This is the SAME external-index limitation site search has — see the
|
|
* search note pattern — so we don't ship an assertion that flakes on infra the
|
|
* preview doesn't own. The DB-backed models feed above is the solid, self-contained
|
|
* signal.
|
|
*
|
|
* FOLLOW-UP for reliable image-feed coverage (not done here): scope the query so
|
|
* `requiresDbPath` is true (e.g. `image.getInfinite { modelId }` — bare modelId
|
|
* forces getAllImages/DB per image.controller.ts:303), seeding the modelId at
|
|
* runtime from this models feed; or stand up a preview-local image index.
|
|
*
|
|
* Only runs under playwright.preview.config.ts (needs PREVIEW_URL + minted states).
|
|
*/
|
|
|
|
const ROLE = 'tester' as const;
|
|
|
|
// A small page is all we need to prove "the feed renders content"; keep it light
|
|
// on the single-replica preview pod (the suite runs serially, workers:1).
|
|
const FEED_LIMIT = 5;
|
|
|
|
test.describe('browse feed renders real content (tester)', () => {
|
|
test.use({ storageState: storageStatePath(ROLE) });
|
|
|
|
test('model.getAll (the /models feed) returns a non-empty page of models', async ({ page }) => {
|
|
// Warm the request context against the preview origin so page.request shares the
|
|
// auth cookie + a real navigated origin (preview-trpc stamps Origin/Referer for
|
|
// the CSRF gate, but navigating once is the safe baseline — mirrors the other
|
|
// tRPC-driven preview specs). domcontentloaded only: NEVER networkidle — the
|
|
// app's background traffic never idles, so a networkidle nav hangs to timeout.
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
|
|
const data = await trpcQuery<{ items: Array<{ id: number }> }>(
|
|
page.request,
|
|
'model.getAll',
|
|
{ limit: FEED_LIMIT }
|
|
);
|
|
|
|
const items = data?.items ?? [];
|
|
// STRUCTURE: a real, non-empty page came back. An empty models feed is the
|
|
// exact regression this catches (broken handler / over-aggressive filter / 500).
|
|
expect(Array.isArray(items), 'model.getAll should return an items array').toBe(true);
|
|
expect(items.length, 'the models feed should render >= 1 model card').toBeGreaterThan(0);
|
|
// Each rendered card needs an identifiable model — assert the first item is a
|
|
// real row (numeric id), not a malformed/placeholder entry.
|
|
expect(typeof items[0]?.id, 'a feed model should carry a numeric id').toBe('number');
|
|
});
|
|
});
|