fix(media): keep self-host media loading after restarts (#3283)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-08 17:28:18 +02:00
1 parent bd56174870
commit 46a356cc40
59 files changed
+1238 -499

No files matched your search

@@ -3,12 +3,12 @@
import assert from 'node:assert/strict';
import {Buffer} from 'node:buffer';
import {Readable} from 'node:stream';
import {describe, test} from 'node:test';
import {describe, mock, test} from 'node:test';
import {installElectronStub} from './LocalAppTestSupport.test.mjs';
installElectronStub();
const {DesktopLocalAppProxyClient} = await import('./LocalAppProxyClient.ts');
const {DesktopLocalAppProxyClient, LocalAppProxyTraffic} = await import('./LocalAppProxyClient.ts');
const PROXY_MAX_CONCURRENT_REQUESTS = 32;
const PROXY_MAX_QUEUED_REQUESTS = 4096;
@@ -93,7 +93,7 @@ function readAvatar(client, index, signal) {
describe('the local app proxy bounds its own concurrency instead of overrunning outbound HTTP', () => {
test('every avatar of a fully rendered chat resolves rather than tripping the outbound capacity cap', async () => {
const {outboundHTTP, state} = createOutboundHTTP();
const client = new DesktopLocalAppProxyClient({outboundHTTP});
const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const controller = new AbortController();
const fetches = [];
@@ -113,7 +113,7 @@ describe('the local app proxy bounds its own concurrency instead of overrunning
test('no more requests are in flight at any instant than the proxy concurrency limit', async () => {
const {outboundHTTP, state} = createOutboundHTTP();
const client = new DesktopLocalAppProxyClient({outboundHTTP});
const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const controller = new AbortController();
const fetches = [];
@@ -130,7 +130,7 @@ describe('the local app proxy bounds its own concurrency instead of overrunning
test('an aborted waiter rejects with the abort reason and leaves its queue slot to the next waiter', async () => {
const {outboundHTTP, state} = createOutboundHTTP();
const client = new DesktopLocalAppProxyClient({outboundHTTP});
const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const held = new AbortController();
const holding = [];
@@ -161,7 +161,7 @@ describe('the local app proxy bounds its own concurrency instead of overrunning
test('the queue refuses a request past its bound rather than growing without limit', async () => {
const {outboundHTTP, state} = createOutboundHTTP();
const client = new DesktopLocalAppProxyClient({outboundHTTP});
const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const controller = new AbortController();
const accepted = [];
@@ -178,3 +178,114 @@ describe('the local app proxy bounds its own concurrency instead of overrunning
assert.equal(settled.length, PROXY_MAX_CONCURRENT_REQUESTS + PROXY_MAX_QUEUED_REQUESTS);
});
});
const RESOURCE_HEADERS_DEADLINE_MS = 30_000;
function createSilentUpstream() {
const state = {started: [], aborted: 0};
const outboundHTTP = {
request: (request) =>
new Promise((resolve, reject) => {
const entry = {
url: request.url,
serviceName: request.serviceName,
answer: () =>
resolve({
headers: {'content-type': 'image/webp'},
message: Readable.from([Buffer.from('avatar-bytes')]),
status: 200,
statusText: 'OK',
url: new URL(request.url),
}),
};
state.started.push(entry);
request.signal.addEventListener(
'abort',
() => {
state.aborted += 1;
reject(request.signal.reason);
},
{once: true},
);
}),
};
return {outboundHTTP, state};
}
function apiRequest(signal) {
return {...avatarRequest(0, signal), targetURL: 'https://api.fluxer.app/v1/users/@me'};
}
describe('media that a slow or silent self-host never answers cannot wedge the local app proxy', () => {
test('avatars dropped while their upstream sends no headers free their permits at the headers deadline', async () => {
mock.timers.enable({apis: ['setTimeout']});
try {
const {outboundHTTP, state} = createSilentUpstream();
const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const renderer = new AbortController();
const dropped = [];
for (let index = 0; index < PROXY_MAX_CONCURRENT_REQUESTS; index += 1) {
const fetch = client.fetch(avatarRequest(index, renderer.signal));
fetch.catch(() => {});
dropped.push(fetch);
}
await tick();
const fresh = client.fetch(avatarRequest(1000, renderer.signal));
await tick();
assert.equal(state.started.length, PROXY_MAX_CONCURRENT_REQUESTS);
mock.timers.tick(RESOURCE_HEADERS_DEADLINE_MS);
await Promise.allSettled(dropped);
await tick();
assert.equal(state.aborted, PROXY_MAX_CONCURRENT_REQUESTS);
assert.equal(state.started.length, PROXY_MAX_CONCURRENT_REQUESTS + 1);
state.started.at(-1).answer();
const response = await fresh;
assert.equal(response.status, 200);
assert.equal(await response.text(), 'avatar-bytes');
} finally {
mock.timers.reset();
}
});
test('API requests keep flowing while every media permit is held by a silent upstream', async () => {
const {outboundHTTP, state} = createSilentUpstream();
const media = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const api = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.API});
const renderer = new AbortController();
for (let index = 0; index < PROXY_MAX_CONCURRENT_REQUESTS * 2; index += 1) {
media.fetch(avatarRequest(index, renderer.signal)).catch(() => {});
}
await tick();
const apiCall = api.fetch(apiRequest(renderer.signal));
await tick();
const started = state.started.at(-1);
assert.equal(started.url, 'https://api.fluxer.app/v1/users/@me');
assert.equal(started.serviceName, LocalAppProxyTraffic.API);
started.answer();
assert.equal((await apiCall).status, 200);
renderer.abort();
});
test('the newest queued media request is admitted first so what is on screen loads before what scrolled away', async () => {
const {outboundHTTP, state} = createSilentUpstream();
const client = new DesktopLocalAppProxyClient({outboundHTTP, traffic: LocalAppProxyTraffic.REMOTE_RESOURCE});
const renderer = new AbortController();
const fetches = [];
for (let index = 0; index < PROXY_MAX_CONCURRENT_REQUESTS + 3; index += 1) {
const fetch = client.fetch(avatarRequest(index, renderer.signal));
fetch.catch(() => {});
fetches.push(fetch);
}
await tick();
state.started[0].answer();
await (await fetches[0]).text();
await tick();
assert.equal(
state.started.at(-1).url,
`https://media.fluxer.app/avatars/${PROXY_MAX_CONCURRENT_REQUESTS + 2}.webp`,
);
renderer.abort();
});
});