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.
|
* @param {string} name - The session name to create.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async function createNewSession(name) {
|
async function createNewSession(name, remoteId) {
|
||||||
|
remoteId = remoteId || '';
|
||||||
try {
|
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 data = await res.json();
|
||||||
const sessionName = data.name || name;
|
const sessionName = data.name || name;
|
||||||
showToast('Creating session \'' + sessionName + '\'…');
|
showToast('Creating session \'' + sessionName + '\'…');
|
||||||
@@ -2073,6 +2075,9 @@ async function createNewSession(name) {
|
|||||||
return;
|
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)
|
// Poll until the session appears in _currentSessions (max 30s, every 2s)
|
||||||
var attempts = 0;
|
var attempts = 0;
|
||||||
var maxAttempts = 15;
|
var maxAttempts = 15;
|
||||||
@@ -2080,13 +2085,13 @@ async function createNewSession(name) {
|
|||||||
attempts++;
|
attempts++;
|
||||||
await pollSessions();
|
await pollSessions();
|
||||||
var found = _currentSessions && _currentSessions.find(function(s) {
|
var found = _currentSessions && _currentSessions.find(function(s) {
|
||||||
return s.name === sessionName;
|
return (s.sessionKey || s.name) === expectedKey;
|
||||||
});
|
});
|
||||||
if (found) {
|
if (found) {
|
||||||
clearInterval(pollForSession);
|
clearInterval(pollForSession);
|
||||||
removeLoadingTile();
|
removeLoadingTile();
|
||||||
showToast('Session \'' + sessionName + '\' ready');
|
showToast('Session \'' + sessionName + '\' ready');
|
||||||
openSession(sessionName);
|
openSession(sessionName, { remoteId: remoteId });
|
||||||
} else if (attempts >= maxAttempts) {
|
} else if (attempts >= maxAttempts) {
|
||||||
clearInterval(pollForSession);
|
clearInterval(pollForSession);
|
||||||
removeLoadingTile();
|
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',
|
'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