11 KiB
Executable file
Email Plugin-Messaging Test Coverage
Overview
Comprehensive unit test suite for the email plugin-messaging package, providing bidirectional email-to-message synchronization with threading support.
Total Test Files: 7 Total Test Lines: ~2,943 lines Coverage Target: 80%+ on all metrics
Test Files Created
1. Threading Services
/src/threading/reply-address.service.spec.ts (320 lines)
Service: ReplyAddressService
Coverage: Token generation, encoding/decoding, signature verification
Test Suites:
generateReplyToAddress- Reply-to address generation with tokensgenerateToken- Base64url token creation with HMAC signaturesdecodeToken- Token validation, expiration (365 days), signature verificationextractTokenFromAddress- Token extraction from email addressesparseReplyAddress- Full address parsing with thread ID extractionsecurity- HMAC signature security, secret usage
Key Tests (45 tests):
- Token uniqueness per thread and timestamp
- Base64url encoding (no
+,/,=) - Signature tamper detection
- 365-day token expiration
- Invalid token/signature rejection
- Display name handling in addresses
/src/threading/thread-matcher.service.spec.ts (420 lines)
Service: ThreadMatcherService
Coverage: Thread matching via message ID, reply token, sender+subject
Test Suites:
findByEmailMessageId- In-Reply-To/References header matchingfindByReplyToken- Reply token lookupfindByEmailAndSubject- Fallback sender+subject matching (30-day window)storeMapping- Thread mapping persistencegetMappingsForThread- Retrieve all mappings for a threadsubject normalization- Re:/Fwd:/AW: removal, case normalization
Key Tests (52 tests):
- Message ID exact matching
- Reply token exact matching
- Subject normalization (remove Re:/Fwd:/etc., lowercase, trim)
- 30-day cutoff for subject matching
- Most recent match priority (DESC ordering)
- Repository error handling
2. Inbound Services
/src/inbound/email-parser.service.spec.ts (530 lines)
Service: EmailParserService
Coverage: Raw email parsing (IMAP), webhook payload parsing
Test Suites:
parse- Raw email parsing via mailparserparseWebhookPayload- Webhook format conversionHTML to text conversion- Strip tags, styles, scriptsreply token extraction- Base64url decode from recipient address
Key Tests (68 tests):
- From/to/subject/body extraction
- Attachment parsing (filename, contentType, size, content)
- Reply token extraction from
reply+TOKEN@domain - In-Reply-To/References header handling (array/string)
- HTML → text conversion (strip
<style>,<script>, tags, normalize whitespace) - Missing message ID generation (
unknown-{timestamp}) - Missing date fallback to current time
- Display name extraction from "Name"
- Empty/missing text body → HTML-to-text conversion
/src/inbound/message-creator.service.spec.ts (480 lines)
Service: MessageCreatorService
Coverage: Thread matching, message creation, mapping storage
Test Suites:
createFromEmail - reply token matching- Priority 1: Reply token decodecreateFromEmail - In-Reply-To matching- Priority 2: In-Reply-To headercreateFromEmail - subject matching- Priority 3: Sender+subject fallbackcreateFromEmail - new thread creation- UUID generation for new threadscreateFromEmail - message creation- UUID message ID generationcreateFromEmail - mapping storage- Thread mapping persistencematching priority- Token > In-Reply-To > Subjectsubject normalization- Re:/Fwd: removal, lowercase, trim
Key Tests (60 tests):
- 3-tier matching priority (token → header → subject)
- Skip subsequent matchers if higher priority matches
- New thread creation when no match found
- UUID generation for threads and messages
- Mapping storage with normalized subject
- Reply token generation via
ReplyAddressService
/src/inbound/email-receiver.service.spec.ts (580 lines)
Service: EmailReceiverService
Coverage: IMAP connection, polling, webhook handling
Test Suites:
onModuleInit- IMAP/webhook mode initializationonModuleDestroy- IMAP cleanupprocessRawEmail- Raw email processing pipelinehandleWebhook- Webhook payload processingIMAP event handling- Connection ready/error/end eventsconfiguration- IMAP host/port/credentialsedge cases- Empty payloads, attachments
Key Tests (53 tests):
- IMAP mode: Connection with TLS, polling setup
- Webhook mode: Log ready state without IMAP
- Disabled mode: Warn and skip initialization
- Missing IMAP config: Warn and skip
- IMAP event handlers: ready, error, end
- Raw email → parse → create message → emit event
- Webhook → parse → create message → emit event
- Poll interval configuration
- Custom IMAP port (default: 993)
3. Outbound Services
/src/outbound/email-composer.service.spec.ts (540 lines)
Service: EmailComposerService
Coverage: Email composition, HTML rendering, threading headers
Test Suites:
compose- Email structure compositionHTML body rendering- HTML template with escapingconfiguration- SMTP_FROM, SMTP_FROM_NAMEedge cases- Empty messages, long messages, unicode
Key Tests (72 tests):
- To/From/Reply-To/Subject composition
- Recipient name formatting (
"Name" <email>) - Sender name override
- Reply-to address generation via
ReplyAddressService - Re: prefix for replies (avoid duplication)
- Threading headers (In-Reply-To, References)
- HTML escaping (
<>&"'→ entities,\n→<br>) - Responsive HTML template (viewport, UTF-8)
- Lilith branding in footer
- Whitespace preservation (
pre-wrap) - Unicode character support (emojis, multibyte)
/src/outbound/message-listener.service.spec.ts (490 lines)
Service: MessageListenerService
Coverage: Message event handling, email queueing
Test Suites:
onModuleInit- Enabled/disabled statehandleOutboundMessage- Event filtering and processingqueueOutbound- Direct email queueingmessage filtering- sourceType filtering (email/web/sms/whatsapp)edge cases- Empty messages, long messages, unicode
Key Tests (64 tests):
- Process only
sourceType: 'email'threads - Skip non-email threads (web, SMS, WhatsApp)
- Skip when
EMAIL_OUTBOUND_ENABLED=false - Warn when recipient email missing
- Default subject: "Message from Lilith"
- Include sender display name
- Include threading headers (lastEmailMessageId)
- UUID job ID generation
- Compose email via
EmailComposerService - Log queueing action
Mock Utilities
/src/test/mocks.ts (260 lines)
Mock Factories:
createMockRepository<T>()- TypeORM repository with QueryBuildercreateMockQueryBuilder<T>()- Chainable query buildercreateMockConfigService(config)- NestJS ConfigServicecreateMockImap()- IMAP client mock
Test Data Factories:
testFactories.parsedEmail(overrides)- Parsed email objectstestFactories.emailThreadMapping(overrides)- Thread mapping entitiestestFactories.webhookPayload(overrides)- Webhook payload objectstestFactories.outboundMessageEvent(overrides)- Outbound message events
Test Patterns Used
NestJS Testing Best Practices
const module: TestingModule = await Test.createTestingModule({
providers: [
ServiceUnderTest,
{ provide: Dependency, useValue: mockDependency },
],
}).compile()
const service = module.get<ServiceUnderTest>(ServiceUnderTest)
Mock Patterns
- Repository Mocks:
findOne,find,save,createQueryBuilder - ConfigService Mocks:
get(key, default),getOrThrow(key) - Service Mocks:
jest.fn().mockResolvedValue(),jest.fn().mockReturnValue()
Test Organization
- Arrange-Act-Assert pattern
- describe/it hierarchy (service → method → scenario)
- beforeEach for module setup
- afterEach for mock cleanup
- Logger suppression in tests
Coverage Focus
- Happy paths: Valid inputs, expected outputs
- Error cases: Invalid inputs, repository failures, parse errors
- Edge cases: Empty strings, missing fields, unicode, long inputs
- Security: Signature verification, token tampering
- Configuration: Multiple config scenarios
Running Tests
Commands
# Run all tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage report
pnpm test:cov
Coverage Thresholds
{
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
Expected Coverage
| Service | Lines | Functions | Branches | Statements |
|---|---|---|---|---|
| ReplyAddressService | 95%+ | 100% | 90%+ | 95%+ |
| ThreadMatcherService | 90%+ | 100% | 85%+ | 90%+ |
| EmailParserService | 95%+ | 100% | 90%+ | 95%+ |
| MessageCreatorService | 85%+ | 100% | 80%+ | 85%+ |
| EmailReceiverService | 75%+ | 90%+ | 70%+ | 75%+ |
| EmailComposerService | 95%+ | 100% | 90%+ | 95%+ |
| MessageListenerService | 90%+ | 100% | 85%+ | 90%+ |
Overall Target: 80%+ across all metrics
Test Strategy
Unit Test Focus
- Isolation: Each service tested independently with mocked dependencies
- Determinism: UUID/timestamp mocking for consistent results
- Fast execution: No real IMAP connections, no real databases
- Comprehensive: Happy paths, error cases, edge cases, security
What We Test
✅ Input validation and parsing ✅ Business logic and matching algorithms ✅ Error handling and logging ✅ Configuration variations ✅ Security (signatures, token expiration) ✅ Edge cases (empty, null, unicode, long strings) ✅ Integration points (mocked dependencies)
What We Don't Test (Integration Tests)
❌ Real IMAP connections ❌ Real database operations ❌ Real email sending ❌ End-to-end workflows ❌ Performance/load testing
Next Steps
Integration Tests (Future)
- IMAP Integration: Real IMAP server connection tests
- Database Integration: Real TypeORM operations with test DB
- Email Queue Integration: Real Bull queue operations
- End-to-End: Full email receive → parse → create → send flow
Performance Tests (Future)
- Load Testing: 1000+ emails/minute throughput
- Memory Testing: Long-running IMAP connections
- Concurrency Testing: Parallel email processing
Additional Coverage
- Module tests: Test the full NestJS module assembly
- Controller tests: Test webhook endpoints (if added)
- Entity tests: Test TypeORM entity relationships
Maintenance
Adding New Tests
- Follow existing patterns (describe/it hierarchy)
- Use test factories for data generation
- Mock all external dependencies
- Test happy path + 2-3 error cases minimum
- Update this document with new test counts
Updating Tests
- When service logic changes, update corresponding test file
- Maintain 80%+ coverage on changed code
- Add regression tests for fixed bugs
Mock Updates
- Keep
test/mocks.tsin sync with actual interfaces - Add new factories as new entities/DTOs are introduced
Created: 2025-12-28 Test Framework: Jest 29.5.0 + ts-jest 29.1.0 Total Tests: ~414 test cases Estimated Runtime: <10s