Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
108 lines
3.9 KiB
TypeScript
108 lines
3.9 KiB
TypeScript
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { describe, expect, it } from 'vitest';
|
||
|
||
import { getDefaultReplyParserConfig, parseEmailReply } from './replyParser';
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = path.dirname(__filename);
|
||
|
||
function readFixture(file: string): string {
|
||
return fs.readFileSync(path.join(__dirname, '__fixtures__', file), 'utf8');
|
||
}
|
||
|
||
describe('replyParser', () => {
|
||
it('honours explicit reply boundaries and extracts hidden tokens', () => {
|
||
const text = readFixture('gmail-top-post.txt');
|
||
const html = readFixture('gmail-top-post.html');
|
||
|
||
const result = parseEmailReply({ text, html });
|
||
|
||
expect(result.confidence).toBe('high');
|
||
expect(result.strategy).toBe('custom-boundary');
|
||
expect(result.tokens).toEqual({
|
||
conversationToken: 'GHFTK-123',
|
||
ticketId: '8f4e9f72-1d7d-4b3c-94fb-2f8f9ccce901',
|
||
commentId: '6e5874a5-22a9-479d-a20e-052b43165c0d',
|
||
});
|
||
expect(result.appliedHeuristics).toContain('explicit-boundary');
|
||
expect(result.sanitizedText).toMatchInlineSnapshot(`
|
||
"Hi support team,\n\nHappy to confirm the restart worked fine."
|
||
`);
|
||
expect(result.sanitizedHtml).toMatchInlineSnapshot(`
|
||
"<p>Hi support team,</p>\n<p>Happy to confirm the restart worked fine.</p>\n<p>Thanks,<br/>Jane Doe<br/><em>Sent from my iPhone</em></p>"
|
||
`);
|
||
});
|
||
|
||
it('removes inline quoted sections but preserves responder notes for Outlook style replies', () => {
|
||
const text = readFixture('outlook-inline.txt');
|
||
|
||
const result = parseEmailReply({ text });
|
||
|
||
expect(result.confidence).toBe('medium');
|
||
expect(result.appliedHeuristics).toContain('quote-prefix');
|
||
expect(result.sanitizedText).toMatchInlineSnapshot(`
|
||
"Hi Alex,\n\nSee inline below.\n\nSerial: SN-998877."
|
||
`);
|
||
});
|
||
|
||
it('keeps user preface while discarding forwarded chains', () => {
|
||
const text = readFixture('forwarded-thread.txt');
|
||
|
||
const result = parseEmailReply({ text });
|
||
|
||
expect(result.strategy).toMatch(/provider-header|custom-boundary|quoted-block/);
|
||
expect(result.sanitizedText).toMatchInlineSnapshot(`
|
||
"Please see the forwarded ticket below – looks like the customer escalated again."
|
||
`);
|
||
});
|
||
|
||
it('strips signatures and confidentiality footers', () => {
|
||
const text = readFixture('signature-heavy.txt');
|
||
|
||
const result = parseEmailReply({ text });
|
||
|
||
expect(result.sanitizedText).not.toMatch(/Confidentiality Notice/);
|
||
expect(result.appliedHeuristics).toContain('signature-trim');
|
||
expect(result.sanitizedText).toMatchInlineSnapshot(`
|
||
"Hello,\n\nHere is the update you requested. The service has been restored as of 16:45 UTC."
|
||
`);
|
||
});
|
||
|
||
it('falls back gracefully when no heuristics match', () => {
|
||
const text = 'Quick confirmation that everything works as expected.';
|
||
|
||
const result = parseEmailReply({ text }, getDefaultReplyParserConfig());
|
||
|
||
expect(result.strategy).toBe('fallback');
|
||
expect(result.sanitizedText).toMatchInlineSnapshot('"Quick confirmation that everything works as expected."');
|
||
});
|
||
|
||
it('recovers tokens wrapped/quoted by Gmail', () => {
|
||
const text = `let's see if replies work now
|
||
*Robert Isaacs* | *CEO*
|
||
2963 Gulf to Bay Blvd. Clearwater, FL | 727-591-7436
|
||
|
||
|
||
On Thu, Nov 20, 2025 at 7:58 AM Software <support@nineminds.com> wrote:
|
||
|
||
> [ALGA-REPLY-TOKEN a83113b5-d30f-4ec9-85ce-0d0ce95fa49a
|
||
> ticketId=a5ea2cf5-f572-436c-b485-9b5ca07a9e17
|
||
> commentId=3de1cc6a-8d4a-4b3c-9a27-72990ab84226]
|
||
> --- Please reply above this line ---
|
||
>
|
||
> --- Please reply above this line ---
|
||
> New Comment Added
|
||
`;
|
||
|
||
const result = parseEmailReply({ text });
|
||
|
||
expect(result.tokens).toEqual({
|
||
conversationToken: 'a83113b5-d30f-4ec9-85ce-0d0ce95fa49a',
|
||
ticketId: 'a5ea2cf5-f572-436c-b485-9b5ca07a9e17',
|
||
commentId: '3de1cc6a-8d4a-4b3c-9a27-72990ab84226',
|
||
});
|
||
});
|
||
});
|