const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const root = path.join(__dirname, '..'); function response(data) { return { headers: { get: () => '' }, ok: true, status: 200, statusText: 'OK', text: () => Promise.resolve(JSON.stringify({ code: 'OK', data })), }; } function browser(search = '') { const requests = []; const attributes = new Map(); const storage = new Map(); const location = { hash: '', hostname: 'h5.global-interaction.com', href: `https://h5.global-interaction.com/guild/fami/host-center/${search}`, origin: 'https://h5.global-interaction.com', pathname: '/guild/fami/host-center/', search, }; const window = { atob: (value) => Buffer.from(value, 'base64').toString('binary'), document: { documentElement: { setAttribute: (key, value) => attributes.set(key, value), }, }, dispatchEvent: () => {}, fetch: (url, options = {}) => { requests.push({ url, options }); return Promise.resolve(response({ ok: true })); }, localStorage: { getItem: (key) => storage.get(key) || null, removeItem: (key) => storage.delete(key), setItem: (key, value) => storage.set(key, String(value)), }, location, }; window.window = window; window.URL = URL; window.URLSearchParams = URLSearchParams; const context = vm.createContext({ Buffer, CustomEvent: function CustomEvent() {}, Date, Error, FormData, JSON, Math, Number, Object, Promise, String, URL, URLSearchParams, console, document: window.document, window, }); return { attributes, context, requests, window }; } function runScript(context, relativePath) { const source = fs.readFileSync(path.join(root, relativePath), 'utf8'); vm.runInContext(source, context, { filename: relativePath }); } async function requestAppCode({ product, search = '' }) { const harness = browser(search); if (product) { runScript(harness.context, 'guild/packages/core/product-context.js'); runScript(harness.context, `guild/products/${product}/product.js`); } runScript(harness.context, 'common/api.js'); await harness.window.HyAppAPI.get('/api/v1/test'); return { harness, appCode: harness.requests[0].options.headers['X-App-Code'], }; } async function main() { const legacy = await requestAppCode({ product: '' }); assert.equal(legacy.appCode, 'lalu', 'legacy pages must stay on Lalu'); const fami = await requestAppCode({ product: 'fami' }); assert.equal(fami.appCode, 'fami', 'Fami page default must be isolated'); assert.equal(fami.harness.window.HyAppAPI.getAppCode(), 'fami'); assert.equal(fami.harness.attributes.get('data-hy-product'), 'fami'); const explicit = await requestAppCode({ product: 'fami', search: '?app_code=lalu&env=test', }); assert.equal( explicit.appCode, 'lalu', 'explicit diagnostic tenant must win over the page default' ); const contextHarness = browser('?token=secret&env=test&language=ar&page=8'); runScript(contextHarness.context, 'guild/packages/core/product-context.js'); runScript(contextHarness.context, 'guild/products/fami/product.js'); const linked = new URL( contextHarness.window.HyGuild.product.buildURL('../wallet/', { mode: 'usdt', }) ); assert.equal(linked.searchParams.get('app_code'), 'fami'); assert.equal(linked.searchParams.get('token'), 'secret'); assert.equal(linked.searchParams.get('env'), 'test'); assert.equal(linked.searchParams.get('language'), 'ar'); assert.equal(linked.searchParams.get('mode'), 'usdt'); assert.equal(linked.searchParams.has('page'), false); const rangeHarness = browser(); runScript(rangeHarness.context, 'guild/packages/core/date-range.js'); const ranges = rangeHarness.window.HyGuild.dateRange; assert.equal( JSON.stringify( ranges.create('last_7_days', { now: new Date(2026, 6, 12) }) ), JSON.stringify({ preset: 'last_7_days', startDate: '2026-07-06', endDate: '2026-07-12', }) ); assert.equal( JSON.stringify( ranges.create('last_month', { now: new Date(2026, 0, 15) }) ), JSON.stringify({ preset: 'last_month', startDate: '2025-12-01', endDate: '2025-12-31', }) ); assert.throws( () => ranges.create('custom', { start: '2026-07-12', end: '2026-07-01' }), /Invalid custom date range/ ); console.log('guild product architecture tests passed'); } main().catch((error) => { console.error(error); process.exitCode = 1; });