feat: route createNewSession through federation proxy for remote devices
- Accept remoteId parameter (defaults to '' via remoteId = remoteId || '')
- Compute endpoint: remoteId ? '/api/federation/{remoteId}/sessions' : '/api/sessions'
- Match sessions in poll loop via sessionKey (with fallback to name)
- Pass { remoteId } option to openSession for remote auto-open
- Add 3 tests verifying federation routing, remoteId passthrough, and sessionKey matching
This commit is contained in:
@@ -2039,9 +2039,11 @@ function showFabSessionInput() {
|
||||
* @param {string} name - The session name to create.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function createNewSession(name) {
|
||||
async function createNewSession(name, remoteId) {
|
||||
remoteId = remoteId || '';
|
||||
try {
|
||||
const res = await api('POST', '/api/sessions', { name });
|
||||
var endpoint = remoteId ? '/api/federation/' + encodeURIComponent(remoteId) + '/sessions' : '/api/sessions';
|
||||
const res = await api('POST', endpoint, { name });
|
||||
const data = await res.json();
|
||||
const sessionName = data.name || name;
|
||||
showToast('Creating session \'' + sessionName + '\'…');
|
||||
@@ -2073,6 +2075,9 @@ async function createNewSession(name) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute expectedKey: for remote sessions, use 'remoteId:sessionName' (sessionKey format)
|
||||
var expectedKey = remoteId ? (remoteId + ':' + sessionName) : sessionName;
|
||||
|
||||
// Poll until the session appears in _currentSessions (max 30s, every 2s)
|
||||
var attempts = 0;
|
||||
var maxAttempts = 15;
|
||||
@@ -2080,13 +2085,13 @@ async function createNewSession(name) {
|
||||
attempts++;
|
||||
await pollSessions();
|
||||
var found = _currentSessions && _currentSessions.find(function(s) {
|
||||
return s.name === sessionName;
|
||||
return (s.sessionKey || s.name) === expectedKey;
|
||||
});
|
||||
if (found) {
|
||||
clearInterval(pollForSession);
|
||||
removeLoadingTile();
|
||||
showToast('Session \'' + sessionName + '\' ready');
|
||||
openSession(sessionName);
|
||||
openSession(sessionName, { remoteId: remoteId });
|
||||
} else if (attempts >= maxAttempts) {
|
||||
clearInterval(pollForSession);
|
||||
removeLoadingTile();
|
||||
|
||||
@@ -4231,3 +4231,63 @@ test('showFabSessionInput creates device select when multi_device_enabled with r
|
||||
'showFabSessionInput must call createNewSession with name and remoteId arguments',
|
||||
);
|
||||
});
|
||||
|
||||
// --- createNewSession federation routing (task-4) ---
|
||||
|
||||
test('createNewSession accepts remoteId parameter and routes to federation endpoint', () => {
|
||||
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||
// Extract createNewSession function body
|
||||
const fnStart = source.indexOf('async function createNewSession(');
|
||||
assert.ok(fnStart !== -1, 'createNewSession function must exist');
|
||||
const fnBody = source.substring(fnStart, fnStart + 2000);
|
||||
// Must accept remoteId parameter
|
||||
assert.ok(
|
||||
fnBody.includes('remoteId'),
|
||||
'createNewSession must accept remoteId parameter',
|
||||
);
|
||||
// Must include federation endpoint path
|
||||
assert.ok(
|
||||
fnBody.includes('/api/federation/'),
|
||||
'createNewSession must include /api/federation/ endpoint path for remote routing',
|
||||
);
|
||||
// Must still have local /api/sessions endpoint
|
||||
assert.ok(
|
||||
fnBody.includes('/api/sessions'),
|
||||
'createNewSession must still use /api/sessions for local sessions',
|
||||
);
|
||||
});
|
||||
|
||||
test('createNewSession passes remoteId through to openSession for auto-open', () => {
|
||||
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||
const fnStart = source.indexOf('async function createNewSession(');
|
||||
assert.ok(fnStart !== -1, 'createNewSession function must exist');
|
||||
// Use 3000 chars to cover the full function including the polling interval callback
|
||||
const fnBody = source.substring(fnStart, fnStart + 3000);
|
||||
// Must call openSession with remoteId option
|
||||
assert.ok(
|
||||
fnBody.includes('openSession') && fnBody.includes('remoteId'),
|
||||
'createNewSession must call openSession with remoteId option',
|
||||
);
|
||||
// Must pass remoteId as an option object to openSession
|
||||
assert.ok(
|
||||
fnBody.includes('{ remoteId') || fnBody.includes('{ remoteId:'),
|
||||
'createNewSession must pass remoteId as option object to openSession',
|
||||
);
|
||||
});
|
||||
|
||||
test('createNewSession matches remote sessions by sessionKey in poll loop', () => {
|
||||
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||
const fnStart = source.indexOf('async function createNewSession(');
|
||||
assert.ok(fnStart !== -1, 'createNewSession function must exist');
|
||||
const fnBody = source.substring(fnStart, fnStart + 2000);
|
||||
// Must use sessionKey in the match logic (with fallback to name)
|
||||
assert.ok(
|
||||
fnBody.includes('sessionKey'),
|
||||
'createNewSession poll loop must match remote sessions by sessionKey',
|
||||
);
|
||||
// Must use expectedKey or similar computed key for comparison
|
||||
assert.ok(
|
||||
fnBody.includes('expectedKey') || (fnBody.includes('sessionKey') && fnBody.includes('remoteId')),
|
||||
'createNewSession must compute expected sessionKey for remote session matching',
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user