inbox-compiled-query.test.ts6.6 KBView on GitHub
import { buildInboxCompiledQuery } from '@/modules/threads/lib/inbox-compiled-query';
import type { InboxConfig } from '@/modules/threads/hooks/use-inboxes';

const importantInbox: InboxConfig = {
  id: 'important',
  name: 'Important',
  position: 0,
  system: true,
  rule: { kind: 'all' },
};

const otherInbox: InboxConfig = {
  id: 'other',
  name: 'Other',
  position: 1,
  system: true,
  rule: { kind: 'all' },
};

const defaultInbox: InboxConfig = {
  id: 'default',
  name: 'Inbox',
  position: 0,
  system: true,
  rule: { kind: 'all' },
};

function customInbox(overrides: Partial<InboxConfig>): InboxConfig {
  return {
    id: 'inbox-cal',
    name: 'Calendar',
    position: 2,
    rule: { kind: 'all_of', clauses: [] },
    query: 'from:<email>',
    ...overrides,
  };
}

describe('buildInboxCompiledQuery', () => {
  it('falls back to label:INBOX for default inbox with no exclusions', () => {
    const result = buildInboxCompiledQuery({
      inbox: defaultInbox,
      inboxes: [defaultInbox],
      hasCategoryPersonal: false,
    });
    expect(result.compiledQuery).toBe('label:INBOX');
    expect(result.inboxName).toBe('Inbox');
    expect(result.queryHash).toContain('client-fallback:default');
  });

  it('prefers an explicit inbox.query for custom inboxes', () => {
    const inbox = customInbox({ query: 'from:<email>' });
    const result = buildInboxCompiledQuery({
      inbox,
      inboxes: [defaultInbox, inbox],
      hasCategoryPersonal: false,
    });
    expect(result.compiledQuery).toBe('from:<email>');
  });

  it("prefers a matching inbox's server compiledQuery + queryHash", () => {
    const inbox = customInbox({
      id: 'inbox-x',
      name: 'X',
      query: 'from:fallback',
      compiledQuery: 'from:from-server AND label:INBOX',
      queryHash: 'sha:abc',
    });
    const result = buildInboxCompiledQuery({
      inbox,
      inboxes: [defaultInbox, inbox],
      hasCategoryPersonal: false,
    });
    expect(result.compiledQuery).toBe('from:from-server AND label:INBOX');
    expect(result.queryHash).toBe('sha:abc');
  });

  it('builds the important inbox include clause with category:personal when present', () => {
    const result = buildInboxCompiledQuery({
      inbox: importantInbox,
      inboxes: [importantInbox, otherInbox],
      hasCategoryPersonal: true,
    });
    expect(result.compiledQuery).toContain('label:INBOX');
    expect(result.compiledQuery).toContain('category:personal');
  });

  it('excludes custom inbox queries from the important inbox query', () => {
    const inbox = customInbox({ query: 'from:<email>' });
    const result = buildInboxCompiledQuery({
      inbox: importantInbox,
      inboxes: [importantInbox, otherInbox, inbox],
      hasCategoryPersonal: false,
    });
    expect(result.compiledQuery).toContain('-(');
    expect(result.compiledQuery).toContain('from:<email>');
  });

  it('does NOT subtract a custom inbox query when alsoShowInImportant is true', () => {
    const inbox = customInbox({
      query: 'label:"Cedar/Agent drafts"',
      alsoShowInImportant: true,
    });
    const result = buildInboxCompiledQuery({
      inbox: importantInbox,
      inboxes: [importantInbox, otherInbox, inbox],
      hasCategoryPersonal: false,
    });
    expect(result.compiledQuery).not.toContain('Cedar/Agent drafts');
  });

  it('builds the other inbox query as label:INBOX minus the important signals', () => {
    const result = buildInboxCompiledQuery({
      inbox: otherInbox,
      inboxes: [importantInbox, otherInbox],
      hasCategoryPersonal: true,
    });
    expect(result.compiledQuery.startsWith('label:INBOX')).toBe(true);
    expect(result.compiledQuery).toContain('category:personal');
    expect(result.compiledQuery).toContain('-(');
  });

  it('uses label:IMPORTANT (not category:personal) when importantSignal=gmail_important', () => {
    const result = buildInboxCompiledQuery({
      inbox: importantInbox,
      inboxes: [importantInbox, otherInbox],
      hasCategoryPersonal: true,
      importantSignal: 'gmail_important',
    });
    expect(result.compiledQuery).toContain('label:IMPORTANT');
    expect(result.compiledQuery).not.toContain('category:personal');
  });

  it('subtracts label:IMPORTANT (not category:personal) from the other inbox when signal=gmail_important', () => {
    const result = buildInboxCompiledQuery({
      inbox: otherInbox,
      inboxes: [importantInbox, otherInbox],
      hasCategoryPersonal: true,
      importantSignal: 'gmail_important',
    });
    expect(result.compiledQuery).toContain('label:IMPORTANT');
    expect(result.compiledQuery).not.toContain('category:personal');
    expect(result.compiledQuery.startsWith('label:INBOX')).toBe(true);
    expect(result.compiledQuery).toContain('-(');
  });

  // A CRM-filtered inbox keeps its real rule in `conversationFilter` and carries a
  // bare `label:INBOX` as its mailbox-scope anchor. If the exclusion builder ever
  // treats that anchor as a business-intent clause, the default inbox compiles to
  // `label:INBOX -(label:INBOX)` and the user's whole Inbox renders empty.
  describe('CRM-filtered inboxes are never subtracted from Inbox', () => {
    const pipelineInbox = customInbox({
      id: 'inbox-pipeline',
      name: 'Pipeline',
      query: 'label:INBOX',
      conversationFilter: { filters: { aopIds: ['aop-deals'] } },
    });

    it('does not empty the default inbox, even with alsoShowInImportant off', () => {
      const result = buildInboxCompiledQuery({
        inbox: defaultInbox,
        inboxes: [defaultInbox, { ...pipelineInbox, alsoShowInImportant: false }],
        hasCategoryPersonal: true,
      });
      expect(result.compiledQuery).toBe('label:INBOX');
      expect(result.compiledQuery).not.toContain('-(');
    });

    it('does not empty the important inbox either', () => {
      const result = buildInboxCompiledQuery({
        inbox: importantInbox,
        inboxes: [importantInbox, { ...pipelineInbox, alsoShowInImportant: false }],
        hasCategoryPersonal: true,
      });
      expect(result.compiledQuery).not.toContain('-(label:INBOX)');
    });

    it('still subtracts a PLAIN custom inbox sharing the same query text', () => {
      const plain = customInbox({ id: 'inbox-plain', query: 'label:INBOX' });
      const result = buildInboxCompiledQuery({
        inbox: defaultInbox,
        inboxes: [defaultInbox, { ...plain, alsoShowInImportant: false }],
        hasCategoryPersonal: true,
      });
      // Proves the guard keys off `conversationFilter`, not off the query string.
      expect(result.compiledQuery).toBe('label:INBOX -(label:INBOX)');
    });
  });
});