Make coverage filing sync explicit

This commit is contained in:
2026-03-14 19:54:59 -04:00
parent 0d6c684227
commit 69b45f35e3
5 changed files with 313 additions and 31 deletions

View File

@@ -168,7 +168,7 @@ export async function upsertWatchlistItem(input: {
lastReviewedAt?: string;
}) {
const result = await client.api.watchlist.post(input);
return await unwrapData<{ item: WatchlistItem }>(result, 'Unable to save watchlist item');
return await unwrapData<{ item: WatchlistItem; autoFilingSyncQueued: boolean }>(result, 'Unable to save watchlist item');
}
export async function updateWatchlistItem(id: number, input: {

View File

@@ -500,7 +500,7 @@ export const app = new Elysia({ prefix: '/api' })
}
try {
const { item, created } = await upsertWatchlistItemRecord({
const { item } = await upsertWatchlistItemRecord({
userId: session.user.id,
ticker,
companyName,
@@ -512,14 +512,10 @@ export const app = new Elysia({ prefix: '/api' })
lastReviewedAt
});
const autoFilingSyncQueued = created
? await queueAutoFilingSync(session.user.id, ticker, {
category: item.category,
tags: item.tags
})
: false;
return Response.json({ item, autoFilingSyncQueued });
return Response.json({
item,
autoFilingSyncQueued: false
});
} catch (error) {
return jsonError(asErrorMessage(error, 'Failed to create watchlist item'));
}

View File

@@ -393,7 +393,7 @@ if (process.env.RUN_TASK_WORKFLOW_E2E === '1') {
expect(tasks.every((task) => typeof task.workflow_run_id === 'string' && task.workflow_run_id.length > 0)).toBe(true);
});
it('persists watchlist category and tags and forwards them to auto sync task payload', async () => {
it('persists watchlist category and tags without auto queueing a filing sync task', async () => {
const created = await jsonRequest('POST', '/api/watchlist', {
ticker: 'shop',
companyName: 'Shopify Inc.',
@@ -415,12 +415,12 @@ if (process.env.RUN_TASK_WORKFLOW_E2E === '1') {
expect(createdBody.item.ticker).toBe('SHOP');
expect(createdBody.item.category).toBe('core');
expect(createdBody.item.tags).toEqual(['growth', 'ecommerce']);
expect(createdBody.autoFilingSyncQueued).toBe(true);
expect(createdBody.autoFilingSyncQueued).toBe(false);
const tasksResponse = await jsonRequest('GET', '/api/tasks?limit=5');
expect(tasksResponse.response.status).toBe(200);
const task = (tasksResponse.json as {
const syncTasks = (tasksResponse.json as {
tasks: Array<{
task_type: string;
payload: {
@@ -430,13 +430,86 @@ if (process.env.RUN_TASK_WORKFLOW_E2E === '1') {
limit?: number;
};
}>;
}).tasks.find((entry) => entry.task_type === 'sync_filings');
}).tasks.filter((entry) => entry.task_type === 'sync_filings');
expect(task).toBeTruthy();
expect(task?.payload.ticker).toBe('SHOP');
expect(task?.payload.limit).toBe(20);
expect(task?.payload.category).toBe('core');
expect(task?.payload.tags).toEqual(['growth', 'ecommerce']);
expect(syncTasks).toHaveLength(0);
});
it('does not queue a filing sync task when coverage metadata is edited', async () => {
const created = await jsonRequest('POST', '/api/watchlist', {
ticker: 'amd',
companyName: 'Advanced Micro Devices, Inc.',
sector: 'Technology',
category: 'watch',
tags: ['semis']
});
expect(created.response.status).toBe(200);
const item = (created.json as {
item: { id: number };
}).item;
const updated = await jsonRequest('PATCH', `/api/watchlist/${item.id}`, {
category: 'core',
tags: ['semis', 'ai']
});
expect(updated.response.status).toBe(200);
const tasksResponse = await jsonRequest('GET', '/api/tasks?limit=5');
expect(tasksResponse.response.status).toBe(200);
const syncTasks = (tasksResponse.json as {
tasks: Array<{ task_type: string }>;
}).tasks.filter((entry) => entry.task_type === 'sync_filings');
expect(syncTasks).toHaveLength(0);
});
it('forwards watchlist metadata when filing sync is started explicitly', async () => {
const created = await jsonRequest('POST', '/api/watchlist', {
ticker: 'shop',
companyName: 'Shopify Inc.',
sector: 'Technology',
category: 'core',
tags: ['growth', 'ecommerce', 'growth', ' ']
});
expect(created.response.status).toBe(200);
const createdBody = created.json as {
item: {
ticker: string;
category: string | null;
tags: string[];
};
};
const sync = await jsonRequest('POST', '/api/filings/sync', {
ticker: createdBody.item.ticker,
limit: 20,
category: createdBody.item.category,
tags: createdBody.item.tags
});
expect(sync.response.status).toBe(200);
const task = (sync.json as {
task: {
task_type: string;
payload: {
ticker: string;
limit: number;
category?: string;
tags?: string[];
};
};
}).task;
expect(task.task_type).toBe('sync_filings');
expect(task.payload.ticker).toBe('SHOP');
expect(task.payload.limit).toBe(20);
expect(task.payload.category).toBe('core');
expect(task.payload.tags).toEqual(['growth', 'ecommerce']);
});
it('accepts category and comma-separated tags on manual filings sync payload', async () => {