fix: make all CI jobs pass (integration, ui) and restore prefix dialog reset
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
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.
This commit is contained in:
1 parent
1c9ddcd48a
commit
eddb7edea4
21 files changed
+88
-72
No files matched your search
@@ -156,7 +156,14 @@ beforeAll(async () => {
|
||||
process.env.REDIS_URL = `redis://:${redisPassword}@${redisContainer.getHost()}:${redisContainer.getMappedPort(6379)}/0`;
|
||||
delete process.env.SKIP_ENV_VALIDATION;
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
Object.assign(process.env, { NODE_ENV: "test" });
|
||||
// Deliberately NOT "test": cache.cached() short-circuits its Redis read and
|
||||
// write whenever NODE_ENV === "test" (see refresh() in src/lib/cache.ts).
|
||||
// This suite exists to exercise the real Redis path, so it runs under a
|
||||
// value that leaves Redis enabled. "development" is used because it is the
|
||||
// only non-production value src/env.ts accepts. Vitest's own environment is
|
||||
// still configured via vitest.integration.config.ts. Object.assign is used
|
||||
// because process.env.NODE_ENV is typed read-only.
|
||||
Object.assign(process.env, { NODE_ENV: "development" });
|
||||
process.env.HOTEL_NAME = "Integration";
|
||||
|
||||
await connection.query(
|
||||
@@ -380,9 +387,10 @@ describe("Redis application cache", () => {
|
||||
let fetches = 0;
|
||||
const fetch = async () => ({ revision: ++fetches });
|
||||
expect(await cache.cached(key, 60_000, fetch)).toEqual({ revision: 1 });
|
||||
// Second read is served from cache, so the origin is not consulted again.
|
||||
expect(
|
||||
await cache.cached(key, 60000, async () => ({ revision: 1 })),
|
||||
).resolves.toMatchObject({ revision: 1 });
|
||||
await cache.cached(key, 60_000, fetch),
|
||||
).toEqual({ revision: 1 });
|
||||
expect(await appRedis?.ttl(key)).toBeGreaterThan(0);
|
||||
cache.invalidateMemory(key);
|
||||
expect(await cache.cached(key, 60_000, fetch)).toEqual({ revision: 1 });
|
||||
@@ -403,6 +411,9 @@ describe("Redis application cache", () => {
|
||||
expect(await cache.cached(first, 60_000, async () => "updated")).toBe(
|
||||
"updated",
|
||||
);
|
||||
// `second`'s memory copy was dropped too, but its Redis entry survives, so
|
||||
// the read is served from the shared cache and never recomputes. This is
|
||||
// what makes the two entries independent.
|
||||
expect(await cache.cached(second, 60_000, async () => "wrong")).toBe(
|
||||
"second",
|
||||
);
|
||||
@@ -620,9 +631,12 @@ describe("real news publication, scheduling and cache delivery", () => {
|
||||
expect(existing.status).toBe("draft");
|
||||
expect(existing.publishedAt).toBeNull();
|
||||
expect(await publicNews.getPublishedArticle(existing.slug)).toBeNull();
|
||||
// A draft has no public article, so this read is a negative result that
|
||||
// gets cached. Asserting both the payload and the TTL is what proves the
|
||||
// "never leak an unpublished article" contract survives in Redis.
|
||||
const negativeRevision = await appRedis?.get(NEWS_REVISION_KEY);
|
||||
const negativeKey = `news:${negativeRevision}:article:v2:slug:${existing.slug}`;
|
||||
expect(await appRedis?.get(negativeKey)).toBeNull();
|
||||
expect(await appRedis?.get(negativeKey)).toBe("null");
|
||||
expect(await appRedis?.ttl(negativeKey)).toBeGreaterThan(0);
|
||||
|
||||
const body = `<p>${"Contenuto completo è 📰 ".repeat(4000)}</p>`;
|
||||
@@ -839,7 +853,8 @@ describe("real news publication, scheduling and cache delivery", () => {
|
||||
expect(await publicNews.getPublishedArticle(existing.slug)).toBeNull();
|
||||
const negativeRevision = await redis.get(NEWS_REVISION_KEY);
|
||||
const negativeKey = `news:${negativeRevision}:article:v2:slug:${existing.slug}`;
|
||||
expect(await redis.get(negativeKey)).toBeNull();
|
||||
// Cached negative results are stored as the JSON encoding of null.
|
||||
expect(await redis.get(negativeKey)).toBe("null");
|
||||
const publish = articleForm({
|
||||
id: String(existing.id),
|
||||
baseToken: articleEditToken(existing),
|
||||
|
||||
Reference in new issue
Block a user