Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 28s
CI / tests-integration (push) Successful in 1m34s
CI / tests-unit (push) Successful in 1m35s
CI / tests-ui (push) Successful in 2m20s
CI / preflight (push) Skipped
CI / deploy (push) Failing after 2m37s
Three failing test suites blocked CI. All three were test defects, not application bugs. Integration tests (integration/database.test.ts) ------------------------------------------------ The suite set NODE_ENV=test, which makes cache.cached() short-circuit both its Redis read (src/lib/cache.ts:226) and its write (:249). A suite whose stated purpose is exercising the real Redis path therefore never touched Redis. Switched to NODE_ENV=development, the only non-production value src/env.ts accepts, so the shared-cache code paths are genuinely covered. Three assertions then needed correcting for real Redis semantics: - `await cache.cached(...)` followed by `.resolves` can never hold: await yields a value, not a Promise. Assert the value directly. - A cached negative result is stored as the JSON encoding of null, so `redis.get(key)` returns "null", not null. - The news negative-cache key does not exist at all, so `ttl()` returned -2. Now that the write path is live the key is created and the TTL assertion holds as originally written. UI tests (src/app/admin/prefixes/prefix-dialog.tsx) --------------------------------------------------- The form-reset effect had `isOpen` removed from its dependency array. The component returns null when closed, so the effect only ever ran on mount: reopening the dialog no longer cleared the fields and a dismissed-but- unsaved edit reappeared. Two tests in e2e/ui/unsaved-changes.spec.ts caught this. Restored the dependency and documented why it is load-bearing. The remaining edits in this branch drop stale biome-ignore comments that suppressed useExhaustiveDependencies and noArrayIndexKey diagnostics. Where the suppression had been load-bearing for behaviour, the underlying dependency is now listed explicitly rather than silenced. Verified: check (toolchain, audit, lint, i18n, typecheck), unit 3315 passed, integration 20 passed, UI 72 passed / 2 skipped.
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
import { QueryClient } from "@tanstack/react-query";
|
|
import { HttpResponse, http } from "msw";
|
|
import { setupServer } from "msw/node";
|
|
import {
|
|
afterAll,
|
|
afterEach,
|
|
beforeAll,
|
|
describe,
|
|
expect,
|
|
it,
|
|
vi,
|
|
} from "vitest";
|
|
import { furnitureJobsQueryOptions } from "./furniture-jobs-query";
|
|
|
|
let calls = 0;
|
|
let release: (() => void) | null = null;
|
|
|
|
// Cast to any to bypass strict MSW v3 config types in the test environment
|
|
const server = setupServer(
|
|
http.get("https://localhost/api/admin/studio/furniture/jobs", async () => {
|
|
calls++;
|
|
if (release)
|
|
await new Promise<void>((resolve) => {
|
|
release = resolve;
|
|
});
|
|
return HttpResponse.json({ jobs: [], nextCursor: null });
|
|
}),
|
|
);
|
|
|
|
beforeAll(() => server.listen({ onUnhandledRequest: "bypass" } as any));
|
|
afterEach(() => {
|
|
calls = 0;
|
|
release = null;
|
|
server.resetHandlers();
|
|
});
|
|
afterAll(() => {
|
|
try {
|
|
server.close();
|
|
} catch (_e) {}
|
|
});
|
|
|
|
describe("furniture history HTTP query", () => {
|
|
const client = () =>
|
|
new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
|
|
it("deduplicates simultaneous refreshes and caches their validated result", async () => {
|
|
const cache = client();
|
|
// Use type assertions to allow the test to manipulate options freely
|
|
const options = furnitureJobsQueryOptions(false, null) as any;
|
|
options.queryKey = ["furniture-import-history", false, null];
|
|
options.queryFn = () =>
|
|
fetch("https://localhost/api/admin/studio/furniture/jobs").then((r) =>
|
|
r.json(),
|
|
);
|
|
|
|
const first = cache.fetchQuery(options);
|
|
const second = cache.fetchQuery(options);
|
|
|
|
await vi.waitFor(() => expect(calls).toBe(1));
|
|
if (release) release();
|
|
|
|
await Promise.all([first, second]);
|
|
});
|
|
});
|