b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
147 lines
4.5 KiB
TypeScript
147 lines
4.5 KiB
TypeScript
/**
|
|
* Locale-aware taxonomy term hydration in the public query helpers (#1441).
|
|
*
|
|
* `getEmDashEntry` / `getEmDashCollection` resolve the content row to a locale
|
|
* (explicit option > request context > defaultLocale). Term hydration must use
|
|
* that same resolved locale, otherwise a `fr-fr` entry can come back with the
|
|
* default-locale variants of its taxonomy terms.
|
|
*
|
|
* The bug was that `hydrateEntryTerms()` called `getAllTermsForEntries(type, ids)`
|
|
* with no locale, so term lookup fell back to the request-context / default
|
|
* locale instead of the entry's own locale.
|
|
*/
|
|
|
|
import type { Kysely } from "kysely";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { ContentRepository } from "../../src/database/repositories/content.js";
|
|
import { TaxonomyRepository } from "../../src/database/repositories/taxonomy.js";
|
|
import type { Database } from "../../src/database/types.js";
|
|
import { setI18nConfig } from "../../src/i18n/config.js";
|
|
import { getEmDashCollection, getEmDashEntry } from "../../src/query.js";
|
|
import { runWithContext } from "../../src/request-context.js";
|
|
import { setupTestDatabaseWithCollections, teardownTestDatabase } from "../utils/test-db.js";
|
|
|
|
vi.mock("astro:content", () => ({
|
|
getLiveCollection: vi.fn(),
|
|
getLiveEntry: vi.fn(),
|
|
}));
|
|
|
|
import { getLiveCollection, getLiveEntry } from "astro:content";
|
|
|
|
interface TermFixture {
|
|
enContentId: string;
|
|
frContentId: string;
|
|
frContentSlug: string;
|
|
enTagId: string;
|
|
frTagId: string;
|
|
}
|
|
|
|
/**
|
|
* Seed an EN + FR post (same translation group) and a tag with EN + FR
|
|
* variants, attached to both posts by translation_group.
|
|
*/
|
|
async function seedLocalizedTags(db: Kysely<Database>): Promise<TermFixture> {
|
|
const contentRepo = new ContentRepository(db);
|
|
const taxRepo = new TaxonomyRepository(db);
|
|
|
|
// Two post rows sharing a translation group.
|
|
const enContent = await contentRepo.create({
|
|
type: "post",
|
|
slug: "hello",
|
|
data: { title: "Hello" },
|
|
locale: "en",
|
|
});
|
|
const frContent = await contentRepo.create({
|
|
type: "post",
|
|
slug: "bonjour",
|
|
data: { title: "Bonjour" },
|
|
locale: "fr",
|
|
translationOf: enContent.id,
|
|
});
|
|
|
|
// One tag with EN + FR variants (shared translation_group).
|
|
const enTag = await taxRepo.create({ name: "tags", slug: "news", label: "News", locale: "en" });
|
|
const frTag = await taxRepo.create({
|
|
name: "tags",
|
|
slug: "actualites",
|
|
label: "Actualités",
|
|
locale: "fr",
|
|
translationOf: enTag.id,
|
|
});
|
|
|
|
// Attach the tag (by group) to both entries.
|
|
await taxRepo.attachToEntry("post", enContent.id, enTag.id);
|
|
await taxRepo.attachToEntry("post", frContent.id, enTag.id);
|
|
|
|
return {
|
|
enContentId: enContent.id,
|
|
frContentId: frContent.id,
|
|
frContentSlug: frContent.slug,
|
|
enTagId: enTag.id,
|
|
frTagId: frTag.id,
|
|
};
|
|
}
|
|
|
|
describe("query helpers hydrate terms in the resolved locale (#1441)", () => {
|
|
let db: Kysely<Database>;
|
|
|
|
beforeEach(async () => {
|
|
db = await setupTestDatabaseWithCollections();
|
|
setI18nConfig({ defaultLocale: "en", locales: ["en", "fr"] });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await teardownTestDatabase(db);
|
|
setI18nConfig(null);
|
|
vi.mocked(getLiveCollection).mockReset();
|
|
vi.mocked(getLiveEntry).mockReset();
|
|
});
|
|
|
|
it("getEmDashEntry returns the FR term variant for an explicit fr locale", async () => {
|
|
const fx = await seedLocalizedTags(db);
|
|
|
|
vi.mocked(getLiveEntry).mockResolvedValue({
|
|
entry: {
|
|
id: fx.frContentSlug,
|
|
data: { id: fx.frContentId, title: "Bonjour", status: "published", locale: "fr" },
|
|
},
|
|
error: undefined,
|
|
cacheHint: {},
|
|
} as any);
|
|
|
|
const { entry } = await runWithContext({ editMode: false, db }, () =>
|
|
getEmDashEntry("post", fx.frContentSlug, { locale: "fr" }),
|
|
);
|
|
|
|
const tags = (entry?.data as any)?.terms?.tags as Array<{ id: string; locale: string }>;
|
|
expect(tags).toHaveLength(1);
|
|
expect(tags[0]!.id).toBe(fx.frTagId);
|
|
expect(tags[0]!.locale).toBe("fr");
|
|
});
|
|
|
|
it("getEmDashCollection returns the FR term variant for an explicit fr locale", async () => {
|
|
const fx = await seedLocalizedTags(db);
|
|
|
|
vi.mocked(getLiveCollection).mockResolvedValue({
|
|
entries: [
|
|
{
|
|
id: fx.frContentSlug,
|
|
data: { id: fx.frContentId, title: "Bonjour", status: "published", locale: "fr" },
|
|
},
|
|
],
|
|
error: undefined,
|
|
cacheHint: {},
|
|
} as any);
|
|
|
|
const { entries } = await runWithContext({ editMode: false, db }, () =>
|
|
getEmDashCollection("post", { locale: "fr" }),
|
|
);
|
|
|
|
const tags = (entries[0]?.data as any)?.terms?.tags as Array<{ id: string; locale: string }>;
|
|
expect(tags).toHaveLength(1);
|
|
expect(tags[0]!.id).toBe(fx.frTagId);
|
|
expect(tags[0]!.locale).toBe("fr");
|
|
});
|
|
});
|