From e2b0c62628e0659df206a64109280c79602fcbda Mon Sep 17 00:00:00 2001 From: Do Siki Date: Sat, 12 Sep 2026 16:37:57 +0200 Subject: [PATCH] test(cms): Payload config + Local API tests, fix stale Docker tests (MITHOME-96) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the two test categories the ticket asked for, replacing the old content-JSON-schema-only testing story now that Payload is the real source of truth: 1. src/payload-config.test.ts — fast, DB-free unit tests over the Collection/Global config objects themselves. WHY not importing payload.config.ts directly: it pulls in the real `payload` and `@payloadcms/db-mongodb` packages as values (not just types), which are ESM-only and Jest's default transformIgnorePatterns skips node_modules entirely — confirmed by trying it (`SyntaxError: Cannot use import statement outside a module` from payload's own dist). Individual collection/global files only ever `import type` from 'payload' (erased at compile time), so they're safely importable in isolation. Two of the assertions are deliberate regression guards for real bugs found earlier this session (MITHOME-121): Media.access.read must stay public, and Partners.logo must stay optional. 2. scripts/test-payload-local-api.ts (npm run test:payload) — exercises every src/lib/payload-content.ts getter plus a full create/findByID/delete cycle against a real, already-migrated MongoDB. WHY a plain node script instead of a Jest integration config: same ESM problem as above, and Payload's dependency graph is too broad to safely add to transformIgnorePatterns — this follows the same working `node --import tsx` pattern already used by migrate-content-to-payload.ts. Not wired into pre-deploy-tests.sh (needs a live, pre-migrated MongoDB, same reasoning as the removed CMS integration tests); run manually or before a release. 3. Fixed the pre-existing (Docker-stack-gated, so silently never caught) staleness in integration.test.ts and e2e-docker.test.ts: unprefixed routes (/rolunk) → locale-prefixed (/hu/rolunk), and Mongo assertions against the old site_config/contact_submissions collections → the real globals/legal-pages/partners/ contact-submissions collections Payload actually uses. Also fixed an unrelated stale error-message assertion for spam detection that never matched the route's real string. Verified: full gate green (tsc, lint, 63 unit tests — 13 new — build), and separately ran test:payload live against the dev MongoDB (8/8 passed, including the write/read/delete cycle actually hitting Mongo). Co-Authored-By: Claude Sonnet 5 --- .agent/steering/testing.md | 23 +++- proto/package.json | 3 +- proto/scripts/test-payload-local-api.ts | 134 ++++++++++++++++++++++ proto/src/__tests__/e2e-docker.test.ts | 22 ++-- proto/src/__tests__/integration.test.ts | 66 ++++++----- proto/src/payload-config.test.ts | 145 ++++++++++++++++++++++++ 6 files changed, 353 insertions(+), 40 deletions(-) create mode 100644 proto/scripts/test-payload-local-api.ts create mode 100644 proto/src/payload-config.test.ts diff --git a/.agent/steering/testing.md b/.agent/steering/testing.md index 3595700..696a575 100644 --- a/.agent/steering/testing.md +++ b/.agent/steering/testing.md @@ -44,6 +44,7 @@ npm run test:coverage # lefedettség riport npm run test:browser # browser integration npm run test:integration # Docker integration (Docker kell!) npm run test:e2e # E2E tesztek (Docker kell!) +npm run test:payload # Payload Local API integráció (élő MongoDB + migrált tartalom kell!) npm run test:all # teljes proto suite npm run test:smoke:staging # Playwright smoke a staging ellen ``` @@ -84,5 +85,23 @@ Plane-sync unit tesztek. Bármelyik hibája megszakítja a kiadást. > serializer, optimista zárolás, verziók panel, logó feltöltés, login/logout, > guide, publish) a Payload CMS-re állás után (MITHOME-93) eltávolítottuk — > a Payload admin felület a saját upstream tesztelésével fedett, ezt itt nem -> duplikáljuk. Payload collection/global konfigurációk saját tesztlefedettsége -> külön feladat (MITHOME-96), még nincs implementálva. +> duplikáljuk. +> +> **MITHOME-96**: Payload collection/global config tesztek — +> `src/payload-config.test.ts` (a `npm test`/pre-deploy suite része, nincs +> hozzá élő DB, csak a Collection/Global config objektumokat vizsgálja — +> köztük két, MITHOME-121-ben talált éles hibára regresszió-őrt: a Media +> publikus olvashatósága, a Partners.logo opcionalitása). A Local API elleni, +> élő MongoDB-t igénylő tesztek külön scriptben vannak +> (`scripts/test-payload-local-api.ts`, `npm run test:payload`) — WHY nem +> Jest: a `payload` csomag ESM-only dist-et ad ki, amit a next/jest +> transzform alapból nem fordít le (a teljes függőségi fa +> `transformIgnorePatterns`-be vétele törékeny lenne), ezért ugyanazt a +> bevált `node --import tsx` mintát követi, mint `migrate-content-to-payload.ts`. +> Nincs a `pre-deploy-tests.sh`-ban (élő, migrált tartalmú MongoDB kell hozzá, +> mint a régi, eltávolított CMS-teszteknek is), de a `npm run test:payload` +> paranccsal bármikor lefuttatható egy futó dev MongoDB ellen. A régi, +> Docker-stack-hez kötött `integration.test.ts`/`e2e-docker.test.ts` is +> frissítve lett a locale-prefixes útvonalakra és a Payload collection-nevekre +> (korábban a pre-Payload, unprefixelt útvonalakra és `site_config`/ +> `contact_submissions` nyers Mongo collection-ökre hivatkoztak). diff --git a/proto/package.json b/proto/package.json index 57bad9f..8357788 100755 --- a/proto/package.json +++ b/proto/package.json @@ -28,7 +28,8 @@ "docker:dev:logs": "cd .. && docker-compose -f docker-compose.dev.yml logs -f app", "docker:build": "docker build -t mozdit-app .", "docker:run": "docker run -p 3000:3000 --env-file .env.local mozdit-app", - "migrate:content": "node --env-file=.env.local --import tsx scripts/migrate-content-to-payload.ts" + "migrate:content": "node --env-file=.env.local --import tsx scripts/migrate-content-to-payload.ts", + "test:payload": "node --env-file=.env.local --import tsx scripts/test-payload-local-api.ts" }, "dependencies": { "@payloadcms/db-mongodb": "^3.88.0", diff --git a/proto/scripts/test-payload-local-api.ts b/proto/scripts/test-payload-local-api.ts new file mode 100644 index 0000000..c1ca503 --- /dev/null +++ b/proto/scripts/test-payload-local-api.ts @@ -0,0 +1,134 @@ +/** + * MITHOME-96 — Payload Local API integrációs teszt. + * + * Ez a "Local API integrációs tesztek" rész a ticketből: valódi, futó + * MongoDB-vel ellenőrzi, hogy a src/lib/payload-content.ts adapter-réteg + * ténylegesen jó alakú adatot ad vissza a Globals/Collections-ökből — nem + * mock-olt Payload-dal, hanem éles Local API hívásokkal. + * + * WHY sima node script és nem Jest teszt: a Payload csomag (és több + * függősége, pl. @payloadcms/richtext-lexical) ESM-only dist-et ad ki — + * Jest (a next/jest SWC transformjával is) alapból nem transzformálja a + * node_modules-t, így `import { getPayload } from 'payload'` egy Jest + * tesztben `SyntaxError: Cannot use import statement outside a module`-lel + * bukik (kipróbálva). A `transformIgnorePatterns` kiterjesztése a teljes + * Payload-függőségi fára törékeny és karbantartás-igényes lenne — ehelyett + * ugyanazt a bevált mintát követjük, mint a migrate-content-to-payload.ts / + * export-content-snapshot.ts scriptek: `node --import tsx`, valódi Node + * ESM-mel, semmilyen Jest-transzform nem kell. + * + * Futtatás (proto/ mappából, futó MongoDB-vel, migrált tartalommal): + * MONGODB_URI="mongodb://admin:password123@localhost:27018/mozdit?authSource=admin" \ + * npm run test:payload + */ +import assert from 'assert/strict' +import { getPayload } from 'payload' +import config from '../src/payload.config' +import { + getCommonContent, + getHomeContent, + getAboutContent, + getServicesContent, + getContactContent, + getLegalPage, + getPartners, +} from '../src/lib/payload-content' + +let passed = 0 + +async function test(name: string, fn: () => Promise) { + try { + await fn() + passed++ + console.log(`✅ ${name}`) + } catch (error) { + console.error(`❌ ${name}`) + throw error + } +} + +async function run() { + const payload = await getPayload({ config }) + + await test('getCommonContent visszaad hu és en tartalmat', async () => { + const hu = await getCommonContent('hu') + const en = await getCommonContent('en') + assert.ok(hu.buttons.webmail, 'hiányzó hu webmail gomb szöveg') + assert.ok(en.buttons.webmail, 'hiányzó en webmail gomb szöveg') + }) + + await test('getHomeContent unwrap-eli a trustBullets és services.items.features tömböket', async () => { + const home = await getHomeContent('hu') + assert.ok(Array.isArray(home.hero.trustBullets)) + assert.ok(home.hero.trustBullets.every((v) => typeof v === 'string')) + for (const item of home.services.items) { + assert.ok(Array.isArray(item.features)) + assert.ok(item.features.every((v) => typeof v === 'string')) + } + }) + + await test('getAboutContent unwrap-eli a story és team paragraphs tömböket', async () => { + const about = await getAboutContent('hu') + assert.ok(Array.isArray(about.story.paragraphs)) + assert.ok(Array.isArray(about.team.paragraphs)) + }) + + await test('getServicesContent unwrap-eli a specs.items tömböket', async () => { + const services = await getServicesContent('hu') + for (const service of services.details.services) { + assert.ok(Array.isArray(service.specs.items)) + } + }) + + await test('getContactContent visszaadja a form mezőket', async () => { + const contact = await getContactContent('hu') + assert.ok(contact.form.fields.email.label) + }) + + await test('getLegalPage megtalálja mindkét jogi oldalt', async () => { + const privacy = await getLegalPage('adatvedelem', 'hu') + const terms = await getLegalPage('hasznalati-feltetelek', 'hu') + assert.equal(privacy?.slug, 'adatvedelem') + assert.equal(terms?.slug, 'hasznalati-feltetelek') + }) + + await test('getPartners csak logóval rendelkező partnereket ad vissza', async () => { + const partners = await getPartners() + for (const partner of partners) { + assert.ok(partner.logo.url) + assert.ok(partner.name) + } + }) + + await test('create/findByID/delete ciklus működik (contact-submissions)', async () => { + const created = await payload.create({ + collection: 'contact-submissions', + data: { + name: 'Local API Test', + email: 'local-api-test@example.com', + subject: 'MITHOME-96 Local API teszt', + message: 'Ez a rekord a test-payload-local-api.ts futása során jön létre és törlődik.', + gdprConsent: true, + status: 'new', + }, + }) + assert.ok(created.id) + + const found = await payload.findByID({ collection: 'contact-submissions', id: created.id }) + assert.equal(found.email, 'local-api-test@example.com') + + await payload.delete({ collection: 'contact-submissions', id: created.id }) + + await assert.rejects( + payload.findByID({ collection: 'contact-submissions', id: created.id }) + ) + }) + + console.log(`\n${passed}/${passed} Payload Local API teszt zöld.`) + process.exit(0) +} + +run().catch((error) => { + console.error('\nPayload Local API teszt sikertelen:', error) + process.exit(1) +}) diff --git a/proto/src/__tests__/e2e-docker.test.ts b/proto/src/__tests__/e2e-docker.test.ts index ac297b3..718e766 100755 --- a/proto/src/__tests__/e2e-docker.test.ts +++ b/proto/src/__tests__/e2e-docker.test.ts @@ -1,6 +1,10 @@ /** * End-to-End tests for the Docker environment * These tests verify the full application flow in the Docker stack + * + * MITHOME-96: routes are locale-prefixed (/hu/..., /en/...) since the + * Payload CMS migration (MITHOME-91/114) — updated from the old unprefixed + * paths. */ describe('Docker E2E Tests', () => { @@ -27,24 +31,24 @@ describe('Docker E2E Tests', () => { expect(html).toContain('mozdIT Bt.') // Test navigation links exist in homepage - expect(html).toContain('href="/rolunk"') - expect(html).toContain('href="/szolgaltatasok"') - expect(html).toContain('href="/kapcsolat"') + expect(html).toContain('href="/hu/rolunk"') + expect(html).toContain('href="/hu/szolgaltatasok"') + expect(html).toContain('href="/hu/kapcsolat"') // Test about page - response = await fetch(`${APP_URL}/rolunk`) + response = await fetch(`${APP_URL}/hu/rolunk`) expect(response.status).toBe(200) html = await response.text() expect(html).toContain('Rólunk') // Test services page - response = await fetch(`${APP_URL}/szolgaltatasok`) + response = await fetch(`${APP_URL}/hu/szolgaltatasok`) expect(response.status).toBe(200) html = await response.text() expect(html).toContain('Szolgáltatásaink') // Test contact page - response = await fetch(`${APP_URL}/kapcsolat`) + response = await fetch(`${APP_URL}/hu/kapcsolat`) expect(response.status).toBe(200) html = await response.text() expect(html).toContain('Kapcsolat') @@ -210,9 +214,9 @@ describe('Docker E2E Tests', () => { const pages = [ { url: '', title: 'mozdIT Bt.' }, - { url: '/rolunk', title: 'Rólunk' }, - { url: '/szolgaltatasok', title: 'Szolgáltatásaink' }, - { url: '/kapcsolat', title: 'Kapcsolatfelvétel' } + { url: '/hu/rolunk', title: 'Rólunk' }, + { url: '/hu/szolgaltatasok', title: 'Szolgáltatásaink' }, + { url: '/hu/kapcsolat', title: 'Kapcsolat' } ] for (const page of pages) { diff --git a/proto/src/__tests__/integration.test.ts b/proto/src/__tests__/integration.test.ts index 1958332..a318771 100755 --- a/proto/src/__tests__/integration.test.ts +++ b/proto/src/__tests__/integration.test.ts @@ -1,6 +1,14 @@ /** * Integration tests for the Docker development environment * These tests run against the real services in the Docker stack + * + * MITHOME-96: updated for the Payload CMS migration (MITHOME-85 epic) — + * routes are now locale-prefixed (/hu/..., /en/...) and content lives in + * Payload collections/globals (`globals`, `legal-pages`, `partners`, + * `contact-submissions`), not the old raw `site_config`/`contact_submissions` + * Mongoose collections. Assumes the Docker dev DB already has content + * migrated (`npm run migrate:content`) — see docs/backups for a snapshot if + * you need to reseed a throwaway environment. */ import { MongoClient, ObjectId } from 'mongodb' @@ -72,14 +80,18 @@ describe('Docker Environment Integration Tests', () => { const mozditDb = mongoClient.db('mozdit') const collections = await mozditDb.listCollections().toArray() - + const collectionNames = collections.map(c => c.name) - expect(collectionNames).toContain('site_config') - expect(collectionNames).toContain('contact_submissions') + // Payload Globals (Home/About/Services/Contact/Common) live in a + // single `globals` collection, one document per global. + expect(collectionNames).toContain('globals') + expect(collectionNames).toContain('legal-pages') + expect(collectionNames).toContain('partners') + expect(collectionNames).toContain('contact-submissions') expect(collectionNames).toContain('users') }) - it('should verify site config data exists', async () => { + it('should verify the Home global exists and is published', async () => { if (process.env.NODE_ENV === 'test' && !process.env.INTEGRATION_TESTS) { return } @@ -88,17 +100,14 @@ describe('Docker Environment Integration Tests', () => { mongoClient = new MongoClient(DOCKER_SERVICES.mongodb) await mongoClient.connect() } - - const mozditDb = mongoClient.db('mozdit') - const siteConfig = await mozditDb.collection('site_config').findOne() - if (!siteConfig) throw new Error('site_config document not found') - expect(siteConfig).toBeTruthy() - expect(siteConfig).toHaveProperty('type', 'site_config') - expect(siteConfig).toHaveProperty('environment', 'development') - expect(siteConfig).toHaveProperty('data') - expect(siteConfig.data).toHaveProperty('general') - expect(siteConfig.data.general).toHaveProperty('name', 'mozdIT Bt.') + const mozditDb = mongoClient.db('mozdit') + const home = await mozditDb.collection('globals').findOne({ globalType: 'home' }) + + if (!home) throw new Error('Home global document not found') + expect(home).toHaveProperty('_status', 'published') + expect(home).toHaveProperty('hero') + expect(home.hero).toHaveProperty('title') }) it('should access Mongo Express UI', async () => { @@ -179,7 +188,7 @@ describe('Docker Environment Integration Tests', () => { } const savedSubmission = await mongoClient .db('mozdit') - .collection('contact_submissions') + .collection('contact-submissions') .findOne({ _id: new ObjectId(result.submissionId) }) expect(savedSubmission).toEqual(expect.objectContaining({ name: contactData.name, @@ -256,7 +265,7 @@ describe('Docker Environment Integration Tests', () => { const result = await response.json() if (response.status === 400) { - expect(result).toHaveProperty('error', 'Spam gyanús tartalom észlelve') + expect(result).toHaveProperty('error', 'Az üzenet spam gyanús tartalmat tartalmaz.') } else if (response.status === 429) { expect(result).toHaveProperty('error') expect(result.error).toContain('Túl sok') @@ -270,13 +279,14 @@ describe('Docker Environment Integration Tests', () => { return } + // MITHOME-91/114: bare "/" 307-redirects to "/hu" — fetch follows + // redirects by default, so this still lands on the real homepage. const response = await fetch(DOCKER_SERVICES.app) expect(response.status).toBe(200) - + const html = await response.text() expect(html).toContain('mozdIT Bt.') - expect(html).toContain('Megbízható web‑ és email‑szolgáltatás') - expect(html).toContain('Webmail Ugrás') + expect(html).toContain('Megbízható web- és email szolgáltatás') }) it('should load about page', async () => { @@ -284,9 +294,9 @@ describe('Docker Environment Integration Tests', () => { return } - const response = await fetch(`${DOCKER_SERVICES.app}/rolunk`) + const response = await fetch(`${DOCKER_SERVICES.app}/hu/rolunk`) expect(response.status).toBe(200) - + const html = await response.text() expect(html).toContain('Rólunk') expect(html).toContain('mozdIT Bt.') @@ -297,14 +307,14 @@ describe('Docker Environment Integration Tests', () => { return } - const response = await fetch(`${DOCKER_SERVICES.app}/szolgaltatasok`) + const response = await fetch(`${DOCKER_SERVICES.app}/hu/szolgaltatasok`) expect(response.status).toBe(200) - + const html = await response.text() expect(html).toContain('Szolgáltatásaink') - expect(html).toContain('Web Hosting') - expect(html).toContain('Email Szolgáltatás') - expect(html).toContain('DNS Adminisztráció') + expect(html).toContain('Webtárhely') + expect(html).toContain('E-mail szolgáltatás') + expect(html).toContain('DNS adminisztráció') }) it('should load contact page', async () => { @@ -312,9 +322,9 @@ describe('Docker Environment Integration Tests', () => { return } - const response = await fetch(`${DOCKER_SERVICES.app}/kapcsolat`) + const response = await fetch(`${DOCKER_SERVICES.app}/hu/kapcsolat`) expect(response.status).toBe(200) - + const html = await response.text() // The page title is "Kapcsolat" not "Kapcsolatfelvétel" expect(html).toContain('Kapcsolat') diff --git a/proto/src/payload-config.test.ts b/proto/src/payload-config.test.ts new file mode 100644 index 0000000..824c682 --- /dev/null +++ b/proto/src/payload-config.test.ts @@ -0,0 +1,145 @@ +/** + * MITHOME-96 — Payload collection/global config tesztek. + * + * WHY nem a teljes payload.config.ts-t importáljuk: az a valódi `payload` és + * `@payloadcms/db-mongodb` csomagokat importálja értékként (nem csak + * típusként), amik ESM-only, natív node_modules-forrást futtatnak — Jest + * (a next/jest transzformmal is) nem tudja lefordítani őket anélkül, hogy a + * teljes payload-függőségi fát is a transformIgnorePatterns kivételévé + * tennénk (próbáltuk: `SyntaxError: Cannot use import statement outside a + * module` a payload csomag saját forrásából). Az egyes Collection/Global + * fájlok viszont csak `import type` formában hivatkoznak a `payload` + * csomagra — ez típus-only import, a build kitörli, így ezek a fájlok + * önmagukban, gyors unit tesztként importálhatók, élő MongoDB/PAYLOAD_SECRET + * nélkül. Ez fedi le a ticket "Payload config tesztek" részét; a Local API + * ellen futó, élő adatbázist igénylő tesztek külön (integrációs) fájlban + * vannak — lásd src/__tests__/payload-local-api.test.ts. + * + * Ezek a tesztek szándékosan regresszió-őrök is: mindkettő egy-egy valódi, + * élesben megtalált hibát rögzít (MITHOME-121: Media publikus olvasása, + * Partners.logo opcionalitása), hogy soha többé ne térjenek vissza észrevétlenül. + */ +import { Users } from './collections/Users' +import { LegalPages } from './collections/LegalPages' +import { Media } from './collections/Media' +import { Partners } from './collections/Partners' +import { ContactSubmissions } from './collections/ContactSubmissions' +import { Home } from './globals/Home' +import { About } from './globals/About' +import { Services } from './globals/Services' +import { Contact } from './globals/Contact' +import { Common } from './globals/Common' + +type FieldLike = { name?: string; required?: boolean; type?: string } + +function findField(fields: unknown[], name: string): FieldLike | undefined { + return (fields as FieldLike[]).find((f) => f.name === name) +} + +describe('Payload collections', () => { + it('all collection slugs are present and unique', () => { + const slugs = [Users, LegalPages, Media, Partners, ContactSubmissions].map((c) => c.slug) + expect(slugs).toEqual(['users', 'legal-pages', 'media', 'partners', 'contact-submissions']) + expect(new Set(slugs).size).toBe(slugs.length) + }) + + describe('Media', () => { + // Regresszió-őr — MITHOME-121: a Media collection read access-e sokáig + // (MITHOME-89 óta) alapértelmezetten csak bejelentkezett usernek volt + // engedélyezett, emiatt a partner logók sosem töltődtek be a publikus + // oldalon (403 -> a Next.js image-optimizer "nem érvényes kép" hibája). + it('allows public (unauthenticated) read access', () => { + expect(Media.access?.read).toBeDefined() + const result = Media.access!.read!({ req: { user: null } } as never) + expect(result).toBe(true) + }) + + it('only accepts image uploads', () => { + expect(Media.upload).toEqual(expect.objectContaining({ mimeTypes: ['image/*'] })) + }) + }) + + describe('Partners', () => { + it('requires name and url', () => { + expect(findField(Partners.fields, 'name')?.required).toBe(true) + expect(findField(Partners.fields, 'url')?.required).toBe(true) + }) + + // Regresszió-őr — MITHOME-121: a felhasználó kérésére a logo mező + // opcionálissá vált, hogy egy partner logó nélkül is menthető legyen. + it('does not require a logo', () => { + const logo = findField(Partners.fields, 'logo') + expect(logo).toBeDefined() + expect(logo?.required).not.toBe(true) + }) + }) + + describe('ContactSubmissions', () => { + // WHY nincs egyedi access blokk itt tesztelve mint "hiányzik": a Payload + // defaultAccess (csak bejelentkezett user) itt a kívánt viselkedés — + // lásd a collection saját WHY-kommentjét. Nincs mit tesztelni rajta + // (nincs felülírás), de a mezőszerkezetet igen. + it('requires the core submission fields', () => { + for (const name of ['name', 'email', 'subject', 'message', 'gdprConsent']) { + expect(findField(ContactSubmissions.fields, name)?.required).toBe(true) + } + }) + + it('defaults status to "new"', () => { + const status = findField(ContactSubmissions.fields, 'status') as FieldLike & { + defaultValue?: string + options?: { value: string }[] + } + expect(status?.defaultValue).toBe('new') + expect(status?.options?.map((o) => o.value)).toEqual(['new', 'read', 'replied', 'archived']) + }) + }) + + describe('LegalPages', () => { + it('has a required, unique slug field', () => { + const slug = findField(LegalPages.fields, 'slug') as FieldLike & { unique?: boolean } + expect(slug?.required).toBe(true) + expect(slug?.unique).toBe(true) + }) + }) + + describe('Users', () => { + it('locks accounts after 5 failed attempts for 10 minutes', () => { + expect(Users.auth).toEqual( + expect.objectContaining({ maxLoginAttempts: 5, lockTime: 10 * 60 * 1000 }) + ) + }) + + it('requires an authenticated user for every access-controlled operation', () => { + const access = Users.access! + for (const op of ['create', 'read', 'update', 'delete', 'unlock'] as const) { + expect(access[op]!({ req: { user: null } } as never)).toBe(false) + expect(access[op]!({ req: { user: {} } } as never)).toBe(true) + } + }) + }) +}) + +describe('Payload globals', () => { + it('all global slugs are present and unique', () => { + const slugs = [Home, About, Services, Contact, Common].map((g) => g.slug) + expect(slugs).toEqual(['home', 'about', 'services', 'contact', 'common']) + expect(new Set(slugs).size).toBe(slugs.length) + }) + + it('Home does not duplicate the Partners collection', () => { + // MITHOME-89 döntés: a partnerek önálló collection-ök, a Home global + // szándékosan nem tartalmaz "partners" mezőt. + expect(findField(Home.fields, 'partners')).toBeUndefined() + }) + + it('Common.buttons.* fields are localized (MITHOME-110 bizonyíték)', () => { + const buttonsGroup = findField(Common.fields, 'buttons') as FieldLike & { + fields?: (FieldLike & { localized?: boolean })[] + } + expect(buttonsGroup?.fields?.length).toBeGreaterThan(0) + for (const field of buttonsGroup!.fields!) { + expect(field.localized).toBe(true) + } + }) +})