mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
test(import): pin the JSON import before moving its writers
This commit is contained in:
1 parent
7a21796b63
commit
ed12625ab6
5 files changed
+1053
No files matched your search
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"data": {
|
||||
"measurements": 4,
|
||||
"moodEntries": 4,
|
||||
"skipped": 2
|
||||
},
|
||||
"error": null
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 200,
|
||||
"action": {
|
||||
"name": "import.upload"
|
||||
},
|
||||
"meta": {
|
||||
"import_stats": {
|
||||
"measurements": 4,
|
||||
"moodEntries": 4,
|
||||
"skipped": 2
|
||||
},
|
||||
"external_id_rejected": 2,
|
||||
"external_id_shapes": "object_description",
|
||||
"external_id_surface": "import.json"
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"effects": [
|
||||
{
|
||||
"effect": "emitDataArrival",
|
||||
"args": [
|
||||
{
|
||||
"userId": "<id:1>",
|
||||
"kind": "weight",
|
||||
"newestSampleAt": "2026-10-11T06:30:00.000Z",
|
||||
"insertedCount": 2,
|
||||
"refId": "<id:2>",
|
||||
"source": "json_import"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"effect": "maybeEnqueueMorningRefresh",
|
||||
"args": [
|
||||
"<id:1>",
|
||||
[
|
||||
"2026-10-12T06:00:00.000Z"
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"measurements": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.4,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-w-1",
|
||||
"measuredAt": "2026-10-10T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": "<bytes:59>",
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.9,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"measuredAt": "2026-10-11T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"value": 104,
|
||||
"unit": "mg/dL",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-g-1",
|
||||
"measuredAt": "2026-10-11T08:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": "FASTING"
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"value": 412,
|
||||
"unit": "min",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-s-1",
|
||||
"measuredAt": "2026-10-12T06:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
}
|
||||
],
|
||||
"mood": [
|
||||
{
|
||||
"date": "2026-10-10",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"tags": "slept well",
|
||||
"source": "IMPORT",
|
||||
"externalId": "daylio-1",
|
||||
"moodLoggedAt": "2026-10-10T07:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:08.803Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:32.701Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-12",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-12T20:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"measurementRollups": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.4
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.9
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 412
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 104
|
||||
}
|
||||
],
|
||||
"moodRollups": [
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 4
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"count": 2,
|
||||
"mean": 3
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 2
|
||||
}
|
||||
],
|
||||
"audit": [
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":4,\"moodEntries\":4,\"skipped\":2}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"data": {
|
||||
"measurements": 3,
|
||||
"moodEntries": 1,
|
||||
"skipped": 6
|
||||
},
|
||||
"error": null
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 200,
|
||||
"action": {
|
||||
"name": "import.upload"
|
||||
},
|
||||
"meta": {
|
||||
"import_stats": {
|
||||
"measurements": 3,
|
||||
"moodEntries": 1,
|
||||
"skipped": 6
|
||||
},
|
||||
"external_id_rejected": 2,
|
||||
"external_id_shapes": "object_description",
|
||||
"external_id_surface": "import.json"
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"effects": [
|
||||
{
|
||||
"effect": "maybeEnqueueMorningRefresh",
|
||||
"args": [
|
||||
"<id:1>",
|
||||
[]
|
||||
]
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"measurements": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.4,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-w-1",
|
||||
"measuredAt": "2026-10-10T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": "<bytes:59>",
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.9,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"measuredAt": "2026-10-11T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"value": 104,
|
||||
"unit": "mg/dL",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-g-1",
|
||||
"measuredAt": "2026-10-11T08:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": "FASTING"
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"value": 412,
|
||||
"unit": "min",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-s-1",
|
||||
"measuredAt": "2026-10-12T06:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
}
|
||||
],
|
||||
"mood": [
|
||||
{
|
||||
"date": "2026-10-10",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"tags": "slept well",
|
||||
"source": "IMPORT",
|
||||
"externalId": "daylio-1",
|
||||
"moodLoggedAt": "2026-10-10T07:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:08.803Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:32.701Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-12",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-12T20:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"measurementRollups": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.4
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.9
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 412
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 104
|
||||
}
|
||||
],
|
||||
"moodRollups": [
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 4
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"count": 2,
|
||||
"mean": 3
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 2
|
||||
}
|
||||
],
|
||||
"audit": [
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":4,\"moodEntries\":4,\"skipped\":2}"
|
||||
},
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":3,\"moodEntries\":1,\"skipped\":6}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"status": 422,
|
||||
"body": {
|
||||
"data": null,
|
||||
"error": "Wert ausserhalb des plausiblen Bereichs",
|
||||
"details": {
|
||||
"issues": [
|
||||
{
|
||||
"path": "measurements.0",
|
||||
"code": "custom",
|
||||
"message": "Wert ausserhalb des plausiblen Bereichs"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"errorCode": "http.invalid",
|
||||
"requestId": "<requestId>"
|
||||
}
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 422,
|
||||
"action": {
|
||||
"name": "import.upload"
|
||||
},
|
||||
"meta": null,
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"effects": [],
|
||||
"stored": {
|
||||
"measurements": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.4,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-w-1",
|
||||
"measuredAt": "2026-10-10T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": "<bytes:59>",
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.9,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"measuredAt": "2026-10-11T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"value": 104,
|
||||
"unit": "mg/dL",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-g-1",
|
||||
"measuredAt": "2026-10-11T08:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": "FASTING"
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"value": 412,
|
||||
"unit": "min",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-s-1",
|
||||
"measuredAt": "2026-10-12T06:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
}
|
||||
],
|
||||
"mood": [
|
||||
{
|
||||
"date": "2026-10-10",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"tags": "slept well",
|
||||
"source": "IMPORT",
|
||||
"externalId": "daylio-1",
|
||||
"moodLoggedAt": "2026-10-10T07:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:08.803Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:32.701Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-12",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-12T20:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"measurementRollups": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.4
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.9
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 412
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 104
|
||||
}
|
||||
],
|
||||
"moodRollups": [
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 4
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"count": 2,
|
||||
"mean": 3
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 2
|
||||
}
|
||||
],
|
||||
"audit": [
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":4,\"moodEntries\":4,\"skipped\":2}"
|
||||
},
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":3,\"moodEntries\":1,\"skipped\":6}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"status": 422,
|
||||
"body": {
|
||||
"data": null,
|
||||
"error": "Timestamp must not be in the future",
|
||||
"details": {
|
||||
"issues": [
|
||||
{
|
||||
"path": "moodEntries.0.loggedAt",
|
||||
"code": "custom",
|
||||
"message": "Timestamp must not be in the future"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"errorCode": "http.invalid",
|
||||
"requestId": "<requestId>"
|
||||
}
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 422,
|
||||
"action": {
|
||||
"name": "import.upload"
|
||||
},
|
||||
"meta": null,
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"effects": [],
|
||||
"stored": {
|
||||
"measurements": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.4,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-w-1",
|
||||
"measuredAt": "2026-10-10T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": "<bytes:59>",
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"value": 81.9,
|
||||
"unit": "kg",
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"measuredAt": "2026-10-11T06:30:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"value": 104,
|
||||
"unit": "mg/dL",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-g-1",
|
||||
"measuredAt": "2026-10-11T08:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": "FASTING"
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"value": 412,
|
||||
"unit": "min",
|
||||
"source": "IMPORT",
|
||||
"externalId": "exp-s-1",
|
||||
"measuredAt": "2026-10-12T06:00:00.000Z",
|
||||
"notes": null,
|
||||
"notesEncrypted": null,
|
||||
"glucoseContext": null
|
||||
}
|
||||
],
|
||||
"mood": [
|
||||
{
|
||||
"date": "2026-10-10",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"tags": "slept well",
|
||||
"source": "IMPORT",
|
||||
"externalId": "daylio-1",
|
||||
"moodLoggedAt": "2026-10-10T07:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:08.803Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-11",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-10T22:02:32.701Z"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-12",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"tags": null,
|
||||
"source": "IMPORT",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-12T20:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"measurementRollups": [
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.4
|
||||
},
|
||||
{
|
||||
"type": "WEIGHT",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 81.9
|
||||
},
|
||||
{
|
||||
"type": "SLEEP_DURATION",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 412
|
||||
},
|
||||
{
|
||||
"type": "BLOOD_GLUCOSE",
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"source": "IMPORT",
|
||||
"count": 1,
|
||||
"mean": 104
|
||||
}
|
||||
],
|
||||
"moodRollups": [
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-10T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 4
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-11T00:00:00.000Z",
|
||||
"count": 2,
|
||||
"mean": 3
|
||||
},
|
||||
{
|
||||
"granularity": "DAY",
|
||||
"bucketStart": "2026-10-12T00:00:00.000Z",
|
||||
"count": 1,
|
||||
"mean": 2
|
||||
}
|
||||
],
|
||||
"audit": [
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":4,\"moodEntries\":4,\"skipped\":2}"
|
||||
},
|
||||
{
|
||||
"action": "import.upload",
|
||||
"details": "{\"measurements\":3,\"moodEntries\":1,\"skipped\":6}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* Parity for `POST /api/import` while its writers move into
|
||||
* `src/lib/import/json/`.
|
||||
*
|
||||
* A first file of mixed rows (measurements with and without an external id,
|
||||
* with a note and a glucose context, an unstable id; mood entries with and
|
||||
* without a logged instant, a twin pair that has to land on two distinct
|
||||
* fallback instants, an external id, an unstable id), the same file again,
|
||||
* and the whole-file refusals. After each request the snapshot holds the
|
||||
* response, the wide event, the recorded arrival and refresh tails, and the
|
||||
* stored measurements, mood entries, rollups and audit rows.
|
||||
*/
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { POST } from "@/app/api/import/route";
|
||||
|
||||
import { callRoute, type RouteAuth } from "../../../kit/call-route";
|
||||
import { makeSession, makeUser } from "../../../kit/factories";
|
||||
import {
|
||||
effectRecorder,
|
||||
eventFacts,
|
||||
expectParity,
|
||||
stable,
|
||||
wideEventRecorder,
|
||||
} from "../../../kit/parity";
|
||||
import { getPrismaClient, truncateAllTables } from "../../setup";
|
||||
|
||||
vi.mock("@/lib/logging/transports", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("@/lib/logging/transports")>();
|
||||
const { wideEventRecorder: recorder } = await import("../../../kit/parity");
|
||||
return {
|
||||
...actual,
|
||||
emitIfSampled: (event: (typeof recorder.events)[number]) => {
|
||||
recorder.events.push(event);
|
||||
},
|
||||
};
|
||||
});
|
||||
vi.mock("@/lib/arrivals/emit-shared", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("@/lib/arrivals/emit-shared")>();
|
||||
const { effectRecorder } = await import("../../../kit/parity");
|
||||
return {
|
||||
...actual,
|
||||
emitDataArrival: async (...args: unknown[]) => {
|
||||
effectRecorder.record("emitDataArrival", args);
|
||||
},
|
||||
};
|
||||
});
|
||||
vi.mock("@/lib/daily/morning-refresh-trigger", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<
|
||||
typeof import("@/lib/daily/morning-refresh-trigger")
|
||||
>();
|
||||
const { effectRecorder } = await import("../../../kit/parity");
|
||||
return {
|
||||
...actual,
|
||||
maybeEnqueueMorningRefresh: async (...args: unknown[]) => {
|
||||
effectRecorder.record("maybeEnqueueMorningRefresh", args);
|
||||
},
|
||||
};
|
||||
});
|
||||
vi.mock("@/lib/jobs/pr-detection", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("@/lib/jobs/pr-detection")>();
|
||||
const { effectRecorder } = await import("../../../kit/parity");
|
||||
return {
|
||||
...actual,
|
||||
enqueuePrDetection: async (...args: unknown[]) => {
|
||||
effectRecorder.record("enqueuePrDetection", args);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const prisma = getPrismaClient();
|
||||
const NOW = new Date("2026-10-15T12:00:00.000Z");
|
||||
|
||||
let userId: string;
|
||||
let owner: RouteAuth;
|
||||
|
||||
async function stored() {
|
||||
const [measurements, mood, measurementRollups, moodRollups, audit] =
|
||||
await Promise.all([
|
||||
prisma.measurement.findMany({
|
||||
where: { userId },
|
||||
orderBy: [{ measuredAt: "asc" }, { type: "asc" }, { value: "asc" }],
|
||||
select: {
|
||||
type: true,
|
||||
value: true,
|
||||
unit: true,
|
||||
source: true,
|
||||
externalId: true,
|
||||
measuredAt: true,
|
||||
notes: true,
|
||||
notesEncrypted: true,
|
||||
glucoseContext: true,
|
||||
},
|
||||
}),
|
||||
prisma.moodEntry.findMany({
|
||||
where: { userId },
|
||||
orderBy: [{ moodLoggedAt: "asc" }, { mood: "asc" }],
|
||||
select: {
|
||||
date: true,
|
||||
mood: true,
|
||||
score: true,
|
||||
moodA1: true,
|
||||
tags: true,
|
||||
source: true,
|
||||
externalId: true,
|
||||
moodLoggedAt: true,
|
||||
},
|
||||
}),
|
||||
prisma.measurementRollup.findMany({
|
||||
where: { userId },
|
||||
orderBy: [
|
||||
{ type: "asc" },
|
||||
{ granularity: "asc" },
|
||||
{ bucketStart: "asc" },
|
||||
],
|
||||
select: {
|
||||
type: true,
|
||||
granularity: true,
|
||||
bucketStart: true,
|
||||
source: true,
|
||||
count: true,
|
||||
mean: true,
|
||||
},
|
||||
}),
|
||||
prisma.moodEntryRollup.findMany({
|
||||
where: { userId },
|
||||
orderBy: [{ granularity: "asc" }, { bucketStart: "asc" }],
|
||||
select: {
|
||||
granularity: true,
|
||||
bucketStart: true,
|
||||
count: true,
|
||||
mean: true,
|
||||
},
|
||||
}),
|
||||
prisma.auditLog.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: "asc" },
|
||||
select: { action: true, details: true },
|
||||
}),
|
||||
]);
|
||||
return { measurements, mood, measurementRollups, moodRollups, audit };
|
||||
}
|
||||
|
||||
async function step(name: string, body: unknown): Promise<void> {
|
||||
wideEventRecorder.take();
|
||||
effectRecorder.take();
|
||||
const res = await callRoute(POST, {
|
||||
method: "POST",
|
||||
route: "/api/import",
|
||||
body,
|
||||
auth: owner,
|
||||
});
|
||||
const events = wideEventRecorder.take();
|
||||
expect(events.length).toBeGreaterThan(0);
|
||||
await expectParity(
|
||||
`import-json.${name}`,
|
||||
stable(
|
||||
{
|
||||
status: res.status,
|
||||
body: res.body,
|
||||
events: events.map(eventFacts),
|
||||
effects: effectRecorder.take(),
|
||||
stored: await stored(),
|
||||
},
|
||||
{ since: NOW },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const FILE = {
|
||||
measurements: [
|
||||
{
|
||||
type: "WEIGHT",
|
||||
value: 81.4,
|
||||
unit: "kg",
|
||||
measuredAt: "2026-10-10T06:30:00.000Z",
|
||||
notes: "after the run",
|
||||
externalId: "exp-w-1",
|
||||
},
|
||||
{
|
||||
type: "WEIGHT",
|
||||
value: 81.9,
|
||||
unit: "kg",
|
||||
measuredAt: "2026-10-11T06:30:00.000Z",
|
||||
},
|
||||
{
|
||||
type: "BLOOD_GLUCOSE",
|
||||
value: 104,
|
||||
unit: "mg/dL",
|
||||
measuredAt: "2026-10-11T08:00:00.000Z",
|
||||
glucoseContext: "FASTING",
|
||||
externalId: "exp-g-1",
|
||||
},
|
||||
{
|
||||
type: "SLEEP_DURATION",
|
||||
value: 412,
|
||||
unit: "min",
|
||||
measuredAt: "2026-10-12T06:00:00.000Z",
|
||||
externalId: "exp-s-1",
|
||||
},
|
||||
{
|
||||
type: "PULSE",
|
||||
value: 64,
|
||||
unit: "bpm",
|
||||
measuredAt: "2026-10-12T07:00:00.000Z",
|
||||
externalId: "<HKQuantitySample: 0x600001b4c2a0>",
|
||||
},
|
||||
],
|
||||
moodEntries: [
|
||||
{
|
||||
date: "2026-10-10",
|
||||
mood: "GUT",
|
||||
score: 4,
|
||||
tags: "slept well",
|
||||
loggedAt: "2026-10-10T07:00:00.000Z",
|
||||
externalId: "daylio-1",
|
||||
},
|
||||
{ date: "2026-10-11", mood: "OKAY", score: 3 },
|
||||
{ date: "2026-10-11", mood: "OKAY", score: 3 },
|
||||
{
|
||||
date: "2026-10-12",
|
||||
mood: "SCHLECHT",
|
||||
score: 2,
|
||||
loggedAt: "2026-10-12T20:00:00.000Z",
|
||||
},
|
||||
{
|
||||
date: "2026-10-13",
|
||||
mood: "GUT",
|
||||
score: 5,
|
||||
externalId: "<MoodEntry: 0x600001b4c2a0>",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.useFakeTimers({ toFake: ["Date"], now: NOW });
|
||||
await truncateAllTables(prisma);
|
||||
const user = await makeUser({ timezone: "Europe/Berlin" });
|
||||
userId = user.id;
|
||||
owner = await makeSession(user);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe("POST /api/import parity", () => {
|
||||
it("01 a first file", async () => {
|
||||
await step("01-first-file", FILE);
|
||||
});
|
||||
|
||||
it("02 the same file again", async () => {
|
||||
await step("02-same-file", FILE);
|
||||
});
|
||||
|
||||
it("03 the whole-file refusals", async () => {
|
||||
await step("03-out-of-range", {
|
||||
measurements: [
|
||||
{
|
||||
type: "WEIGHT",
|
||||
value: 4_000,
|
||||
unit: "kg",
|
||||
measuredAt: "2026-10-10T06:30:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
await step("04-forward-dated", {
|
||||
moodEntries: [
|
||||
{
|
||||
date: "2026-10-20",
|
||||
mood: "GUT",
|
||||
score: 4,
|
||||
loggedAt: "2026-10-20T07:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user