feat: closeSession skips DELETE for remote sessions

- Guard DELETE /api/sessions/current with if (!_viewingSourceUrl) check
- Reset _viewingSourceUrl to '' after the conditional DELETE
- For remote sessions, the remote instance doesn't need to know we
  stopped watching - just disconnect WebSocket and update local UI

Tests added:
- closeSession does NOT fire DELETE for remote session (non-empty _viewingSourceUrl)
- closeSession still fires DELETE /api/sessions/current for local session

Task: task-8-close-session-remote-aware
This commit is contained in:
Brian Krabach
2026-03-31 03:53:34 -07:00
parent ac0b84c1d9
commit 28d32d21be
2 changed files with 81 additions and 2 deletions
+5 -2
View File
@@ -1190,8 +1190,11 @@ function closeSession() {
if (window._closeTerminal) window._closeTerminal(); if (window._closeTerminal) window._closeTerminal();
// Fire-and-forget DELETE // Fire-and-forget DELETE — skip for remote sessions (they don't need to know we stopped watching)
api('DELETE', '/api/sessions/current').catch(() => {}); if (!_viewingSourceUrl) {
api('DELETE', '/api/sessions/current').catch(function() {});
}
_viewingSourceUrl = '';
const expanded = $('view-expanded'); const expanded = $('view-expanded');
const overview = $('view-overview'); const overview = $('view-overview');
+76
View File
@@ -1168,6 +1168,82 @@ test('closeSession fires DELETE /api/sessions/current', async () => {
globalThis.document.getElementById = origGetById; globalThis.document.getElementById = origGetById;
}); });
test('closeSession does NOT fire DELETE for remote session (non-empty _viewingSourceUrl)', async () => {
const origFetch = globalThis.fetch;
const origGetById = globalThis.document.getElementById;
const origQS = globalThis.document.querySelector;
const origSetTimeout = globalThis.setTimeout;
// Setup to call openSession with remote sourceUrl
globalThis.fetch = async () => ({ ok: true });
globalThis.document.getElementById = () => ({ textContent: '', style: {}, classList: { remove: () => {}, add: () => {} } });
globalThis.document.querySelector = () => null;
globalThis.setTimeout = () => {};
globalThis.window._openTerminal = () => {};
globalThis.window._closeTerminal = () => {};
// Open a remote session - this sets _viewingSourceUrl = 'http://work:8088'
await app.openSession('remote-sess', { sourceUrl: 'http://work:8088' });
// Restore setTimeout so Promise-based yielding works
globalThis.setTimeout = origSetTimeout;
// Reset fetch tracking
const fetchCalls = [];
globalThis.fetch = async (url, opts) => { fetchCalls.push({ url, opts }); return { ok: true }; };
// Close session
await app.closeSession();
// yield microtask queue for any fire-and-forget calls
await new Promise((r) => setTimeout(r, 0));
const deleteCall = fetchCalls.find((c) => c.url === '/api/sessions/current' && c.opts && c.opts.method === 'DELETE');
assert.ok(!deleteCall, 'closeSession should NOT fire DELETE for remote session');
globalThis.fetch = origFetch;
globalThis.document.getElementById = origGetById;
globalThis.document.querySelector = origQS;
globalThis.setTimeout = origSetTimeout;
});
test('closeSession still fires DELETE /api/sessions/current for local session', async () => {
const origFetch = globalThis.fetch;
const origGetById = globalThis.document.getElementById;
const origQS = globalThis.document.querySelector;
const origSetTimeout = globalThis.setTimeout;
// Setup to call openSession without sourceUrl (local session)
globalThis.fetch = async () => ({ ok: true });
globalThis.document.getElementById = () => ({ textContent: '', style: {}, classList: { remove: () => {}, add: () => {} } });
globalThis.document.querySelector = () => null;
globalThis.setTimeout = () => {};
globalThis.window._openTerminal = () => {};
globalThis.window._closeTerminal = () => {};
// Open a local session - this sets _viewingSourceUrl = ''
await app.openSession('local-sess', {});
// Restore setTimeout so Promise-based yielding works
globalThis.setTimeout = origSetTimeout;
// Reset fetch tracking
const fetchCalls = [];
globalThis.fetch = async (url, opts) => { fetchCalls.push({ url, opts }); return { ok: true }; };
// Close session
await app.closeSession();
// yield microtask queue for fire-and-forget DELETE
await new Promise((r) => setTimeout(r, 0));
const deleteCall = fetchCalls.find((c) => c.url === '/api/sessions/current' && c.opts && c.opts.method === 'DELETE');
assert.ok(deleteCall, 'closeSession should fire DELETE /api/sessions/current for local session');
globalThis.fetch = origFetch;
globalThis.document.getElementById = origGetById;
globalThis.document.querySelector = origQS;
globalThis.setTimeout = origSetTimeout;
});
// ─── Command Palette ───────────────────────────────────────────────────────── // ─── Command Palette ─────────────────────────────────────────────────────────