Skip to content

feat(parse-server-mongo): compat-lane-ready sample + JS line coverage gate#97

Open
AkashKumar7902 wants to merge 6 commits intomainfrom
feat/parse-server-mongo-compat-lane-ready
Open

feat(parse-server-mongo): compat-lane-ready sample + JS line coverage gate#97
AkashKumar7902 wants to merge 6 commits intomainfrom
feat/parse-server-mongo-compat-lane-ready

Conversation

@AkashKumar7902
Copy link
Copy Markdown
Contributor

@AkashKumar7902 AkashKumar7902 commented May 1, 2026

Summary

Brings the parse-server-mongo/ sample (originally introduced as a focused boot-phase mongo tiebreaker reproducer in #94 / #95) up to the keploy compat-lane shape used by samples-python/doccano-django and samples-java/restheart-mongo:

  1. flow.sh refactored into the bootstrap | record-traffic | coverage subcommand contract.
  2. docker-compose.yml now uses ${VAR:-default} substitutions throughout so concurrent matrix cells can share a docker daemon.
  3. New keploy.yml.template ships parse-server's noise filter (objectId, sessionToken, createdAt, updatedAt, Date header).
  4. Broader REST + GraphQL traffic surface in parse_record_traffic (51 distinct fires across users, sessions, classes, roles, files, cloud-functions, schemas, hooks, GraphQL, aggregate).
  5. Real JS line coverage of node_modules/parse-server/lib via NODE_V8_COVERAGE + a custom report tool — see "Coverage architecture" below.

The boot-phase divergence behaviour for the mongo/v2 tiebreaker test is preserved (bootstrap still sleeps 3s after /health so pre-mutation find _SCHEMA snapshots land first; record-traffic still issues the three-class _SCHEMA mutation pattern).

Layout

parse-server-mongo/
├── Dockerfile               # node:20-bookworm-slim + parse-server deps (base; uninstrumented)
├── Dockerfile.coverage      # extends base, adds c8 + graceful-shutdown shim
├── docker-compose.yml       # mongo:7 + this sample, env-driven
├── docker-compose.coverage.yml  # overlay; sets NODE_V8_COVERAGE=/coverage
├── coverage-report.js       # reads V8 dumps, emits Covered N/M (XX.X%)
├── flow.sh                  # bootstrap | record-traffic | coverage
├── index.js                 # 25-line parse-server bootstrap (unchanged)
├── package.json             # parse-server@8.2.3 + express@4.21.2 (unchanged)
└── keploy.yml.template      # noise filter

Coverage architecture

Real JS line coverage via Node-native NODE_V8_COVERAGE + a custom coverage-report.js, validated locally end-to-end at 38.7% (9605/24851 lines) on the existing record-traffic surface. Distribution sanity-checks: PostgresStorageAdapter 7.7% (mongo selected, postgres unused), MongoStorageAdapter 46%, RestQuery 52.8%, RestWrite 45.4%, Auth 40.5%, LiveQuery 3% (not exercised) — the numbers reflect what traffic actually executes.

Critical: the base Dockerfile and docker-compose.yml are untouched. Coverage instrumentation lives in a separate overlay applied only by the standalone GH Actions coverage workflow. The keploy/enterprise compat lane consumes the base compose unchanged.

How it works:

  1. The overlay's Dockerfile.coverage installs c8 and drops a coverage-entrypoint.js shim that registers process.on('SIGTERM', () => process.exit(0)). Without that, express's app.listen pins the event loop and compose stop would signal-kill node (exit 143) before V8's NODE_V8_COVERAGE writer ran.
  2. The overlay sets NODE_V8_COVERAGE=/coverage. Each Node process writes a coverage-<pid>-<ts>.json dump on clean exit.
  3. coverage-report.js reads V8's nested block-coverage ranges, resolves per-line execution count against the deepest enclosing range, and emits a Covered N/M (XX.X%) summary plus a c8-shaped json-summary. We don't use c8 report because c8 filters node_modules/** even with --include overrides, and parse-server lives entirely under node_modules/parse-server.

flow.sh coverage against the base compose (no overlay) is a graceful no-op.

Coverage gate

.github/workflows/parse-server-mongo.yml triggers ONLY on changes under parse-server-mongo/**. Build (PR HEAD) + release (base ref) coverage compared; PR fails if drop exceeds 1.0pp. Threshold overridable via vars.PARSE_COVERAGE_THRESHOLD. The first-PR escape hatch detects a missing helper script on the base ref and treats baseline as 0%.

Run modes

  • Smoke check (without keploy): docker compose up -d && bash flow.sh bootstrap 240 && bash flow.sh record-traffic — exactly what the keploy enterprise lane wraps.
  • Real coverage (without keploy): apply the overlay, run bootstrap + record-traffic + compose stop -t 30 parse-server + flow.sh coverage.
  • Concurrent matrix cell: env-vars (PARSE_PROJECT, PARSE_HOST_PORT, PARSE_NETWORK_SUBNET, …) all ${VAR:-default}-substituted so multiple cells can run in parallel.

See README for full commands.

Consumers

  • keploy/enterprise .woodpecker/parse-server-linux.yml — three-cell record/replay matrix that delegates compose + bootstrap + traffic to this sample.

Test plan

  • docker compose up -d boots mongo + parse-server cleanly with no env vars
  • flow.sh bootstrap 240 signs up the fixed user, persists session token
  • flow.sh record-traffic exercises 51 fires across the surface above
  • Coverage validated locally end-to-end: 38.7% line coverage of node_modules/parse-server/lib
  • flow.sh coverage against base compose exits 0 cleanly with INFO message
  • Concurrent matrix cells confirmed via env-var overrides

…e template

Make the parse-server-mongo sample directly consumable as a keploy compat-lane
sample, on par with samples-typescript/umami-postgres and samples-python/
doccano-django.

- flow.sh refactored to subcommand form: bootstrap | record-traffic |
  coverage | list-routes. bootstrap is idempotent (4xx during signup is
  treated as already-exists), persists the session token to
  /tmp/parse-token-${PARSE_PHASE}, and preserves the 3-second post-/health
  pause that the boot-phase _SCHEMA divergence reproducer needs.
- record-traffic ports the broad parse-server REST + GraphQL surface
  (51 fire calls across users/sessions/classes/roles/files/cloud-functions/
  schemas/hooks/graphql/aggregate). Every curl is preceded by log_fired so
  PARSE_FIRED_ROUTES_FILE captures (method,route) for coverage. The
  multi-class _SCHEMA mutation (GameScore, PlayerStats, Achievement) is
  retained so the boot-phase tiebreaker reproducer still triggers.
- coverage subcommand reports (method,route) coverage with numerator from
  keploy/test-set-*/tests/*.yaml when present, else from the fired-routes
  log. Denominator from a curated 35-route table in parse_list_routes.
- docker-compose.yml takes ${VAR:-default} for project name, network name,
  network subnet, IPs, container names, host:container port, and the
  internal PORT env. Defaults preserve standalone `docker compose up`;
  overrides let concurrent matrix cells share a single docker daemon.
- keploy.yml.template carries the parse-server noise filter (header.Date,
  body.objectId, body.sessionToken, body.createdAt, body.updatedAt) so a
  lane consumer can apply it to keploy.yml after `keploy config --generate`.
- README rewritten to document the four subcommands, all env vars, the
  route surface covered, the boot-phase divergence rationale, and a
  concurrent-cell run recipe.

Signed-off-by: Akash Kumar <meakash7902@gmail.com>
Copilot AI review requested due to automatic review settings May 1, 2026 01:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Promotes parse-server-mongo/ from a minimal boot-phase reproducer into a keploy compat-lane sample by adding a subcommand-driven traffic/coverage driver, making compose configuration override-friendly for CI matrix concurrency, and providing a Keploy noise-filter template + updated docs.

Changes:

  • Reworked flow.sh into bootstrap | record-traffic | coverage | list-routes, broadened the fired API surface, and added a coverage report mode.
  • Parameterized docker-compose.yml using ${VAR:-default} for project/network/container naming, IPs, and ports to support concurrent runs on a shared daemon.
  • Added keploy.yml.template for Parse Server noise filtering and expanded README.md to document usage and overrides.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
parse-server-mongo/flow.sh Introduces subcommands, broader traffic generation, fired-route logging, and coverage reporting.
parse-server-mongo/docker-compose.yml Makes compose names/network/IPs/ports/env configurable via environment variable defaults.
parse-server-mongo/keploy.yml.template Adds a noise filter template for volatile Parse Server fields.
parse-server-mongo/README.md Updates documentation for new flow/compose knobs and lane usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +120 to +125
case "${signup_status}" in
2*) echo "[flow] signup ok (HTTP ${signup_status})" ;;
4*)
# 4xx during bootstrap is treated as already-exists; re-run case.
echo "[flow] signup returned HTTP ${signup_status} — treating as already-exists"
;;
Comment on lines +137 to +142
token="$(printf '%s' "${login_resp}" | jq -r '.sessionToken // empty' 2>/dev/null || true)"
if [ -z "${token}" ]; then
echo "[flow] no sessionToken in login response — bootstrap will continue but session-bound calls will be skipped"
: > "${PARSE_TOKEN_FILE}"
else
printf '%s' "${token}" > "${PARSE_TOKEN_FILE}"
Comment on lines +78 to +81
# urlenc — minimal URL-encoder for query payloads.
urlenc() {
jq -rn --arg v "$1" '$v|@uri'
}
PARSE_SERVER_MASTER_KEY: ${PARSE_MASTER_KEY:-keploy-parse-master}
PARSE_SERVER_MASTER_KEY_IPS: ${PARSE_MASTER_KEY_IPS:-0.0.0.0/0,::0}
PARSE_SERVER_SERVER_URL: ${PARSE_SERVER_URL:-http://localhost:6100/parse}
PARSE_SERVER_DATABASE_URI: ${PARSE_DATABASE_URI:-mongodb://172.30.0.10:27017/parse}
# parse-server-mongo

Minimal Parse Server + MongoDB sample. Exists primarily as a falsifying e2e reproducer for the mongo/v2 boot-phase candidate-selection bug — a class of bug where an application issues the same query repeatedly during recording while DB state mutates, and the matcher's score-tied tiebreaker at replay picks the wrong same-shape mock.
Parse Server (`parseplatform/parse-server` 9.x via `parse-server` npm 8.2.x) backed by MongoDB 7. Packaged as a complete keploy compat-lane sample: subcommand-driven traffic, env-driven docker-compose so concurrent matrix cells share a daemon, a noise-filter template that masks the parse-server identifiers that change every run, and a curated route list for coverage reporting.
Comment on lines +69 to +75
# fire METHOD URL [curl args...] — log then send a tolerant curl.
fire() {
local method="$1"
local url="$2"
shift 2
log_fired "${method}" "${url}"
curl -sS -X "${method}" "${url}" "$@" >/dev/null 2>&1 || true
Comment thread parse-server-mongo/flow.sh Outdated
Comment on lines +360 to +361
method="$(awk '/^ method:/{print $2; exit}' "${f}")"
route="$(awk '/^ url:/{print $2; exit}' "${f}")"
Introduce a GitHub Actions workflow scoped to the parse-server-mongo
sample that runs the same end-to-end record/coverage shape as the
doccano-django and umami-postgres lanes. The workflow is path-filtered
to `parse-server-mongo/**` (plus the workflow file and its helper
script) so it does NOT trigger on changes to other samples in this
repo or on changes elsewhere.

Three jobs:
  * build-coverage    — runs the sample end-to-end against the PR's
                        HEAD ref; emits a coverage percentage.
  * release-coverage  — same, against the PR's base ref; first-PR
                        bootstrap escape hatch returns 0% if the
                        sample doesn't exist on the base ref yet.
  * coverage-gate     — fails the PR if HEAD coverage drops more
                        than COVERAGE_THRESHOLD percentage points
                        below base. Default 1.0pp; override via the
                        repo variable PARSE_COVERAGE_THRESHOLD.

A sticky PR comment surfaces the base-vs-PR coverage diff with a
prose explanation of which parse-server REST + GraphQL routes the
sample's flow.sh::parse_record_traffic exercises.

Helper script .github/workflows/scripts/run-and-measure-parse-server.sh
is per-sample (sibling samples will land their own
run-and-measure-<sample>.sh on parallel branches). It does docker
compose up -d --build, polls /parse/health, runs flow.sh bootstrap →
record-traffic → coverage with PARSE_FIRED_ROUTES_FILE as the
standalone numerator, parses the report, and emits coverage=PCT to
GITHUB_OUTPUT.

This gate runs ONLY on PRs touching parse-server-mongo/. The
enterprise PR pipeline (.woodpecker/parse-server-linux.yml) calls
flow.sh coverage informationally and does NOT gate; the gate lives
here on the sample repo, isolated from the enterprise lane.

Signed-off-by: Akash Kumar <meakash7902@gmail.com>
…se ref

parse-server-mongo/ already exists on main (PRs #94/#95), so the
prior detect step returned true on every PR's release-coverage
job and the measure step then tried to invoke a helper script
that didn't exist on the base ref. Extending detect to require
the helper script too lets the first-PR escape hatch fire when
the workflow itself is the change being introduced.

Signed-off-by: Akash Kumar <meakash7902@gmail.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

parse-server-mongo sample coverage

ref coverage
base (main) 0.0%
this PR 38.7%

Threshold: PR may not drop coverage by more than 1.0pp. Override per-repo via the PARSE_COVERAGE_THRESHOLD actions variable.

Replaces the prior API-route-surface "coverage" (counting fired
routes / curated route table) with actual V8 line coverage of the
parse-server library code under node_modules/parse-server/lib.

Architecture:
  - `Dockerfile.coverage` extends the base image with a graceful-
    shutdown shim (coverage-entrypoint.js installs SIGTERM/SIGINT
    handlers calling process.exit(0)) so V8 actually flushes its
    coverage data on `compose stop`. Without that shim, express's
    app.listen pins the loop and the kernel signal-kills node
    (exit 143) before NODE_V8_COVERAGE writes anything.
  - `docker-compose.coverage.yml` is an OVERLAY: applied via
    `-f docker-compose.yml -f docker-compose.coverage.yml`. It
    sets NODE_V8_COVERAGE=/coverage and bind-mounts the host
    ./coverage dir. The base `Dockerfile` and `docker-compose.yml`
    are untouched, so keploy/integrations and keploy/enterprise
    CI lanes consume the base compose and pay zero coverage cost.
  - `coverage-report.js` reads V8's per-process JSON dumps and
    produces a `Covered N/M (XX.X%)` summary plus a c8-shaped
    coverage-summary.json. We don't use `c8 report` because c8
    filters node_modules even when --include is set, and
    parse-server lives entirely under node_modules/parse-server.
    The custom tool walks V8's nested ranges (block coverage)
    and resolves per-line execution count to the deepest range.
  - `flow.sh::parse_coverage` shells into the coverage image to
    run coverage-report.js against the dumped V8 data; when called
    against the base image (no overlay) it prints
    "INFO: ... uninstrumented" and exits 0 so enterprise lanes'
    `flow.sh coverage || true` informational calls keep working.

Removed:
  - `parse_list_routes` (curated route table denominator).
  - `parse_collect_recorded_routes` (keploy-tests / fired-routes
    reader).
  - The legacy route-surface `parse_coverage` body.
  - `list-routes` subcommand.

Validated locally: helper produced `coverage=38.7` to GITHUB_OUTPUT
against a fresh stack (signup + record-traffic + clean stop).
38.7% reflects coverage of node_modules/parse-server/lib —
mongo paths ~46%, REST ~52%, Auth ~40%, schema ~58%; postgres
adapter ~7% (mongo storage selected) and LiveQuery ~3% (not
exercised), which is the expected distribution.

Signed-off-by: Akash Kumar <meakash7902@gmail.com>
Signed-off-by: Akash Kumar <meakash7902@gmail.com>
…/FIRED_ROUTES refs

Signed-off-by: Akash Kumar <meakash7902@gmail.com>
@AkashKumar7902 AkashKumar7902 changed the title feat(parse-server-mongo): make sample compat-lane ready feat(parse-server-mongo): compat-lane-ready sample + JS line coverage gate May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants