feat: add tagSessions and mergeSources for multi-source parallel polling
These two functions enable multi-source session federation:
- tagSessions(sessions, deviceName, sourceUrl): tags each session with
deviceName, sourceUrl, and sessionKey (format: sourceUrl::name),
returns new objects without mutating originals
- mergeSources(results): takes [{source, sessions}] objects, tags each
source's sessions and concatenates into a single flat array
Both functions are exported in module.exports.
6 new tests added covering all specified behaviors.
This commit is contained in:
@@ -1663,6 +1663,44 @@ function bindStaticEventListeners() {
|
|||||||
|
|
||||||
// ─── Test-only helpers ────────────────────────────────────────────────────────
|
// ─── Test-only helpers ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// ─── Multi-source parallel polling ─────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag each session in the array with deviceName, sourceUrl, and sessionKey.
|
||||||
|
* Returns new session objects; does NOT mutate originals.
|
||||||
|
* sessionKey format: sourceUrl + '::' + name
|
||||||
|
* @param {object[]} sessions
|
||||||
|
* @param {string} deviceName
|
||||||
|
* @param {string} sourceUrl
|
||||||
|
* @returns {object[]}
|
||||||
|
*/
|
||||||
|
function tagSessions(sessions, deviceName, sourceUrl) {
|
||||||
|
return (sessions || []).map(function(s) {
|
||||||
|
return Object.assign({}, s, {
|
||||||
|
deviceName: deviceName,
|
||||||
|
sourceUrl: sourceUrl,
|
||||||
|
sessionKey: sourceUrl + '::' + (s.name || ''),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge sessions from multiple sources into a single flat array.
|
||||||
|
* Each result is an object with {source, sessions}.
|
||||||
|
* Tags each source's sessions with deviceName/sourceUrl/sessionKey.
|
||||||
|
* @param {Array<{source: {name: string, url: string}, sessions: object[]}>} results
|
||||||
|
* @returns {object[]}
|
||||||
|
*/
|
||||||
|
function mergeSources(results) {
|
||||||
|
var all = [];
|
||||||
|
for (var i = 0; i < results.length; i++) {
|
||||||
|
var r = results[i];
|
||||||
|
var tagged = tagSessions(r.sessions, r.source.name, r.source.url);
|
||||||
|
for (var j = 0; j < tagged.length; j++) { all.push(tagged[j]); }
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
/** Test-only: set _currentSessions directly. */
|
/** Test-only: set _currentSessions directly. */
|
||||||
function _setCurrentSessions(sessions) {
|
function _setCurrentSessions(sessions) {
|
||||||
_currentSessions = sessions;
|
_currentSessions = sessions;
|
||||||
@@ -1778,6 +1816,9 @@ if (typeof module !== 'undefined' && module.exports) {
|
|||||||
createNewSession,
|
createNewSession,
|
||||||
// Kill session
|
// Kill session
|
||||||
killSession,
|
killSession,
|
||||||
|
// Multi-source parallel polling
|
||||||
|
tagSessions,
|
||||||
|
mergeSources,
|
||||||
// Test-only helpers
|
// Test-only helpers
|
||||||
_setCurrentSessions,
|
_setCurrentSessions,
|
||||||
_setViewMode,
|
_setViewMode,
|
||||||
|
|||||||
@@ -2072,4 +2072,65 @@ test('buildSources strips trailing slash from remote URLs', () => {
|
|||||||
assert.strictEqual(sources[2].url, 'https://server2.example.com', 'multiple trailing slashes should be stripped from remote URL');
|
assert.strictEqual(sources[2].url, 'https://server2.example.com', 'multiple trailing slashes should be stripped from remote URL');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- tagSessions ---
|
||||||
|
|
||||||
|
test('tagSessions adds deviceName and sourceUrl to each session', () => {
|
||||||
|
const sessions = [{ name: 'work' }, { name: 'play' }];
|
||||||
|
const result = app.tagSessions(sessions, 'Laptop', 'https://host.example.com');
|
||||||
|
assert.strictEqual(result[0].deviceName, 'Laptop', 'first session should have deviceName set');
|
||||||
|
assert.strictEqual(result[0].sourceUrl, 'https://host.example.com', 'first session should have sourceUrl set');
|
||||||
|
assert.strictEqual(result[1].deviceName, 'Laptop', 'second session should have deviceName set');
|
||||||
|
assert.strictEqual(result[1].sourceUrl, 'https://host.example.com', 'second session should have sourceUrl set');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tagSessions adds sessionKey formatted as sourceUrl::name', () => {
|
||||||
|
const sessions = [{ name: 'my-session' }];
|
||||||
|
const result = app.tagSessions(sessions, 'Laptop', 'https://host.example.com');
|
||||||
|
assert.strictEqual(result[0].sessionKey, 'https://host.example.com::my-session', 'sessionKey should be sourceUrl::name');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tagSessions handles empty sessions input (returns empty array)', () => {
|
||||||
|
const result = app.tagSessions([], 'Laptop', 'https://host.example.com');
|
||||||
|
assert.deepStrictEqual(result, [], 'tagSessions should return empty array for empty input');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tagSessions does not mutate the original session objects', () => {
|
||||||
|
const original = { name: 'work' };
|
||||||
|
const sessions = [original];
|
||||||
|
const result = app.tagSessions(sessions, 'Laptop', 'https://host.example.com');
|
||||||
|
assert.ok(!('deviceName' in original), 'original session should not be mutated');
|
||||||
|
assert.ok(!('sourceUrl' in original), 'original session should not have sourceUrl added');
|
||||||
|
assert.ok(!('sessionKey' in original), 'original session should not have sessionKey added');
|
||||||
|
assert.notStrictEqual(result[0], original, 'returned session should be a new object, not the original');
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- mergeSources ---
|
||||||
|
|
||||||
|
test('mergeSources combines sessions from multiple sources with correct sessionKeys', () => {
|
||||||
|
const results = [
|
||||||
|
{
|
||||||
|
source: { name: 'Laptop', url: '' },
|
||||||
|
sessions: [{ name: 'local-session' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: { name: 'Server', url: 'https://server.example.com' },
|
||||||
|
sessions: [{ name: 'remote-session' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const merged = app.mergeSources(results);
|
||||||
|
assert.strictEqual(merged.length, 2, 'merged array should have 2 sessions total');
|
||||||
|
const local = merged.find((s) => s.name === 'local-session');
|
||||||
|
const remote = merged.find((s) => s.name === 'remote-session');
|
||||||
|
assert.ok(local, 'local-session should be present');
|
||||||
|
assert.ok(remote, 'remote-session should be present');
|
||||||
|
assert.strictEqual(local.sessionKey, '::local-session', 'local session key should be ::local-session');
|
||||||
|
assert.strictEqual(remote.sessionKey, 'https://server.example.com::remote-session', 'remote session key should include url');
|
||||||
|
assert.strictEqual(remote.deviceName, 'Server', 'remote session should have correct deviceName');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mergeSources returns empty array for empty input', () => {
|
||||||
|
const result = app.mergeSources([]);
|
||||||
|
assert.deepStrictEqual(result, [], 'mergeSources should return empty array for empty input');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user