Skip to content

[ndi-cloud-node] e2e tests for the dataset write-lock API (follow-up to #784) #794

@stevevanhooser

Description

@stevevanhooser

Follow-up to #784 (writeLock endpoints in waltham-data-science/ndi-cloud-node, PR #106).

The PR adds unit tests in api/src/tests/ that cover the service / middleware / controller in isolation (mocked Mongoose), but there are no end-to-end tests against a deployed API for the new endpoints. We have e2e coverage for the older Dataset / Document endpoints in api/e2eScripts/, so the gap is specifically the new write-lock surface.

Scope

Add e2e scripts under api/e2eScripts/datasets/ (or a new api/e2eScripts/datasets/write-lock/ folder) in the same style as the existing scripts. The tests should run against a deployed dev environment and exercise the real HTTP API, not the in-process service.

Endpoints to cover

  • GET /v1/datasets/:datasetId/write-lock
  • POST /v1/datasets/:datasetId/write-lock
  • PATCH /v1/datasets/:datasetId/write-lock
  • DELETE /v1/datasets/:datasetId/write-lock

Plus the Document write paths under lock:

  • POST /v1/datasets/:datasetId/documents
  • POST /v1/datasets/:datasetId/documents/:documentId
  • DELETE /v1/datasets/:datasetId/documents/:documentId
  • POST /v1/datasets/:datasetId/documents/bulk-delete
  • POST /v1/datasets/:datasetId/documents/bulk-upload

Test cases (golden-path + edge)

Lifecycle

  1. GET on a fresh dataset returns { writeLock: { state: 'idle' } }.
  2. POST with { reason: 'e2e-test' } returns 200 with state: 'held', heldBy, heldUntil, reason.
  3. GET after acquire returns the held lock.
  4. PATCH extends heldUntil (compare timestamps).
  5. DELETE releases the lock; subsequent GET shows state: 'idle'.

Idempotency / conflict
6. POST twice by the same user → both 200 (idempotent re-acquire).
7. User A acquires; user B POSTs → 409 with current holder details (heldBy, reason).
8. User A acquires; user B DELETEs → 403.
9. User A acquires; user B PATCHes → 403.

Write-path enforcement
10. User A acquires the lock; user B POSTs a document → 423 with { heldBy, heldUntil, reason }.
11. User A acquires; user B POSTs to documents/:id (update), DELETEs a document, POSTs bulk-delete, POSTs bulk-upload → all 423.
12. While held, user B GETs documents → still 200 (reads must not be blocked).
13. While held, user A (the holder) writes a document → 200 (holder bypass).

Expiry
14. Acquire with ttlSeconds: 30 (the service minimum), wait > 30s, then user B POSTs a document → 200 (lazy expiry clears the lock). GET write-lock after the next write call should show state: 'idle'.

Admin force-release
15. User A acquires the lock; admin user DELETEs the lock → 204. Subsequent GET shows state: 'idle'.
16. Admin force-releasing an already-idle lock → 204.

Auth
17. Unauthenticated POST / PATCH / DELETE → 401 or 403 (whatever the auth middleware returns; document it).

Out of scope

  • The client-side ndi.migrate.cloud consumer (separate issue, NDI-matlab Dayone #10).
  • Load / concurrency stress tests; one acquire-attempt-from-each-of-two-users is enough for v1.

Dependencies

PR waltham-data-science/ndi-cloud-node#106 must be merged and deployed to dev before these tests can run green.

Suggested file layout

api/e2eScripts/datasets/write-lock/
  acquire-release.sh         # cases 1-5
  idempotency-conflict.sh    # cases 6-9
  write-rejection.sh         # cases 10-13
  expiry.sh                  # case 14
  admin-force-release.sh     # cases 15-16
  auth.sh                    # case 17

…or one consolidated script if that matches the existing style better.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions