import assert from 'node:assert/strict' import { createServer } from 'node:http' import test from 'node:test' import { WikiClient } from '../src/wiki-client.js' import { normalizeApiKey, normalizeBaseUrl } from '../src/utils.js' const listItem = { id: 1, path: 'docs', locale: 'en', title: 'Docs', description: 'Documentation', contentType: 'markdown', isPublished: true, updatedAt: '2026-01-02T10:00:00.000Z', } function startWiki({ expectedKey = 'test-key' } = {}) { const requests = [] const server = createServer((request, response) => { let raw = '' request.on('data', (chunk) => { raw += chunk }) request.on('end', () => { const authorization = request.headers.authorization || '' const body = JSON.parse(raw || '{}') requests.push({ authorization, query: body.query || '', variables: body.variables || {} }) if (authorization !== `Bearer ${expectedKey}`) { response.writeHead(401, { 'Content-Type': 'application/json' }) response.end(JSON.stringify({ error: 'Unauthorized' })) return } const send = (data) => { response.writeHead(200, { 'Content-Type': 'application/json' }) response.end(JSON.stringify({ data })) } if (body.query.includes('WikiPrintPages')) { send({ pages: { list: [listItem] } }) } else if (body.query.includes('WikiPrintLocalization')) { send({ localization: { config: { locale: 'en', namespacing: true, namespaces: ['en', 'de'] }, }, }) } else if (body.query.includes('WikiPrintPageSource')) { send({ pages: { single: { content: '# Docs', contentType: 'markdown' } } }) } else if (body.query.includes('WikiPrintPage')) { send({ pages: { singleByPath: { ...listItem, render: '

Docs

', createdAt: listItem.updatedAt, }, }, }) } else { response.writeHead(400, { 'Content-Type': 'application/json' }) response.end(JSON.stringify({ errors: [{ message: 'Unknown query' }] })) } }) }) return new Promise((resolve) => { server.listen(0, '127.0.0.1', () => { resolve({ url: `http://127.0.0.1:${server.address().port}`, requests, close: () => new Promise((done) => server.close(done)), }) }) }) } test('normalizes API keys from environment-style input', () => { assert.equal(normalizeApiKey(' test-key '), 'test-key') assert.equal(normalizeApiKey('Bearer test-key'), 'test-key') assert.equal(normalizeApiKey('bearer test-key'), 'test-key') assert.equal(normalizeApiKey(undefined), '') }) test('uses Wiki.js 2.x queries and Bearer authentication', async () => { const wiki = await startWiki() try { const client = new WikiClient({ baseUrl: normalizeBaseUrl(wiki.url), apiKey: 'Bearer test-key', }) assert.equal((await client.listPages())[0].path, 'docs') assert.equal((await client.getLocalization()).namespacing, true) assert.equal((await client.getPage('docs', 'en')).render, '

Docs

') assert.equal((await client.getPageSource(1)).content, '# Docs') assert.ok(wiki.requests.length >= 4) assert.ok(wiki.requests.every((request) => request.authorization === 'Bearer test-key')) const queryFor = (marker) => wiki.requests.find((request) => request.query.includes(marker))?.query || '' // These fields exist in the Wiki.js 2.x schema but require permissions // beyond read:pages (read:source or write:pages), so requests protected // with read:pages must not select them. const listQuery = queryFor('WikiPrintPages') assert.match(listQuery, /\bpath\b/) assert.match(listQuery, /\blocale\b/) assert.match(listQuery, /\bisPublished\b/) assert.doesNotMatch(listQuery, /\bisPrivate\b/) assert.doesNotMatch(listQuery, /\bauthorEmail\b/) assert.doesNotMatch(listQuery, /\bprivateNS\b/) assert.doesNotMatch(listQuery, /\bcontent\b/) const pageQuery = queryFor('WikiPrintPage(') assert.match(pageQuery, /\brender\b/) // Wiki.js may expose `toc` as a String scalar while returning an array, // which makes the whole page query fail with INTERNAL_SERVER_ERROR. // The exporter does not use it. assert.doesNotMatch(pageQuery, /\btoc\b/) assert.doesNotMatch(pageQuery, /\bisPublished\b/) assert.doesNotMatch(pageQuery, /\bisPrivate\b/) assert.doesNotMatch(pageQuery, /\bauthorEmail\b/) assert.doesNotMatch(pageQuery, /\bcontent\b/) const sourceQuery = queryFor('WikiPrintPageSource') assert.match(sourceQuery, /\bcontent\b/) assert.doesNotMatch(sourceQuery, /\brender\b/) const localizationQuery = queryFor('WikiPrintLocalization') assert.match(localizationQuery, /\blocale\b/) assert.match(localizationQuery, /\bnamespacing\b/) assert.match(localizationQuery, /\bnamespaces\b/) } finally { await wiki.close() } }) test('reports a rejected Wiki.js 2.x API key', async () => { const wiki = await startWiki({ expectedKey: 'different-key' }) try { const client = new WikiClient({ baseUrl: normalizeBaseUrl(wiki.url), apiKey: 'test-key', timeoutMs: 2_000, }) await assert.rejects(client.listPages(), (error) => { assert.equal(error.code, 'WIKI_AUTH_FAILED') assert.equal(error.status, 401) return true }) } finally { await wiki.close() } })