Repository navigation
Conversation
The SES throttle reschedule did not re-attach the email queue parameters, so the retry resumed the Hog VM and the email was never sent. Re-attach them, the same way as the per-workflow and team cap reschedules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
📝 Walkthrough
Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to This change keeps the original email payload when a throttled send is retried, and the added tests cover delivery and error cases. No merge-blocking risk remains. Security Architecture Review
Pre-merge checks |
|
There was a problem hiding this comment.
🧹 Nitpick comments (2)
nodejs/tests/helpers/ses.ts (2)
31-36: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueA rejected upstream call can write headers twice.
If
handleRequestthrows afterresponse.writeHead, the catch handler callswriteHead(502)again. The second call raisesERR_HTTP_HEADERS_SENTinside the promise rejection handler. That error goes unhandled and can fail the Jest run. At present the code writes nothing before the throw at Line 132, so this needs a future change to trigger. The cheap fix is to checkresponse.headersSentfirst.Proposed fix
void this.handleRequest(request, response).catch((error: Error) => { + if (response.headersSent) { + response.destroy(error) + return + } response.writeHead(502, { 'content-type': 'application/json' })
53-57: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
stop()can hang on keep-alive sockets.
server.close()calls back only after every open connection ends. The SDK's HTTP handler keeps sockets alive. The tests callsesV2Client.destroy()beforestop(), which should release them. A test that fails before that call can leaveafterEachwaiting until it times out. CallcloseAllConnections()beforeclose().Proposed fix
await new Promise<void>((resolve, reject) => { this.server.close((error) => (error ? reject(error) : resolve())) + this.server.closeAllConnections() })Source: Learnings
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
894e936d-f9b8-4014-a2b5-a4784b9ff47c
📒 Files selected for processing (8)
.github/workflows/ci-nodejs.ymldocker-compose.base.ymldocker-compose.dev.ymldocs/published/handbook/engineering/developing-locally.mdnodejs/src/cdp/services/messaging/email.service.ses.test.tsnodejs/src/cdp/services/messaging/email.service.test.tsnodejs/src/cdp/services/messaging/email.service.tsnodejs/tests/helpers/ses.ts
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.
webjunkie
left a comment
There was a problem hiding this comment.
Note
Agent review, actively prompted for and steered.
Requesting changes from the DevEx side. The ses compose service is not necessary. The LocalSes proxy already does the work that the tests assert on.
Answer SendEmail in the proxy and drop the container
LocalSes.handleRequest already parses and records every SendEmailCommandInput in requests. It also injects the SESv2 errors itself. The container supplies only two things:
- A
MessageId. OnerandomUUID()in the proxy gives the same result. - The
/storeinbox. It returns the same input that the proxy already recorded. MostgetEmails()assertions sit next to anses.requestsassertion that checks the same send.
The SDK still sends real HTTP requests to the proxy. Serialization, retries and error mapping stay covered. A client-level mock such as aws-sdk-client-mock would lose that, so the proxy is the right level.
The change is small and the PR loses a lot:
- the
sesservice indocker-compose.base.ymlanddocker-compose.dev.yml, with its health check and profile - the
ci-nodejs.ymlprobe for unrebased branches, and the compose files added tonode_full, which force a full Node.js run on every compose edit - the
developing-locally.mdparagraph and the manualdocker compose ... up sesstep - the shared-inbox isolation logic and the
unrelatedClienttest, which exist only because tests share one inbox
The layers above this PR (#112820 through #113056) use the fake only through LocalSes, in email.service.ses.test.ts, cdp-cyclotron-worker-email.consumer.test.ts and workflows-e2e.serial.test.ts. Their call sites can stay, so the change reaches them on the next restack. This came up while reading #112820.
Follow the precedent from the LocalStack removal
#81257 removed LocalStack from the dev stack in August, which also removed the last SES emulator. That PR kept dedicated containers only where a real backend makes the decision under test (dynamodb-local for conditional writes). For KMS it added an in-process stub, nodejs/tests/session-replay/recording-api/stub-kms-client.ts. SES here matches the KMS case.
#81257 also documents why SES_ENDPOINT in nodejs/src/cdp/config.ts and posthog/settings/ses.py stays at localhost:4566. The port is dead on purpose. The SES client passes no explicit credentials, so the dead port keeps a local send off real AWS. This PR binds the fake to that port, so whether the port is dead now depends on whether the optional profile runs. An in-process proxy on port 0 leaves the guard as it is.
LocalSes now returns a MessageId and records delivered emails itself, so the tests no longer need the aws-ses-v2-local compose service. This drops the service, its CI wait step and node_full trigger, the docs paragraph, and the shared-inbox isolation test. Port 4566 stays dead locally, which keeps an unconfigured send off real AWS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
nodejs/tests/helpers/ses.ts (1)
68-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueValidate the parsed request body shape.
parseJSON(...) as SendEmailCommandInputis an unchecked cast. A request whose body is not a JSON object, such asnull, is pushed intorequests.toEmailthen throws on property access. The error path returns 500, but the invalid input stays inrequests. This is test-helper code, and the SDK always sends objects. The risk is low.
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Advanced
- Run ID:
dbb085db-bb45-47a0-887f-60fa4cd6fddf
📒 Files selected for processing (2)
nodejs/src/cdp/services/messaging/email.service.ses.test.tsnodejs/tests/helpers/ses.ts
💤 Files with no reviewable changes (1)
- nodejs/src/cdp/services/messaging/email.service.ses.test.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
|
This PR was closed due to lack of activity. Feel free to reopen if it's still relevant. |
Problem
Workflow developers cannot catch SES payload and HTTP error regressions when email tests replace the SDK’s send method.
Note
🟢 Merge risk: low · two-way door (merge-risk rubric)
Changes
MessageId, and keeps the delivered emails.Stacked on #112295; the GitHub diff includes its ancestors. Counts and reviews cover this layer.
Before:
flowchart LR Tests[EmailService tests] --> Mock[Mocked SDK send] classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000; class Tests phYellow; class Mock phGray;After:
flowchart LR Tests[EmailService tests] --> SDK[Real SESv2 SDK] --> Proxy[In-process SES proxy] --> Inbox[Request and delivery assertions] classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff; classDef phRed fill:#f54e00,stroke:#f54e00,color:#fff; classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000; class Tests phYellow; class SDK,Proxy phBlue; class Inbox phGray;How did you test this code?
Test rationale: S1 drives
EmailService.executeSendEmailthrough the real SDK. Cases cover delivery, SDK retries and hard failures. With the throttle reschedule fix reverted, both retry cases fail.Real AWS provisioning and mailbox delivery remain untested. Fork CI requires maintainer approval.
Evidence and context
Release status
Docs update
None. The tests need no extra service, so the local development guide does not change.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: T3 Code / Codex, GPT-6.1 Sol
Skills:
tdd,authoring-ci-workflows,writing-tests,writing-code-comments,writing-pr-descriptions,reviewing-with-coderabbit,running-ci-preflight,merge-risk,qa-swarm,leaving-pr-comments,writing-voice,unslop,principle-prove-it-works.QA and read-only Opus review findings are fixed: inbox isolation, connection diagnostics, AWS profiles, compose compatibility and setup documentation.
CodeRabbit CLI was signed out and skipped.
A review follow-up by Claude Code (Claude Opus 5.5) replaced the compose container with the in-process proxy and removed the compose, CI and docs changes. Skills:
writing-pr-descriptions.