221 lines
5.7 KiB
JavaScript
221 lines
5.7 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
require('../src/common');
|
|
|
|
const { createPageAuthToken } = require('../src/request-auth');
|
|
const { registerPlayerOnboardingRoutes } = require('../src/player/onboarding');
|
|
|
|
const originalSharedSecret = process.env.PULSE_SIGNAGE_SHARED_SECRET;
|
|
const originalFetch = global.fetch;
|
|
|
|
test.after(() => {
|
|
if (originalSharedSecret === undefined) {
|
|
delete process.env.PULSE_SIGNAGE_SHARED_SECRET;
|
|
} else {
|
|
process.env.PULSE_SIGNAGE_SHARED_SECRET = originalSharedSecret;
|
|
}
|
|
|
|
global.fetch = originalFetch;
|
|
});
|
|
|
|
function createAppAndHandlers() {
|
|
const handlers = {};
|
|
const app = {
|
|
use() {},
|
|
get(path, ...routeHandlers) {
|
|
handlers[path] = function (req, res) {
|
|
return new Promise((resolve, reject) => {
|
|
const runHandler = (index) => {
|
|
const handler = routeHandlers[index];
|
|
if (!handler) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
let nextCalled = false;
|
|
const next = function () {
|
|
nextCalled = true;
|
|
return runHandler(index + 1);
|
|
};
|
|
|
|
let result;
|
|
try {
|
|
result = handler(req, res, next);
|
|
} catch (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
|
|
Promise.resolve(result).then(function (value) {
|
|
if (!nextCalled) {
|
|
resolve(value);
|
|
}
|
|
}, reject);
|
|
};
|
|
|
|
runHandler(0);
|
|
});
|
|
};
|
|
},
|
|
post() {}
|
|
};
|
|
|
|
return { app, handlers };
|
|
}
|
|
|
|
function createResponse() {
|
|
const headers = {};
|
|
return {
|
|
headers,
|
|
statusCode: 200,
|
|
body: null,
|
|
set(name, value) {
|
|
headers[name] = value;
|
|
return this;
|
|
},
|
|
status(code) {
|
|
this.statusCode = code;
|
|
return this;
|
|
},
|
|
type(value) {
|
|
headers['Content-Type'] = value;
|
|
return this;
|
|
},
|
|
json(value) {
|
|
this.body = value;
|
|
return this;
|
|
},
|
|
send(value) {
|
|
this.body = value;
|
|
return this;
|
|
}
|
|
};
|
|
}
|
|
|
|
function registerThinClientRoutes(fetchImpl) {
|
|
process.env.PULSE_SIGNAGE_SHARED_SECRET = 'bridge-secret';
|
|
global.fetch = fetchImpl;
|
|
|
|
const { app, handlers } = createAppAndHandlers();
|
|
registerPlayerOnboardingRoutes(app, {
|
|
app,
|
|
common: {
|
|
renderPlayerOnboardingLandingPage() {
|
|
return 'landing';
|
|
},
|
|
renderPlayerOnboardingFormPage() {
|
|
return 'form';
|
|
}
|
|
},
|
|
thinClientBaseUrl: 'http://bridge.test',
|
|
playerPublicBaseUrl: 'http://public.test'
|
|
});
|
|
|
|
return handlers;
|
|
}
|
|
|
|
test('remote onboarding status proxies to the bridge and rewrites playerUrl', async () => {
|
|
const fetchCalls = [];
|
|
const handlers = registerThinClientRoutes(async (url, init) => {
|
|
fetchCalls.push({ url, init });
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
get(name) {
|
|
return String(name || '').toLowerCase() === 'content-type' ? 'application/json; charset=utf-8' : null;
|
|
}
|
|
},
|
|
async json() {
|
|
return {
|
|
deviceId: 'device123',
|
|
onboarded: true,
|
|
clientName: 'Lobby Player',
|
|
screenId: 7,
|
|
screenSlug: 'main-screen',
|
|
screenName: 'Main Screen',
|
|
playerUrl: null
|
|
};
|
|
}
|
|
};
|
|
});
|
|
|
|
const token = createPageAuthToken({ scope: 'onboarding', deviceId: 'device123' });
|
|
const res = createResponse();
|
|
|
|
await handlers['/api/onboarding/status']({
|
|
headers: {
|
|
'x-pulse-page-auth': token
|
|
},
|
|
query: {
|
|
deviceId: 'device 123!?'
|
|
},
|
|
socket: {}
|
|
}, res);
|
|
|
|
assert.equal(fetchCalls.length, 1);
|
|
assert.equal(fetchCalls[0].url, 'http://bridge.test/api/onboarding/status?deviceId=device123');
|
|
assert.equal(fetchCalls[0].init.method, 'GET');
|
|
assert.equal(fetchCalls[0].init.headers['x-pulse-page-auth'], token);
|
|
assert.equal(res.statusCode, 200);
|
|
assert.equal(res.body.playerUrl, 'http://public.test/screen/main-screen');
|
|
assert.equal(res.body.screenSlug, 'main-screen');
|
|
});
|
|
|
|
test('remote onboarding status returns 502 when the bridge is unavailable', async () => {
|
|
const handlers = registerThinClientRoutes(async () => {
|
|
return null;
|
|
});
|
|
|
|
const token = createPageAuthToken({ scope: 'player', deviceId: 'device123' });
|
|
const res = createResponse();
|
|
|
|
await handlers['/api/onboarding/status']({
|
|
headers: {
|
|
'x-pulse-page-auth': token
|
|
},
|
|
query: {
|
|
deviceId: 'device123'
|
|
},
|
|
socket: {}
|
|
}, res);
|
|
|
|
assert.equal(res.statusCode, 502);
|
|
assert.deepEqual(res.body, { error: 'Player bridge unavailable.' });
|
|
});
|
|
|
|
test('remote onboarding screens proxy preserves the bridge response body', async () => {
|
|
const fetchCalls = [];
|
|
const handlers = registerThinClientRoutes(async (url, init) => {
|
|
fetchCalls.push({ url, init });
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
get(name) {
|
|
return String(name || '').toLowerCase() === 'content-type' ? 'application/json; charset=utf-8' : null;
|
|
}
|
|
},
|
|
async text() {
|
|
return JSON.stringify({ screens: [{ id: 1, name: 'Lobby', slug: 'lobby' }] });
|
|
}
|
|
};
|
|
});
|
|
|
|
const token = createPageAuthToken({ scope: 'onboarding', deviceId: 'device123' });
|
|
const res = createResponse();
|
|
|
|
await handlers['/api/onboarding/screens']({
|
|
headers: {
|
|
'x-pulse-page-auth': token
|
|
},
|
|
query: {},
|
|
socket: {}
|
|
}, res);
|
|
|
|
assert.equal(fetchCalls.length, 1);
|
|
assert.equal(fetchCalls[0].url, 'http://bridge.test/api/onboarding/screens');
|
|
assert.equal(fetchCalls[0].init.method, 'GET');
|
|
assert.equal(res.statusCode, 200);
|
|
assert.equal(res.headers['Content-Type'], 'application/json; charset=utf-8');
|
|
assert.equal(res.body, JSON.stringify({ screens: [{ id: 1, name: 'Lobby', slug: 'lobby' }] }));
|
|
}); |