// The menu client against a fake fetcher: the URL built from base, code and // locale, the envelope unwrapped, 404 as null. No network. import { test } from 'node:test' import assert from 'node:assert/strict' import { createMenuClient, isNotFound, menuPath } from '../app/utils/client.ts' type Call = { path: string; query?: Record } function fakeFetcher(answer: (path: string) => unknown) { const calls: Call[] = [] const fetcher = async (path: string, query?: Record) => { calls.push(query === undefined ? { path } : { path, query }) return answer(path) } return { fetcher, calls } } const MENU = { data: { code: 'main', name: 'Main', css_class: null, items: [{ id: 1, title: 'Home', type: 'url', url: '/', target: null, css_class: null, icon: null, code: null, items: [] }], }, } test('menuPath encodes the code', () => { assert.equal(menuPath('main'), '/menus/main') assert.equal(menuPath('a b/c'), '/menus/a%20b%2Fc') }) test('getMenu calls /menus/ and passes locale only when set', async () => { const { fetcher, calls } = fakeFetcher(() => MENU) const client = createMenuClient(fetcher) const menu = await client.getMenu('main') assert.equal(menu?.code, 'main') assert.equal(menu?.items.length, 1) await client.getMenu('main', 'en') assert.deepEqual(calls, [{ path: '/menus/main' }, { path: '/menus/main', query: { locale: 'en' } }]) }) test('the fetcher base plus the path is the API URL', async () => { const urls: string[] = [] const base = 'http://127.0.0.1:8096/_menu/api/v1' const client = createMenuClient(async (path, query) => { const u = new URL(base + path) for (const [k, v] of Object.entries(query ?? {})) u.searchParams.set(k, v) urls.push(u.toString()) return MENU }) await client.getMenu('main-menu', 'pl') assert.deepEqual(urls, ['http://127.0.0.1:8096/_menu/api/v1/menus/main-menu?locale=pl']) }) test('a 404 resolves to null; other errors are thrown', async () => { const notFound = createMenuClient(async () => { throw Object.assign(new Error('Not Found'), { statusCode: 404 }) }) assert.equal(await notFound.getMenu('nope'), null) const failing = createMenuClient(async () => { throw Object.assign(new Error('boom'), { status: 500 }) }) await assert.rejects(failing.getMenu('main'), /boom/) }) test('a body without data is null; missing items become an empty list', async () => { assert.equal(await createMenuClient(async () => ({})).getMenu('x'), null) const menu = await createMenuClient(async () => ({ data: { code: 'x', name: 'X', css_class: null } })).getMenu('x') assert.deepEqual(menu?.items, []) }) test('isNotFound reads status, statusCode and response.status', () => { assert.equal(isNotFound({ status: 404 }), true) assert.equal(isNotFound({ statusCode: 404 }), true) assert.equal(isNotFound({ response: { status: 404 } }), true) assert.equal(isNotFound({ status: 500 }), false) assert.equal(isNotFound(null), false) })