diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-01-default.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-01-default.json.snap new file mode 100644 index 000000000..4e510a638 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-01-default.json.snap @@ -0,0 +1,86 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 2, + "sort": "documentDate", + "order": "desc", + "filtered": false + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-02-search.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-02-search.json.snap new file mode 100644 index 000000000..b4df20193 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-02-search.json.snap @@ -0,0 +1,54 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 1, + "sort": "documentDate", + "order": "desc", + "filtered": true + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-03-kind.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-03-kind.json.snap new file mode 100644 index 000000000..f562be3b7 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-03-kind.json.snap @@ -0,0 +1,86 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 2, + "sort": "documentDate", + "order": "desc", + "filtered": true + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-04-episode.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-04-episode.json.snap new file mode 100644 index 000000000..4433cdfff --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-04-episode.json.snap @@ -0,0 +1,59 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 1, + "sort": "documentDate", + "order": "desc", + "filtered": true + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-05-year.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-05-year.json.snap new file mode 100644 index 000000000..b4df20193 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-05-year.json.snap @@ -0,0 +1,54 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 1, + "sort": "documentDate", + "order": "desc", + "filtered": true + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-06-range.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-06-range.json.snap new file mode 100644 index 000000000..0f3ac5e04 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-06-range.json.snap @@ -0,0 +1,59 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 1, + "sort": "documentDate", + "order": "desc", + "filtered": false + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-07-sorted.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-07-sorted.json.snap new file mode 100644 index 000000000..80854ea45 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-07-sorted.json.snap @@ -0,0 +1,54 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": "" + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 1, + "sort": "title", + "order": "asc", + "filtered": false + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-08-next-page.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-08-next-page.json.snap new file mode 100644 index 000000000..cd302d295 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-08-next-page.json.snap @@ -0,0 +1,59 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 1, + "sort": "title", + "order": "asc", + "filtered": false + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-09-invalid.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-09-invalid.json.snap new file mode 100644 index 000000000..e7205ca8b --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-09-invalid.json.snap @@ -0,0 +1,29 @@ +{ + "status": 422, + "body": { + "data": null, + "error": "Invalid list query", + "details": { + "issues": [ + { + "path": "sort", + "code": "invalid_value", + "message": "Invalid option: expected one of \"documentDate\"|\"createdAt\"|\"title\"" + } + ] + }, + "meta": { + "errorCode": "documents.inbound.invalidQuery", + "requestId": "" + } + }, + "events": [ + { + "status": 422, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.get-10-delegated.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-10-delegated.json.snap new file mode 100644 index 000000000..4e510a638 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.get-10-delegated.json.snap @@ -0,0 +1,86 @@ +{ + "status": 200, + "body": { + "data": { + "documents": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + } + ], + "nextCursor": null + }, + "error": null + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.inbound.list" + }, + "meta": { + "count": 2, + "sort": "documentDate", + "order": "desc", + "filtered": false + }, + "warnings": null, + "error": null + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-01-stored.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-01-stored.json.snap new file mode 100644 index 000000000..283688949 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-01-stored.json.snap @@ -0,0 +1,95 @@ +{ + "status": 201, + "body": { + "data": { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + }, + "error": null + }, + "events": [ + { + "status": 201, + "action": { + "name": "documents.summary.enqueueSkipped" + }, + "meta": { + "documentId": "", + "byteSize": 47, + "servingClass": "inline", + "linked": 1, + "linkedVisits": 0, + "scoped": false, + "sourceSystem": null, + "aiDeferred": false, + "reason": "no_provider" + }, + "warnings": null, + "error": null + } + ], + "effects": [ + { + "effect": "enqueueDocumentIndex", + "args": [ + "", + "" + ] + }, + { + "effect": "enqueueDocumentThumbnail", + "args": [ + "", + "" + ] + } + ], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-02-duplicate.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-02-duplicate.json.snap new file mode 100644 index 000000000..9e810fbdc --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-02-duplicate.json.snap @@ -0,0 +1,79 @@ +{ + "status": 200, + "body": { + "data": { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2026-10-02", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [ + { + "episodeId": "", + "name": "Knee" + } + ], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + }, + "error": null, + "meta": { + "duplicate": true + } + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.vault.upload" + }, + "meta": { + "documentId": "", + "duplicate": true, + "scoped": false, + "sourceSystem": null, + "aiDeferred": false + }, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-03-second.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-03-second.json.snap new file mode 100644 index 000000000..10cd57835 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-03-second.json.snap @@ -0,0 +1,104 @@ +{ + "status": 201, + "body": { + "data": { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "providerType": null, + "reportDate": null, + "documentDate": "2025-03-04", + "errorReason": null, + "factCount": 0, + "pendingCount": 0, + "conditionLinks": [], + "encounterLinks": [], + "servingClass": "inline", + "hasContentIndex": false, + "contentIndexSource": null, + "lastIndexAttemptAt": null, + "lastIndexOutcome": null, + "hasThumbnail": false, + "sourceSystem": null, + "sourceId": null, + "createdAt": "", + "updatedAt": "" + }, + "error": null + }, + "events": [ + { + "status": 201, + "action": { + "name": "documents.summary.enqueueSkipped" + }, + "meta": { + "documentId": "", + "byteSize": 42, + "servingClass": "inline", + "linked": 0, + "linkedVisits": 0, + "scoped": false, + "sourceSystem": null, + "aiDeferred": false, + "reason": "no_provider" + }, + "warnings": null, + "error": null + } + ], + "effects": [ + { + "effect": "enqueueDocumentIndex", + "args": [ + "", + "" + ] + }, + { + "effect": "enqueueDocumentThumbnail", + "args": [ + "", + "" + ] + } + ], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-04-no-file.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-04-no-file.json.snap new file mode 100644 index 000000000..b59bda209 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-04-no-file.json.snap @@ -0,0 +1,55 @@ +{ + "status": 422, + "body": { + "data": null, + "error": "Field 'file' must be a file", + "meta": { + "errorCode": "http.invalid", + "requestId": "" + } + }, + "events": [ + { + "status": 422, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-05-invalid-metadata.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-05-invalid-metadata.json.snap new file mode 100644 index 000000000..487c2088e --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-05-invalid-metadata.json.snap @@ -0,0 +1,64 @@ +{ + "status": 422, + "body": { + "data": null, + "error": "Invalid document metadata", + "details": { + "issues": [ + { + "path": "kind", + "code": "invalid_value", + "message": "Invalid option: expected one of \"DOCTOR_REPORT\"|\"DISCHARGE_LETTER\"|\"LAB_RESULT\"|\"IMAGING\"|\"PRESCRIPTION\"|\"REFERRAL\"|\"INSURANCE\"|\"VACCINATION\"|\"SICK_NOTE\"|\"OTHER\"" + } + ] + }, + "meta": { + "errorCode": "documents.inbound.invalidMetadata", + "requestId": "" + } + }, + "events": [ + { + "status": 422, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-06-empty.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-06-empty.json.snap new file mode 100644 index 000000000..289166a8c --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-06-empty.json.snap @@ -0,0 +1,55 @@ +{ + "status": 422, + "body": { + "data": null, + "error": "Uploaded file is empty", + "meta": { + "errorCode": "documents.inbound.invalidMetadata", + "requestId": "" + } + }, + "events": [ + { + "status": 422, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-07-unsupported.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-07-unsupported.json.snap new file mode 100644 index 000000000..573253337 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-07-unsupported.json.snap @@ -0,0 +1,56 @@ +{ + "status": 415, + "body": { + "data": null, + "error": "This file type is not supported.", + "meta": { + "errorCode": "documents.inbound.fileType", + "reason": "unsupportedType", + "requestId": "" + } + }, + "events": [ + { + "status": 415, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-08-unknown-episode.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-08-unknown-episode.json.snap new file mode 100644 index 000000000..9be010e54 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-08-unknown-episode.json.snap @@ -0,0 +1,55 @@ +{ + "status": 404, + "body": { + "data": null, + "error": "Episode not found", + "meta": { + "errorCode": "documents.inbound.episodeNotFound", + "requestId": "" + } + }, + "events": [ + { + "status": 404, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-09-half-key.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-09-half-key.json.snap new file mode 100644 index 000000000..72f370f5d --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-09-half-key.json.snap @@ -0,0 +1,64 @@ +{ + "status": 422, + "body": { + "data": null, + "error": "Invalid source key", + "details": { + "issues": [ + { + "path": "sourceId", + "code": "invalid_type", + "message": "Invalid input: expected string, received undefined" + } + ] + }, + "meta": { + "errorCode": "documents.inbound.invalidMetadata", + "requestId": "" + } + }, + "events": [ + { + "status": 422, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-10-keys-differ.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-10-keys-differ.json.snap new file mode 100644 index 000000000..8414003ac --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-10-keys-differ.json.snap @@ -0,0 +1,55 @@ +{ + "status": 422, + "body": { + "data": null, + "error": "The source key in the address and in the form differ.", + "meta": { + "errorCode": "documents.inbound.invalidMetadata", + "requestId": "" + } + }, + "events": [ + { + "status": 422, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-11-token-stored.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-11-token-stored.json.snap new file mode 100644 index 000000000..d0af23f8a --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-11-token-stored.json.snap @@ -0,0 +1,98 @@ +{ + "status": 201, + "body": { + "data": { + "id": "", + "duplicate": false + }, + "error": null + }, + "events": [ + { + "status": 201, + "action": { + "name": "documents.summary.enqueueSkipped" + }, + "meta": { + "documentId": "", + "byteSize": 46, + "servingClass": "inline", + "linked": 0, + "linkedVisits": 0, + "scoped": true, + "sourceSystem": "PAPERLESS", + "aiDeferred": true, + "reason": "deferred" + }, + "warnings": null, + "error": null + } + ], + "effects": [ + { + "effect": "enqueueDocumentIndex", + "args": [ + "", + "", + { + "localOnly": true + } + ] + }, + { + "effect": "enqueueDocumentThumbnail", + "args": [ + "", + "" + ] + } + ], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "OTHER", + "title": null, + "filename": "imported.pdf", + "mimeType": "application/pdf", + "byteSize": 46, + "status": "STORED", + "documentDate": "", + "sourceSystem": "PAPERLESS", + "sourceId": "41", + "deletedAt": null, + "conditionLinks": [] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-12-token-query-key.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-12-token-query-key.json.snap new file mode 100644 index 000000000..e09fd2747 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-12-token-query-key.json.snap @@ -0,0 +1,79 @@ +{ + "status": 200, + "body": { + "data": { + "id": "", + "duplicate": true + }, + "error": null, + "meta": { + "duplicate": true + } + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.vault.upload" + }, + "meta": { + "documentId": "", + "duplicate": true, + "scoped": true, + "sourceSystem": "PAPERLESS", + "aiDeferred": false + }, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "OTHER", + "title": null, + "filename": "imported.pdf", + "mimeType": "application/pdf", + "byteSize": 46, + "status": "STORED", + "documentDate": "", + "sourceSystem": "PAPERLESS", + "sourceId": "41", + "deletedAt": null, + "conditionLinks": [] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-13-token-deleted.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-13-token-deleted.json.snap new file mode 100644 index 000000000..5792498db --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-13-token-deleted.json.snap @@ -0,0 +1,82 @@ +{ + "status": 200, + "body": { + "data": { + "id": "", + "duplicate": true, + "deleted": true + }, + "error": null, + "meta": { + "duplicate": true, + "deleted": true + } + }, + "events": [ + { + "status": 200, + "action": { + "name": "documents.vault.upload" + }, + "meta": { + "documentId": "", + "duplicate": true, + "deleted": true, + "scoped": true, + "sourceSystem": "PAPERLESS", + "aiDeferred": false + }, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "OTHER", + "title": null, + "filename": "imported.pdf", + "mimeType": "application/pdf", + "byteSize": 46, + "status": "STORED", + "documentDate": "", + "sourceSystem": "PAPERLESS", + "sourceId": "41", + "deletedAt": "2026-10-14T09:00:00.000Z", + "conditionLinks": [] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/__snapshots__/documents-inbound.post-14-module-off.json.snap b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-14-module-off.json.snap new file mode 100644 index 000000000..8b7405689 --- /dev/null +++ b/tests/integration/routes/parity/__snapshots__/documents-inbound.post-14-module-off.json.snap @@ -0,0 +1,70 @@ +{ + "status": 403, + "body": { + "data": null, + "error": "Module \"inboundDocuments\" is not enabled", + "meta": { + "errorCode": "module.disabled", + "module": "inboundDocuments", + "requestId": "" + } + }, + "events": [ + { + "status": 403, + "action": null, + "meta": null, + "warnings": null, + "error": null + } + ], + "effects": [], + "stored": [ + { + "id": "", + "kind": "DISCHARGE_LETTER", + "title": "Discharge letter", + "filename": "discharge.pdf", + "mimeType": "application/pdf", + "byteSize": 47, + "status": "STORED", + "documentDate": "2026-10-02T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [ + { + "episodeId": "" + } + ] + }, + { + "id": "", + "kind": "OTHER", + "title": null, + "filename": "imported.pdf", + "mimeType": "application/pdf", + "byteSize": 46, + "status": "STORED", + "documentDate": "", + "sourceSystem": "PAPERLESS", + "sourceId": "41", + "deletedAt": "2026-10-14T09:00:00.000Z", + "conditionLinks": [] + }, + { + "id": "", + "kind": "LAB_RESULT", + "title": "Blood test", + "filename": "labs.pdf", + "mimeType": "application/pdf", + "byteSize": 42, + "status": "STORED", + "documentDate": "2025-03-04T12:00:00.000Z", + "sourceSystem": null, + "sourceId": null, + "deletedAt": null, + "conditionLinks": [] + } + ] +} diff --git a/tests/integration/routes/parity/documents-inbound.parity.test.ts b/tests/integration/routes/parity/documents-inbound.parity.test.ts new file mode 100644 index 000000000..81ef63c24 --- /dev/null +++ b/tests/integration/routes/parity/documents-inbound.parity.test.ts @@ -0,0 +1,343 @@ +/** + * Parity for `POST` and `GET /api/documents/inbound` while the upload + * request handling and the list read move into `src/lib/documents/`. + * + * The upload walk: a stored PDF with metadata and an episode link, the same + * bytes again, each refusal the route tells apart (no file, invalid + * metadata, an empty file, an unsupported type, an unknown episode, half a + * source key, a query and a form key that disagree), a narrow + * `documents:write` token storing under a source key with AI reading held + * back, its re-send by query key, its re-send after the person deleted the + * document, and the module switched off. The list walk: the default page, + * a search, the filters, a sorted page and the page after it, a refused + * query, and a delegate reading the record. After each request the snapshot + * holds the response, the wide event, the recorded queue tails and, for the + * uploads, the stored documents. + */ +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; + +import { GET, POST } from "@/app/api/documents/inbound/route"; +import { DOCUMENTS_WRITE_SCOPE } from "@/lib/documents/scopes"; + +import { callRoute, type RouteAuth } from "../../../kit/call-route"; +import { + makeGrant, + makeSession, + makeToken, + makeUser, +} from "../../../kit/factories"; +import { + effectRecorder, + eventFacts, + expectParity, + stable, + wideEventRecorder, +} from "../../../kit/parity"; +import { + getPrismaClient, + switchSessionTo, + truncateAllTables, +} from "../../setup"; + +vi.mock("@/lib/logging/transports", async (importOriginal) => { + const actual = + await importOriginal(); + const { wideEventRecorder: recorder } = await import("../../../kit/parity"); + return { + ...actual, + emitIfSampled: (event: (typeof recorder.events)[number]) => { + recorder.events.push(event); + }, + }; +}); +vi.mock("@/lib/jobs/document-index", async (importOriginal) => { + const actual = + await importOriginal(); + const { effectRecorder } = await import("../../../kit/parity"); + return { + ...actual, + enqueueDocumentIndex: async (...args: unknown[]) => { + effectRecorder.record("enqueueDocumentIndex", args); + }, + }; +}); +vi.mock("@/lib/jobs/document-thumbnail", async (importOriginal) => { + const actual = + await importOriginal(); + const { effectRecorder } = await import("../../../kit/parity"); + return { + ...actual, + enqueueDocumentThumbnail: async (...args: unknown[]) => { + effectRecorder.record("enqueueDocumentThumbnail", args); + }, + }; +}); +vi.mock("@/lib/jobs/document-summary", async (importOriginal) => { + const actual = + await importOriginal(); + const { effectRecorder } = await import("../../../kit/parity"); + return { + ...actual, + enqueueDocumentSummary: async (...args: unknown[]) => { + effectRecorder.record("enqueueDocumentSummary", args); + }, + }; +}); + +const prisma = getPrismaClient(); +const NOW = new Date("2026-10-15T12:00:00.000Z"); +const PDF = (tag: string) => + Buffer.from(`%PDF-1.7\n% ${tag}\n1 0 obj\n<<>>\nendobj\n%%EOF\n`); + +let userId: string; +let owner: RouteAuth; +let writer: RouteAuth; +let delegate: RouteAuth & { sessionId: string }; +let episodeId: string; + +async function stored() { + return prisma.inboundDocument.findMany({ + where: { userId }, + orderBy: [{ createdAt: "asc" }, { filename: "asc" }], + select: { + id: true, + kind: true, + title: true, + filename: true, + mimeType: true, + byteSize: true, + status: true, + documentDate: true, + sourceSystem: true, + sourceId: true, + deletedAt: true, + conditionLinks: { select: { episodeId: true } }, + }, + }); +} + +function form( + file: Buffer | null, + name: string, + fields: Record = {}, +): FormData { + const data = new FormData(); + if (file) data.set("file", new File([new Uint8Array(file)], name)); + for (const [key, value] of Object.entries(fields)) { + for (const v of Array.isArray(value) ? value : [value]) { + data.append(key, v); + } + } + return data; +} + +async function upload( + name: string, + body: FormData, + options: { auth?: RouteAuth; query?: Record } = {}, +): Promise<{ id: string | null }> { + wideEventRecorder.take(); + effectRecorder.take(); + const res = await callRoute<{ id: string | null }>(POST, { + method: "POST", + route: "/api/documents/inbound", + body, + query: options.query, + auth: options.auth ?? owner, + }); + const events = wideEventRecorder.take(); + expect(events.length).toBeGreaterThan(0); + await expectParity( + `documents-inbound.${name}`, + stable( + { + status: res.status, + body: res.body, + events: events.map(eventFacts), + effects: effectRecorder.take(), + stored: await stored(), + }, + { since: NOW }, + ), + ); + return { id: res.data?.id ?? null }; +} + +async function list( + name: string, + query: Record, + auth: RouteAuth = owner, +): Promise<{ nextCursor: string | null }> { + wideEventRecorder.take(); + const res = await callRoute<{ nextCursor: string | null }>(GET, { + method: "GET", + route: "/api/documents/inbound", + query, + auth, + }); + const events = wideEventRecorder.take(); + expect(events.length).toBeGreaterThan(0); + await expectParity( + `documents-inbound.${name}`, + stable( + { status: res.status, body: res.body, events: events.map(eventFacts) }, + { since: NOW }, + ), + ); + return { nextCursor: res.data?.nextCursor ?? null }; +} + +beforeAll(async () => { + vi.useFakeTimers({ toFake: ["Date"], now: NOW }); + await truncateAllTables(prisma); + const user = await makeUser({ + timezone: "Europe/Berlin", + modulePreferencesJson: { inboundDocuments: true }, + }); + userId = user.id; + owner = await makeSession(user); + writer = await makeToken(user, { permissions: [DOCUMENTS_WRITE_SCOPE] }); + const episode = await prisma.illnessEpisode.create({ + data: { + userId, + label: "Knee", + type: "OTHER", + onsetAt: new Date("2026-09-01T00:00:00.000Z"), + }, + }); + episodeId = episode.id; + const reader = await makeUser({ timezone: "UTC" }); + await makeGrant(user, reader); + delegate = await makeSession(reader); +}); + +afterAll(() => { + vi.useRealTimers(); +}); + +describe("POST /api/documents/inbound parity", () => { + it("a stored upload and the same bytes again", async () => { + const body = () => + form(PDF("discharge"), "discharge.pdf", { + title: "Discharge letter", + kind: "DISCHARGE_LETTER", + documentDate: "2026-10-02", + episodeIds: [episodeId], + }); + await upload("post-01-stored", body()); + await upload("post-02-duplicate", body()); + await upload( + "post-03-second", + form(PDF("labs"), "labs.pdf", { + title: "Blood test", + kind: "LAB_RESULT", + documentDate: "2025-03-04", + }), + ); + }); + + it("the refusals", async () => { + await upload("post-04-no-file", form(null, "x", { title: "Nothing" })); + await upload( + "post-05-invalid-metadata", + form(PDF("bad-kind"), "bad.pdf", { kind: "SHOPPING_LIST" }), + ); + await upload("post-06-empty", form(Buffer.alloc(0), "empty.pdf")); + await upload( + "post-07-unsupported", + form( + Buffer.from("hi\n"), + "page.html", + ), + ); + await upload( + "post-08-unknown-episode", + form(PDF("orphan"), "orphan.pdf", { + episodeIds: ["cmissingepisode000000000"], + }), + ); + await upload("post-09-half-key", form(PDF("half"), "half.pdf"), { + query: { sourceSystem: "PAPERLESS" }, + }); + await upload( + "post-10-keys-differ", + form(PDF("differ"), "differ.pdf", { + sourceSystem: "PAPERLESS", + sourceId: "77", + }), + { query: { sourceSystem: "PAPERLESS", sourceId: "78" } }, + ); + }); + + it("a narrow token storing under a source key", async () => { + const stored = await upload( + "post-11-token-stored", + form(PDF("imported"), "imported.pdf", { + sourceSystem: "PAPERLESS", + sourceId: "41", + aiRead: "defer", + }), + { auth: writer }, + ); + await upload("post-12-token-query-key", form(PDF("imported"), "x.pdf"), { + auth: writer, + query: { sourceSystem: "PAPERLESS", sourceId: "41" }, + }); + await prisma.inboundDocument.update({ + where: { id: stored.id! }, + data: { deletedAt: new Date("2026-10-14T09:00:00.000Z") }, + }); + await upload( + "post-13-token-deleted", + form(PDF("imported"), "imported.pdf", { + sourceSystem: "PAPERLESS", + sourceId: "41", + }), + { auth: writer }, + ); + }); + + it("the module switched off", async () => { + await prisma.user.update({ + where: { id: userId }, + data: { modulePreferencesJson: { inboundDocuments: false } }, + }); + await upload("post-14-module-off", form(PDF("dark"), "dark.pdf")); + await prisma.user.update({ + where: { id: userId }, + data: { modulePreferencesJson: { inboundDocuments: true } }, + }); + }); +}); + +describe("GET /api/documents/inbound parity", () => { + it("the default page, a search and the filters", async () => { + await list("get-01-default", {}); + await list("get-02-search", { q: "blood" }); + await list("get-03-kind", { kind: "DISCHARGE_LETTER,LAB_RESULT" }); + await list("get-04-episode", { episodeId }); + await list("get-05-year", { year: 2025 }); + await list("get-06-range", { from: "2026-10-01", to: "2026-10-02" }); + }); + + it("a sorted page and the page after it", async () => { + const first = await list("get-07-sorted", { + sort: "title", + order: "asc", + limit: 1, + }); + await list("get-08-next-page", { + sort: "title", + order: "asc", + limit: 1, + cursor: first.nextCursor!, + }); + }); + + it("a refused query, and a delegate reading the record", async () => { + await list("get-09-invalid", { sort: "size" }); + await switchSessionTo(delegate.sessionId, userId); + await list("get-10-delegated", {}, delegate); + await switchSessionTo(delegate.sessionId, null); + }); +});