dealPickerScope.test.ts2.1 KBView on GitHub import { readFileSync } from 'node:fs';
import { join } from 'node:path';
/**
* "Search a deal…" must search deals.
*
* `crm.searchConversationsMinimal` ILIKEs the name of EVERY conversation the user owns, ordered by
* `lastUpdatedAt`. Cedar mints a conversation per unrecognised correspondent, so on a real account
* that set is mostly not deals: one reporting account had 117 conversations of which 6 sat under
* the Deals AOP, and typing "gmail" into the task picker returned a column of
* `<email>` rows — recruiters, vendor notifications, and unclassified senders.
*
* The route takes `dealsOnly`, which narrows to the AOPs the user actually works. The assertion is
* structural because the alternative is standing up tRPC, auth and a database to prove that one
* literal is still in one object — and it is exactly the sort of argument a later refactor drops
* without breaking a type or a render.
*/
describe('task deal pickers are scoped to deals', () => {
const read = (relative: string) =>
readFileSync(join(__dirname, '..', '..', '..', 'modules', 'userTasks', relative), 'utf8');
const PICKERS = ['components/NewTaskDialog.tsx', 'components/TaskCommandBar.tsx'];
it.each(PICKERS)('%s asks for dealsOnly', (relative) => {
const source = read(relative);
const call = source.slice(source.indexOf('searchConversationsMinimal'));
expect(call).toContain('dealsOnly: true');
});
it.each(PICKERS)('%s says "deal", so the label matches the scope', (relative) => {
expect(read(relative)).toContain('Search a deal');
});
it('the route still accepts the argument', () => {
const route = readFileSync(
join(__dirname, '..', '..', '..', '..', 'server', 'src', 'trpc', 'routes', 'crm.ts'),
'utf8',
);
expect(route).toContain('dealsOnly: z.boolean()');
// Narrowing must be skipped when the caller already named AOPs, or an explicit filter would be
// silently widened back to "everything workable".
expect(route).toMatch(/input\.dealsOnly && \(!resolvedAopIds \|\| resolvedAopIds\.length === 0\)/);
});
});