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
139 lines
3.5 KiB
Markdown
139 lines
3.5 KiB
Markdown
# GreenMail + IMAP In-App Artifact Validation Runbook
|
|
|
|
## Goal
|
|
|
|
Validate that IMAP inbound callback processing persists all in-scope artifacts in-app:
|
|
|
|
- regular attachment
|
|
- embedded image artifact
|
|
- original `.eml`
|
|
|
|
## 1) Start test infra
|
|
|
|
Bring up GreenMail test server plus IMAP service:
|
|
|
|
```bash
|
|
docker compose --profile test \
|
|
-f docker-compose.yaml \
|
|
-f docker-compose.base.yaml \
|
|
-f docker-compose.ee.yaml \
|
|
-f docker-compose.imap.ce.yaml \
|
|
-f docker-compose.imap-test.yaml \
|
|
--env-file server/.env \
|
|
up -d imap-test-server email-service
|
|
```
|
|
|
|
GreenMail defaults from `docker-compose.imap-test.yaml`:
|
|
|
|
- SMTP: `localhost:3025`
|
|
- IMAP: `localhost:3143`
|
|
- test user: `imap_user@localhost` / `imap_pass`
|
|
|
|
## 2) Configure in-app IMAP mode
|
|
|
|
Set these env vars for server + IMAP service (same `IMAP_WEBHOOK_SECRET` on both):
|
|
|
|
```bash
|
|
IMAP_WEBHOOK_SECRET=<shared-secret>
|
|
IMAP_INBOUND_EMAIL_IN_APP_PROCESSING_ENABLED=true
|
|
```
|
|
|
|
If running server locally (`npm run dev`), point IMAP service back to host:
|
|
|
|
```bash
|
|
IMAP_WEBHOOK_URL=http://host.docker.internal:<local-port>/api/email/webhooks/imap
|
|
```
|
|
|
|
## 3) Send a deterministic SMTP message
|
|
|
|
Use Python SMTP to send one email containing:
|
|
|
|
- one regular attachment
|
|
- one HTML `data:image` embedded payload
|
|
|
|
```bash
|
|
python3 - <<'PY'
|
|
import smtplib
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
from email.mime.base import MIMEBase
|
|
from email import encoders
|
|
import base64
|
|
import uuid
|
|
|
|
msg = MIMEMultipart()
|
|
msg["From"] = "sender@example.com"
|
|
msg["To"] = "imap_user@localhost"
|
|
msg["Subject"] = f"Inbound artifact smoke {uuid.uuid4().hex[:8]}"
|
|
|
|
embedded = base64.b64encode(b"embedded-image-body").decode()
|
|
html = f'<p>hello<img src="data:image/png;base64,{embedded}" /></p>'
|
|
msg.attach(MIMEText("hello", "plain"))
|
|
msg.attach(MIMEText(html, "html"))
|
|
|
|
part = MIMEBase("text", "plain")
|
|
part.set_payload(b"regular-attachment-body")
|
|
encoders.encode_base64(part)
|
|
part.add_header("Content-Disposition", 'attachment; filename="regular.txt"')
|
|
msg.attach(part)
|
|
|
|
with smtplib.SMTP("localhost", 3025) as s:
|
|
s.sendmail(msg["From"], [msg["To"]], msg.as_string())
|
|
PY
|
|
```
|
|
|
|
## 4) Verify IMAP service + webhook processing
|
|
|
|
Check IMAP service logs for successful sync/dispatch:
|
|
|
|
```bash
|
|
docker logs --tail 200 $(docker ps --format '{{.Names}}' | grep email-service | head -1)
|
|
```
|
|
|
|
Check server logs for IMAP webhook handoff:
|
|
|
|
- `handoff: "unified_pointer_queue"`
|
|
- no fatal artifact persistence error
|
|
|
|
## 5) Verify database artifacts
|
|
|
|
Find newest ticket from this sender/subject window and verify associated docs:
|
|
|
|
```sql
|
|
select t.ticket_id, t.title, t.entered_at
|
|
from tickets t
|
|
where t.tenant = '<tenant_id>'
|
|
and t.title like 'Inbound artifact smoke %'
|
|
order by t.entered_at desc
|
|
limit 1;
|
|
```
|
|
|
|
```sql
|
|
select d.document_name, d.mime_type, d.file_size
|
|
from documents d
|
|
join document_associations da
|
|
on da.tenant = d.tenant and da.document_id = d.document_id
|
|
where d.tenant = '<tenant_id>'
|
|
and da.entity_type = 'ticket'
|
|
and da.entity_id = '<ticket_id>'
|
|
order by d.document_name;
|
|
```
|
|
|
|
Expected `document_name` set includes:
|
|
|
|
- `regular.txt`
|
|
- `embedded-image-1.png`
|
|
- `original-email-<sanitized-message-id>.eml`
|
|
|
|
Also verify backing `external_files` exists for each `documents.file_id`.
|
|
|
|
## 6) UI verification
|
|
|
|
Open ticket Documents tab and confirm all three artifact classes are visible.
|
|
|
|
## Pass criteria
|
|
|
|
- ticket/comment ingest succeeds
|
|
- documents are associated to ticket for regular + embedded + `.eml`
|
|
- duplicate replay of same message id does not create duplicate artifacts
|