Collapse filing sync notifications into one batch surface

This commit is contained in:
2026-03-14 19:32:09 -04:00
parent 61b072d31f
commit 0d6c684227
9 changed files with 1148 additions and 280 deletions

View File

@@ -467,6 +467,60 @@ if (process.env.RUN_TASK_WORKFLOW_E2E === '1') {
expect(task.payload.tags).toEqual(['semis', 'ai']);
});
it('reuses the same in-flight filing sync task for repeated same-ticker requests', async () => {
const first = await jsonRequest('POST', '/api/filings/sync', {
ticker: 'NVDA',
limit: 20
});
const second = await jsonRequest('POST', '/api/filings/sync', {
ticker: 'nvda',
limit: 20
});
expect(first.response.status).toBe(200);
expect(second.response.status).toBe(200);
const firstTask = (first.json as { task: { id: string } }).task;
const secondTask = (second.json as { task: { id: string } }).task;
expect(secondTask.id).toBe(firstTask.id);
const tasksResponse = await jsonRequest('GET', '/api/tasks?limit=10&status=queued&status=running');
expect(tasksResponse.response.status).toBe(200);
const tasks = (tasksResponse.json as {
tasks: Array<{ id: string; task_type: string; payload: { ticker?: string } }>;
}).tasks.filter((task) => task.task_type === 'sync_filings' && task.payload.ticker === 'NVDA');
expect(tasks).toHaveLength(1);
});
it('lets different tickers queue independent filing sync tasks', async () => {
const nvda = await jsonRequest('POST', '/api/filings/sync', { ticker: 'NVDA', limit: 20 });
const msft = await jsonRequest('POST', '/api/filings/sync', { ticker: 'MSFT', limit: 20 });
const aapl = await jsonRequest('POST', '/api/filings/sync', { ticker: 'AAPL', limit: 20 });
const ids = [
(nvda.json as { task: { id: string } }).task.id,
(msft.json as { task: { id: string } }).task.id,
(aapl.json as { task: { id: string } }).task.id
];
expect(new Set(ids).size).toBe(3);
const tasksResponse = await jsonRequest('GET', '/api/tasks?limit=10&status=queued&status=running');
expect(tasksResponse.response.status).toBe(200);
const syncTickers = (tasksResponse.json as {
tasks: Array<{ task_type: string; payload: { ticker?: string } }>;
}).tasks
.filter((task) => task.task_type === 'sync_filings')
.map((task) => task.payload.ticker)
.filter((ticker): ticker is string => typeof ticker === 'string');
expect(syncTickers.sort()).toEqual(['AAPL', 'MSFT', 'NVDA']);
});
it('scopes the filings endpoint by ticker while leaving the global endpoint mixed', async () => {
if (!sqliteClient) {
throw new Error('sqlite client not initialized');