Skip to content

fix: resolve #807, #808, #809, #810 — ID retry, dashboard pagination, E2E webhook/SSE, report index - #1

Open
topsonDev wants to merge 1 commit into
mainfrom
fix/issues-807-808-809-810
Open

fix: resolve #807, #808, #809, #810 — ID retry, dashboard pagination, E2E webhook/SSE, report index#1
topsonDev wants to merge 1 commit into
mainfrom
fix/issues-807-808-809-810

Conversation

@topsonDev

Copy link
Copy Markdown
Owner

Summary

This PR resolves four open issues in a single batch.


StellarEdu#807 — generateStudentId collision retry

Problem: Under concurrency, two registrations could pass the Student.exists() check simultaneously, then both call Student.create(). The second write hit the unique index and surfaced as an opaque 409.

Fix (backend/src/controllers/studentController.js):

  • Track autoGenerated flag when no studentId is supplied in the request body.
  • Hoist assignedFee / assignedDeadline to function scope so the catch block can access them during a retry.
  • On a MongoDB 11000 error with an auto-generated ID, call generateStudentId() again (up to MAX_ID_RETRIES attempts) and retry Student.create() with the new candidate.
  • Only propagate DUPLICATE_STUDENT (409) when the caller supplied the ID explicitly, or after all retry attempts are exhausted.

StellarEdu#808 — Frontend dashboard pagination

Finding: frontend/src/pages/dashboard.jsx already implements full server-side pagination, debounced search, status/class filters, and skeleton loading. Stat counts are sourced from the /payments/summary aggregate endpoint. The acceptance criteria are satisfied by the existing implementation; no code changes were required.


StellarEdu#809 — E2E test: webhook + SSE side effects

Problem: The pipeline E2E test suite had no assertions covering the webhook and SSE side effects that fire after a successful payment.

Fix (tests/e2e/paymentPipeline.e2e.test.js):

  • Scenario 7 — webhook: configures a webhookUrl on the school, spies on webhookService.notifyPaymentConfirmed, calls verifyPayment, and asserts the webhook was fired with the correct URL, payment payload (txHash, status: 'SUCCESS'), and HMAC secret.
  • Scenario 8 — SSE: spies on sseService.emit, triggers syncAllPayments with a matched transaction, and asserts the 'payment' event was emitted on the correct school channel with the expected txHash.
  • Both scenarios run against mongodb-memory-server with no real network calls; CI-safe.

StellarEdu#810 — Compound index alignment for report queries

Problem: reportService.aggregateByDate matched { schoolId, status: 'SUCCESS', studentDeleted: { $ne: true } } but omitted deletedAt: null. The partial compound index on payments has partialFilterExpression: { studentDeleted: { $ne: true }, deletedAt: null }. Without both conditions the planner could not use the index and fell back to a COLLSCAN.

Fix (backend/src/services/reportService.js):

  • Added deletedAt: null to the $match stage in aggregateByDate so the query fully satisfies the partial filter expression.
  • Added hint({ schoolId: 1, status: 1, confirmedAt: -1 }) to the aggregate call to pin the planner to the partial compound index and prevent in-memory sorts on confirmedAt.
  • getDashboardMetrics already includes deletedAt: null in all its $match stages; no further changes needed.

Files changed

File Issue
backend/src/controllers/studentController.js StellarEdu#807
backend/src/services/reportService.js StellarEdu#810
tests/e2e/paymentPipeline.e2e.test.js StellarEdu#809

Testing

  • Existing unit and integration tests continue to pass.
  • New E2E scenarios (7 & 8) run without real network calls using mongodb-memory-server and the existing fakeHorizon fixture.

Closes StellarEdu#807
Closes StellarEdu#808
Closes StellarEdu#809
Closes StellarEdu#810

…tellarEdu#810

StellarEdu#807 — generateStudentId collision retry
- Track whether a studentId was auto-generated in registerStudent.
- Hoist assignedFee/assignedDeadline to function scope so the catch
  block can access them during a retry.
- On a MongoDB 11000 duplicate-key error with an auto-generated ID,
  call generateStudentId() again (up to MAX_ID_RETRIES attempts) and
  retry Student.create() with the new candidate, instead of surfacing
  an opaque 409 to the client.
- Only propagate DUPLICATE_STUDENT (409) when the caller supplied the
  ID explicitly, or when all retry attempts are exhausted.

StellarEdu#808 — Frontend dashboard pagination
- Server-side pagination, search, status/class filters, and stat counts
  from server aggregates were already present in dashboard.jsx and the
  /students + /payments/summary API routes. No additional changes
  required; confirmed the implementation satisfies acceptance criteria.

StellarEdu#809 — E2E payment pipeline webhook + SSE assertions
- Added Scenario 7 to tests/e2e/paymentPipeline.e2e.test.js: sets a
  webhookUrl on the school, spies on webhookService.notifyPaymentConfirmed,
  calls verifyPayment, and asserts the webhook was fired with the correct
  URL, payment payload, and HMAC secret.
- Added Scenario 8: spies on sseService.emit, triggers syncAllPayments
  with a matched transaction, and asserts the SSE 'payment' event was
  emitted on the correct school channel.
- Both tests run against mongodb-memory-server with no real network calls.

StellarEdu#810 — Compound index alignment for report queries
- reportService.aggregateByDate: added deletedAt: null to the $match
  stage. Without it the query did not satisfy the partial index filter
  expression ({ studentDeleted: {$ne: true}, deletedAt: null }) and
  MongoDB fell back to a COLLSCAN on large collections.
- Added hint({ schoolId: 1, status: 1, confirmedAt: -1 }) to the
  aggregate call so the planner always picks the partial compound index
  and never performs an in-memory sort on confirmedAt.
- getDashboardMetrics already included deletedAt: null in all its
  $match stages; no further changes were needed there.

Closes StellarEdu#807
Closes StellarEdu#808
Closes StellarEdu#809
Closes StellarEdu#810
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant