fix(mock): emit server event after app ready so onServer runs#5969
fix(mock): emit server event after app ready so onServer runs#5969elrrrrrrr wants to merge 1 commit into
server event after app ready so onServer runs#5969Conversation
📝 WalkthroughWalkthrough
ChangesServer Event Emission Timing Fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsStopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying egg with
|
| Latest commit: |
464503c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8f3a0a2a.egg-cci.pages.dev |
| Branch Preview URL: | https://fix-mock-emit-server-after-r.egg-cci.pages.dev |
There was a problem hiding this comment.
Code Review
This pull request adjusts the initialization order in MockApplicationWorker to await app.ready() before invoking createServer(app). This ensures that the server event is not emitted before egg core registers its listener, preventing the loss of critical event bindings like clientError. A new test case has been added to verify that the clientError listener is successfully attached after initialization. No review comments were provided, so there is no additional feedback to address.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## next #5969 +/- ##
==========================================
+ Coverage 85.50% 85.53% +0.02%
==========================================
Files 669 669
Lines 19825 19828 +3
Branches 3917 3919 +2
==========================================
+ Hits 16952 16960 +8
+ Misses 2481 2477 -4
+ Partials 392 391 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a lifecycle ordering bug in @eggjs/mock single-process mode where the mock HTTP server was created (and the server event emitted) before Egg core registers its once('server', ...) listener during app.ready(), causing Application.onServer() not to run and leaving clientError (and related server wiring) uninitialized.
Changes:
- Move mock HTTP server creation (
createServer(app)) to run afterawait app.ready()in single-process mock initialization. - Add a regression test asserting that
clientErroris wired onapp.serverafterapp.ready().
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| plugins/mock/src/lib/app.ts | Reorders initialization so createServer() runs after app.ready() to avoid losing the server event and ensure onServer() executes. |
| plugins/mock/test/app.test.ts | Adds a regression test verifying app.server has a clientError listener after readiness. |
|
@codex review |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/mock/test/app.test.ts`:
- Around line 90-101: The test function creates an app and performs assertions,
but if any assertion fails, the app.close() cleanup call is skipped, causing
state leakage. Restructure the code by moving the await app.ready() call and all
assertions into a try block, then place await app.close() in a finally block to
ensure cleanup always executes regardless of assertion failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9fd68324-40d7-48ab-b479-260dbfa8188f
📒 Files selected for processing (2)
plugins/mock/src/lib/app.tsplugins/mock/test/app.test.ts
Deploying egg-v3 with
|
| Latest commit: |
464503c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b3b2c844.egg-v3.pages.dev |
| Branch Preview URL: | https://fix-mock-emit-server-after-r.egg-v3.pages.dev |
|
Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
68a72fd to
0cc716b
Compare
server event isn't lostserver event after app ready so onServer runs
0cc716b to
ec8ccfb
Compare
server event after app ready so onServer runsserver event after app ready so onServer runs
| // @eggjs/mock emits the `server` event before `app.ready()`, while egg core | ||
| // registers its `once("server", ...)` listener inside Application.load() | ||
| // (during app.ready()). egg core re-emits `server` after the listener is | ||
| // registered so onServer still runs and wires up the `clientError` handler | ||
| // (plus graceful shutdown / server timeout / websocket). Regression guard: | ||
| // assert the clientError listener is wired after ready. |
@eggjs/mock created the http server and emitted the `server` event inside
`createServer()`, which runs BEFORE `app.ready()`. But egg core registers its
`once('server', server => this.onServer(server))` listener inside
`Application.load()` (during `app.ready()`), and `onServer` reads loaded config.
So the event was emitted before the listener existed (and before config was
loaded), and `onServer` — which wires up clientError logging, graceful shutdown,
server timeout and websocket support — never ran in single-process mock mode.
Move the `server` emit out of `createServer()` (it now only creates & caches the
server, so `app.server` stays available during boot as before) and emit it once
after `await app.ready()` in both the single and parallel app workers. This
mirrors @eggjs/cluster, which also emits `server` after the app is ready, and
keeps the event emitted exactly once.
Added a regression test asserting `app.server` has a `clientError` listener
after ready.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ec8ccfb to
464503c
Compare
What's the problem this PR solves?
In single-process mock mode (
mm.app()), egg core'sonServer()never runs, so the http server gets noclientErrorhandler (and graceful shutdown / server timeout / websocket wiring is skipped too). Symptom: a malformed request that triggersclientErroris not logged.Root cause
@eggjs/mockcreates the http server and emitsserverinsidecreateServer(), which runs beforeawait app.ready(). But egg core registers itsserverlistener insideApplication.load()(which runs duringapp.ready()), andonServerreads loaded config. So the event is emitted before the listener exists (and before config is loaded) → it's lost →onServernever runs.egg3 (worked) — config loaded synchronously, listener registered in the constructor
egg4 (broken) — async loader moved listener registration into load()
This PR — emit once, after ready
The fix
Stop emitting
serverinsidecreateServer()(it now only creates & caches the server, soapp.serverstays available during boot exactly as before), and emit it once, afterawait app.ready()in both the single (lib/app.ts) and parallel (lib/parallel/app.ts) app workers:This:
serverexactly once, after the listener is registered and config is loaded (no double-emit, no prematureonServer);createServerbeforeready(soapp.serveris available during boot — the intentional ordering from More security tips #98 is preserved);@eggjs/cluster, which also emitsserverafter the app is ready (the behaviour More security tips #98 originally referenced).Alternatives considered (and rejected)
serverlistener in the constructor (restore egg3 timing): ❌ crashes —onServerreadsthis.config, which isn't loaded untilapp.ready(), so firing it at the pre-ready emit throws. This is exactly why egg4 moved the registration intoload().createServer()to afterapp.ready(): ❌ shifts app-boot microtask timing and turns unrelated boot-error fixtures (e.g.plugins/multipart,cluster-client-error) into unhandled errors → CI red. KeepingcreateServerbeforereadyand only relocating the emit avoids this.serverafter ready (keep the early emit too): works, but emits twice; the early emit can double-fireserverlisteners registered before ready. Removing the early emit is cleaner.Test
Added a regression guard in
plugins/mock/test/app.test.ts: afterapp.ready(), assertapp.serverhas aclientErrorlistener (i.e.onServerran). It fails before this change (count0) and passes after.Validation
Verified against a real egg4 framework (chair,
@eggjs/mock@7.0.2-beta.9): ashould log error when 400test (raw malformed request → expect log) failed becauseclientErrorwasn't wired. With this change it passes deterministically.🤖 Generated with Claude Code