'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');
const Database = require('better-sqlite3');
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

const { applyBaseSchema, applyMigrations } = require('../lib/db/schema');
const {
  isRejectionEmail,
  matchRejectionEmail,
  REJECTION_EMAIL_SYNC_LOG,
  syncRejectionEmails,
} = require('../lib/rejection-email-sync');

function createDb() {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'job-search-rejection-sync-'));
  const db = new Database(path.join(dir, 'jobs.db'));
  applyBaseSchema(db);
  applyMigrations(db);
  return db;
}

function insertJob(db, job) {
  db.prepare(`
    INSERT INTO jobs (
      id, title, company, url, platform, location, posted_at, description,
      status, applied_at, stage
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(
    job.id,
    job.title,
    job.company,
    job.url,
    job.platform || 'Greenhouse',
    job.location || 'Remote',
    job.posted_at || '2026-04-01T00:00:00Z',
    job.description || 'Build infrastructure',
    job.status || 'applied',
    job.applied_at || '2026-04-02T00:00:00Z',
    job.stage || 'applied'
  );
}

function makeMessage({ uid, subject, fromAddress, receivedAt, raw }) {
  return {
    uid,
    subject,
    fromAddress,
    receivedAt: receivedAt || '2026-04-14T18:33:58.000Z',
    messageId: `<message-${uid}@example.com>`,
    raw,
  };
}

function makeFetcher(messages, uidValidity = '777') {
  return async ({ lastUid }) => ({
    uidValidity,
    lastUid: messages.length ? messages[messages.length - 1].uid : lastUid,
    messages: messages.filter((message) => lastUid == null || message.uid > Number(lastUid)),
  });
}

describe('rejection email sync', () => {
  it('detects rejection language in raw email text', () => {
    const message = makeMessage({
      uid: 1,
      subject: 'Important information about your application to Gather AI',
      fromAddress: 'careers@gather.ai',
      raw: `
        Content-Type: text/html; charset="utf-8"

        <p>Thank you for applying for the Platform Engineer role at Gather AI.</p>
        <p>Unfortunately, we have decided not to proceed with your candidacy at this time.</p>
      `,
    });

    assert.equal(isRejectionEmail(message), true);
  });

  it('detects newer rejection template phrases', () => {
    const examples = [
      'We decided to extend an offer to another candidate and are not continuing with any new interviews.',
      'We made the decision to not move forward with your application at this time.',
      'We decided to proceed with candidates whose backgrounds more closely align with this role.',
      'Due to limited headcount, the team is moving forward with other candidates for this role.',
      'The role has recently been paused while we reassess hiring plans.',
      'Thank you for your interest, but this opening has now been filled.',
      'This requisition has already been placed on hold.',
    ];

    for (const [index, raw] of examples.entries()) {
      assert.equal(isRejectionEmail(makeMessage({
        uid: index + 100,
        subject: 'Application update',
        fromAddress: 'no-reply@example.com',
        raw,
      })), true);
    }
  });

  it('detects base64-encoded html rejection templates', () => {
    const html = `
      <div>Hi Jake,</div>
      <div>Thank you for your interest in the Senior Software Engineer, Site Reliability role at Babylist.</div>
      <div>After reviewing your application, we've decided to move forward with other candidates at this time.</div>
      <div>The Babylist Talent Team</div>
    `;
    const message = makeMessage({
      uid: 101,
      subject: 'Thank You for Your Interest in Babylist',
      fromAddress: 'no-reply@appreview.gem.com',
      raw: `
        Content-Type: multipart/alternative; boundary="part"

        --part
        Content-Type: text/html; charset="utf-8"
        Content-Transfer-Encoding: base64

        ${Buffer.from(html, 'utf8').toString('base64')}
        --part--
      `,
    });

    assert.equal(isRejectionEmail(message), true);
  });

  it('matches the Babylist base64 rejection to the applied Greenhouse job', () => {
    const db = createDb();
    insertJob(db, {
      id: 'builtin-1',
      company: 'Babylist',
      title: 'Senior Software Engineer, Site Reliability',
      url: 'https://job-boards.greenhouse.io/babylist/jobs/5690273004?gh_src=eeef46564us',
      status: 'archived',
    });
    insertJob(db, {
      id: 'greenhouse-1',
      company: 'babylist',
      title: 'Senior Software Engineer, Site Reliability',
      url: 'https://job-boards.greenhouse.io/babylist/jobs/5690273004',
      status: 'applied',
    });

    const html = `
      <div>Hi Jake,</div>
      <div>Thank you for your interest in the Senior Software Engineer, Site Reliability role at Babylist and for taking the time to apply.</div>
      <div>After reviewing your application, we've decided to move forward with other candidates at this time.</div>
      <div>The Babylist Talent Team</div>
    `;
    const match = matchRejectionEmail(db, makeMessage({
      uid: 102,
      subject: 'Thank You for Your Interest in Babylist',
      fromAddress: 'no-reply@appreview.gem.com',
      raw: `
        Content-Type: multipart/alternative; boundary="part"

        --part
        Content-Type: text/html; charset="utf-8"
        Content-Transfer-Encoding: base64

        ${Buffer.from(html, 'utf8').toString('base64')}
        --part--
      `,
    }));

    assert.equal(match.job.id, 'greenhouse-1');
    assert.equal(match.reason, 'company_title_match');
  });

  it('matches and applies a rejection when company and title are both present', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Gather AI',
      title: 'Platform Engineer',
      url: 'https://job-boards.greenhouse.io/gather/jobs/12345',
    });

    const summary = await syncRejectionEmails(db, {
      skipTrash: true,
      fetchMessages: makeFetcher([
        makeMessage({
          uid: 11,
          subject: 'Important information about your application to Gather AI',
          fromAddress: 'careers@gather.ai',
          raw: `
            Thank you for applying for the Platform Engineer role at Gather AI.
            Unfortunately, we have decided not to proceed with your candidacy at this time.
          `,
        }),
      ]),
    });

    const job = db.prepare(`
      SELECT status, stage, rejected_from_stage, rejected_at
      FROM jobs
      WHERE id = 'job-1'
    `).get();
    const events = db.prepare(`
      SELECT event_type, from_value, to_value
      FROM events
      WHERE job_id = 'job-1'
    `).all();
    const emailLog = db.prepare(`
      SELECT matched_job_id, match_status, reason
      FROM rejection_email_log
      WHERE uid = 11
    `).get();

    assert.deepEqual(summary, {
      fetched: 1,
      candidates: 1,
      applied: 1,
      dryRun: 0,
      ignored: 0,
      unmatched: 0,
    });
    assert.equal(job.status, 'rejected');
    assert.equal(job.stage, 'rejected');
    assert.equal(job.rejected_from_stage, 'applied');
    assert.ok(job.rejected_at);
    assert.equal(events.length, 1);
    assert.deepEqual(events[0], {
      event_type: 'stage_change',
      from_value: 'applied',
      to_value: 'rejected',
    });
    assert.deepEqual(emailLog, {
      matched_job_id: 'job-1',
      match_status: 'applied',
      reason: 'single_active_company_job',
    });
  });

  it('does not auto-match a company-only rejection when multiple active jobs share that company', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Railway',
      title: 'Platform Engineer',
      url: 'https://jobs.ashbyhq.com/railway/1111',
    });
    insertJob(db, {
      id: 'job-2',
      company: 'Railway',
      title: 'Site Reliability Engineer',
      url: 'https://jobs.ashbyhq.com/railway/2222',
    });

    const summary = await syncRejectionEmails(db, {
      skipTrash: true,
      fetchMessages: makeFetcher([
        makeMessage({
          uid: 12,
          subject: 'Follow-up from Railway',
          fromAddress: 'no-reply@ashbyhq.com',
          raw: `
            Hey there,
            Our team was able to have a look at your resume.
            Unfortunately, we will not be moving forward with the process.
          `,
        }),
      ]),
    });

    const jobs = db.prepare(`
      SELECT id, status, stage
      FROM jobs
      ORDER BY id
    `).all();
    const emailLog = db.prepare(`
      SELECT matched_job_id, match_status, reason
      FROM rejection_email_log
      WHERE uid = 12
    `).get();

    assert.deepEqual(summary, {
      fetched: 1,
      candidates: 1,
      applied: 0,
      dryRun: 0,
      ignored: 0,
      unmatched: 1,
    });
    assert.deepEqual(jobs, [
      { id: 'job-1', status: 'applied', stage: 'applied' },
      { id: 'job-2', status: 'applied', stage: 'applied' },
    ]);
    assert.deepEqual(emailLog, {
      matched_job_id: null,
      match_status: 'unmatched',
      reason: 'ambiguous_company_match',
    });
  });

  it('skips already-seen messages on later sync runs', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Gather AI',
      title: 'Platform Engineer',
      url: 'https://job-boards.greenhouse.io/gather/jobs/12345',
    });

    const fetchMessages = makeFetcher([
      makeMessage({
        uid: 13,
        subject: 'Important information about your application to Gather AI',
        fromAddress: 'careers@gather.ai',
        raw: `
          Thank you for applying for the Platform Engineer role at Gather AI.
          Unfortunately, we have decided not to proceed with your candidacy at this time.
        `,
      }),
    ]);

    const first = await syncRejectionEmails(db, { fetchMessages, dryRun: true, skipTrash: true });
    const second = await syncRejectionEmails(db, { fetchMessages, dryRun: true, skipTrash: true });
    const rows = db.prepare('SELECT COUNT(*) AS n FROM rejection_email_log').get();

    assert.equal(first.dryRun, 1);
    assert.deepEqual(second, {
      fetched: 0,
      candidates: 0,
      applied: 0,
      dryRun: 0,
      ignored: 0,
      unmatched: 0,
    });
    assert.equal(rows.n, 1);
  });

  it('can disambiguate same-company jobs when the exact title is present', () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Railway',
      title: 'Platform Engineer',
      url: 'https://jobs.ashbyhq.com/railway/1111',
    });
    insertJob(db, {
      id: 'job-2',
      company: 'Railway',
      title: 'Site Reliability Engineer',
      url: 'https://jobs.ashbyhq.com/railway/2222',
    });

    const match = matchRejectionEmail(db, makeMessage({
      uid: 14,
      subject: 'Follow-up from Railway',
      fromAddress: 'no-reply@ashbyhq.com',
      raw: `
        Thanks for your interest in Railway.
        Unfortunately, we will not be moving forward with the Site Reliability Engineer role.
      `,
    }));

    assert.equal(match.job.id, 'job-2');
    assert.equal(match.reason, 'company_title_match');
  });

  it('matches compact company names against spaced email text when there is one active job', () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'gatherai',
      title: 'Platform Engineer',
      url: 'https://job-boards.greenhouse.io/gather/jobs/12345',
    });

    const match = matchRejectionEmail(db, makeMessage({
      uid: 15,
      subject: 'Important information about your application to Gather AI',
      fromAddress: 'careers@gather.ai',
      raw: `
        Thank you for applying for the Platform Engineer role at Gather AI.
        Unfortunately, we have decided not to proceed with your candidacy at this time.
      `,
    }));

    assert.equal(match.job.id, 'job-1');
    assert.equal(match.reason, 'single_active_company_job');
  });

  it('uses the daily rejection-sync log path', () => {
    assert.equal(path.dirname(REJECTION_EMAIL_SYNC_LOG), path.join(__dirname, '..', 'logs', 'rejection-sync'));
    assert.match(path.basename(REJECTION_EMAIL_SYNC_LOG), /^\d{8}\.log$/);
  });

  it('records already-rejected matching jobs as ignored instead of unmatched', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'blinkhealth',
      title: 'Staff Site Reliability Engineer',
      url: 'https://job-boards.greenhouse.io/blinkhealth/jobs/12345',
      status: 'rejected',
      stage: 'rejected',
    });

    const summary = await syncRejectionEmails(db, {
      skipTrash: true,
      fetchMessages: makeFetcher([
        makeMessage({
          uid: 16,
          subject: 'Important information about your application to Blink Health: Staff Site Reliability Engineer',
          fromAddress: 'no-reply@greenhouse.io',
          raw: `
            Thank you for applying to Blink Health.
            Unfortunately, we have decided not to move forward.
          `,
        }),
      ]),
    });

    const emailLog = db.prepare(`
      SELECT matched_job_id, match_status, reason
      FROM rejection_email_log
      WHERE uid = 16
    `).get();

    assert.equal(summary.ignored, 1);
    assert.equal(summary.unmatched, 0);
    assert.deepEqual(emailLog, {
      matched_job_id: 'job-1',
      match_status: 'ignored',
      reason: 'already_rejected',
    });
  });

  it('updates a previous unmatched email log when a replay finds a match', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'PitchBookData',
      title: 'Sr. Site Reliability Engineer',
      url: 'https://job-boards.greenhouse.io/pitchbookdata/jobs/2222',
    });

    db.prepare(`
      INSERT INTO rejection_email_log (
        mailbox, uid_validity, uid, message_id, subject, match_status, reason
      ) VALUES (?, ?, ?, ?, ?, ?, ?)
    `).run('[Gmail]/All Mail', '777', 19, '<old@example.com>', 'Old unmatched row', 'unmatched', 'no_company_match');

    await syncRejectionEmails(db, {
      skipTrash: true,
      fetchMessages: makeFetcher([
        makeMessage({
          uid: 19,
          subject: 'Your application for Sr. Site Reliability Engineer at PitchBook',
          fromAddress: 'no-reply@pitchbook.com',
          raw: `
            Thank you for applying to PitchBook.
            After careful consideration, we have decided to move forward with other candidates
            for the Sr. Site Reliability Engineer role.
          `,
        }),
      ]),
    });

    const emailLog = db.prepare(`
      SELECT subject, company_hint, title_hint, matched_job_id, match_status, reason
      FROM rejection_email_log
      WHERE uid = 19
    `).get();

    assert.deepEqual(emailLog, {
      subject: 'Your application for Sr. Site Reliability Engineer at PitchBook',
      company_hint: 'PitchBookData',
      title_hint: 'Sr. Site Reliability Engineer',
      matched_job_id: 'job-1',
      match_status: 'applied',
      reason: 'single_active_company_job',
    });
  });

  it('classifies messages without writing jobs, logs, or sync metadata', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Acme',
      title: 'Platform Engineer',
      url: 'https://job-boards.greenhouse.io/acme/jobs/12345',
    });

    const summary = await syncRejectionEmails(db, {
      classifyOnly: true,
      skipTrash: true,
      fetchMessages: makeFetcher([
        makeMessage({
          uid: 23,
          subject: 'Update from Acme',
          fromAddress: 'careers@acme.example',
          raw: 'Unfortunately, we are not moving forward with your application.',
        }),
      ]),
    });

    const job = db.prepare("SELECT status, stage FROM jobs WHERE id = 'job-1'").get();
    const emailLogs = db.prepare('SELECT COUNT(*) AS n FROM rejection_email_log').get();
    const metadata = db.prepare("SELECT COUNT(*) AS n FROM metadata WHERE key LIKE 'rejection_email_%'").get();

    assert.equal(summary.dryRun, 1);
    assert.deepEqual(job, { status: 'applied', stage: 'applied' });
    assert.equal(emailLogs.n, 0);
    assert.equal(metadata.n, 0);
  });

  it('replays old messages without advancing normal sync metadata', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Acme',
      title: 'Platform Engineer',
      url: 'https://job-boards.greenhouse.io/acme/jobs/12345',
    });
    db.prepare("INSERT INTO metadata (key, value) VALUES ('rejection_email_last_uid', '99')").run();
    db.prepare("INSERT INTO metadata (key, value) VALUES ('rejection_email_uid_validity', '777')").run();
    db.prepare(`
      INSERT INTO rejection_email_log (
        mailbox, uid_validity, uid, message_id, subject, match_status, reason
      ) VALUES (?, ?, ?, ?, ?, ?, ?)
    `).run('[Gmail]/All Mail', '777', 24, '<old@example.com>', 'Old unmatched row', 'unmatched', 'no_company_match');

    let observedLastUid = undefined;
    const fetchMessages = async ({ lastUid }) => {
      observedLastUid = lastUid;
      return makeFetcher([
        makeMessage({
          uid: 24,
          subject: 'Update from Acme',
          fromAddress: 'careers@acme.example',
          raw: 'Unfortunately, we are not moving forward with your application.',
        }),
      ])({ lastUid });
    };

    const summary = await syncRejectionEmails(db, {
      replay: true,
      skipTrash: true,
      fetchMessages,
    });

    const emailLog = db.prepare(`
      SELECT subject, matched_job_id, match_status, reason
      FROM rejection_email_log
      WHERE uid = 24
    `).get();
    const lastUid = db.prepare("SELECT value FROM metadata WHERE key = 'rejection_email_last_uid'").get();

    assert.equal(observedLastUid, null);
    assert.equal(summary.applied, 1);
    assert.deepEqual(emailLog, {
      subject: 'Update from Acme',
      matched_job_id: 'job-1',
      match_status: 'applied',
      reason: 'single_active_company_job',
    });
    assert.equal(lastUid.value, '99');
  });

  it('rescans a Trash UID overlap when the checkpoint is ahead of a matching rejection', async () => {
    const db = createDb();
    insertJob(db, {
      id: 'greenhouse-7336592',
      company: 'reddit',
      title: 'Staff Systems Engineer, AI Enablement and Productivity Engineering',
      url: 'https://job-boards.greenhouse.io/reddit/jobs/7336592',
      status: 'closed',
      stage: 'closed',
      applied_at: '2026-03-16T22:58:22.000Z',
    });
    insertJob(db, {
      id: 'greenhouse-7731772',
      company: 'reddit',
      title: 'Senior Machine Learning Systems Engineer',
      url: 'https://job-boards.greenhouse.io/reddit/jobs/7731772',
      status: 'applied',
      stage: 'applied',
      applied_at: '2026-04-14T00:44:25.358Z',
    });
    db.prepare("INSERT INTO metadata (key, value) VALUES ('rejection_email_trash_last_uid', '570')").run();
    db.prepare("INSERT INTO metadata (key, value) VALUES ('rejection_email_trash_uid_validity', '777')").run();

    const trashMessage = makeMessage({
      uid: 468,
      subject: 'Update from Reddit',
      fromAddress: 'no-reply@greenhouse.io',
      receivedAt: '2026-05-06T16:51:02.000Z',
      raw: `
        Thank you for your interest in the Staff Systems Engineer, AI Enablement and
        Productivity Engineering role at Reddit.
        Unfortunately, we are unable to move forward with your application.
      `,
    });
    const calls = [];
    const fetchMessages = async ({ mailbox, lastUid, uidValidity, uidOverlap }) => {
      calls.push({ mailbox, lastUid, uidValidity, uidOverlap });
      if (mailbox !== '[Gmail]/Trash') {
        return { uidValidity: '777', lastUid, messages: [] };
      }

      const floor = Number(lastUid) - Number(uidOverlap || 0);
      return {
        uidValidity: '777',
        lastUid,
        messages: trashMessage.uid > floor ? [trashMessage] : [],
      };
    };

    const summary = await syncRejectionEmails(db, { fetchMessages });
    const job = db.prepare(`
      SELECT status, stage, rejected_from_stage, rejected_at
      FROM jobs
      WHERE id = 'greenhouse-7336592'
    `).get();
    const emailLog = db.prepare(`
      SELECT mailbox, uid, matched_job_id, match_status, reason
      FROM rejection_email_log
      WHERE uid = 468
    `).get();
    const trashCall = calls.find((call) => call.mailbox === '[Gmail]/Trash');

    assert.equal(trashCall.uidOverlap, 200);
    assert.equal(summary.applied, 1);
    assert.deepEqual(job, {
      status: 'rejected',
      stage: 'rejected',
      rejected_from_stage: 'closed',
      rejected_at: '2026-05-06T16:51:02.000Z',
    });
    assert.deepEqual(emailLog, {
      mailbox: '[Gmail]/Trash',
      uid: 468,
      matched_job_id: 'greenhouse-7336592',
      match_status: 'applied',
      reason: 'company_title_match',
    });
  });

  it('prefers active jobs before falling back to already-rejected jobs', () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Acme',
      title: 'Platform Engineer',
      url: 'https://job-boards.greenhouse.io/acme/jobs/1111',
      status: 'rejected',
      stage: 'rejected',
    });
    insertJob(db, {
      id: 'job-2',
      company: 'Acme',
      title: 'Site Reliability Engineer',
      url: 'https://job-boards.greenhouse.io/acme/jobs/2222',
    });

    const match = matchRejectionEmail(db, makeMessage({
      uid: 17,
      subject: 'Important information about your application to Acme',
      fromAddress: 'careers@acme.example',
      raw: `
        Thank you for applying to Acme.
        Unfortunately, we have decided not to proceed.
      `,
    }));

    assert.equal(match.job.id, 'job-2');
    assert.equal(match.reason, 'single_active_company_job');
  });

  it('matches company suffix variants like PitchBook Data emails that say PitchBook', () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'pitchbookdata',
      title: 'Software Development Engineer, Platform Engineering',
      url: 'https://job-boards.greenhouse.io/pitchbookdata/jobs/1111',
      status: 'closed',
      stage: 'closed',
    });
    insertJob(db, {
      id: 'job-2',
      company: 'pitchbookdata',
      title: 'Sr. Site Reliability Engineer',
      url: 'https://job-boards.greenhouse.io/pitchbookdata/jobs/2222',
    });

    const match = matchRejectionEmail(db, makeMessage({
      uid: 18,
      subject: 'Your application for Sr. Site Reliability Engineer at PitchBook',
      fromAddress: 'no-reply@greenhouse.io',
      raw: `
        Thank you for applying to PitchBook.
        Unfortunately, we have decided not to move forward with your application
        for the Sr. Site Reliability Engineer role.
      `,
    }));

    assert.equal(match.job.id, 'job-2');
    assert.equal(match.reason, 'company_title_match');
  });

  it('uses title to disambiguate multiple matching company variants', () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Anduril',
      title: 'Senior Site Reliability Engineer, Production Engineering',
      url: 'https://builtin.com/job/anduril/sre-production',
    });
    insertJob(db, {
      id: 'job-2',
      company: 'andurilindustries',
      title: 'Software Engineer, DevOps, Discovery',
      url: 'https://job-boards.greenhouse.io/andurilindustries/jobs/1111',
      status: 'closed',
      stage: 'closed',
    });

    const match = matchRejectionEmail(db, makeMessage({
      uid: 20,
      subject: 'Thank you for applying to Anduril Industries',
      fromAddress: 'no-reply@greenhouse.io',
      raw: `
        After looking over your experience, we've made the decision to not move forward
        with your application for Senior Site Reliability Engineer, Production Engineering at this time.
      `,
    }));

    assert.equal(match.job.id, 'job-1');
    assert.equal(match.reason, 'company_title_match');
  });

  it('prefers a non-archived duplicate when title matches are otherwise identical', () => {
    const db = createDb();
    insertJob(db, {
      id: 'job-1',
      company: 'Acme',
      title: 'Senior DevOps Engineer',
      url: 'https://example.com/acme/jobs/1111',
      status: 'archived',
      stage: 'applied',
    });
    insertJob(db, {
      id: 'job-2',
      company: 'Acme',
      title: 'Senior DevOps Engineer',
      url: 'https://example.com/acme/jobs/2222',
      status: 'closed',
      stage: 'closed',
    });
    insertJob(db, {
      id: 'job-3',
      company: 'Acme',
      title: 'Senior DevOps Engineer (Active Secret Clearance)',
      url: 'https://example.com/acme/jobs/3333',
    });

    const match = matchRejectionEmail(db, makeMessage({
      uid: 25,
      subject: 'Application update',
      fromAddress: 'careers@acme.example',
      raw: `
        Thank you for your interest in Acme.
        The Senior DevOps Engineer role has now been filled.
      `,
    }));

    assert.equal(match.job.id, 'job-2');
    assert.equal(match.reason, 'company_title_match');
  });

});
