mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
test(mood): pin the bulk mood ingest before moving it into a service
This commit is contained in:
1 parent
da937718bc
commit
74e08e6906
6 files changed
+1179
No files matched your search
+224
@@ -0,0 +1,224 @@
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"data": {
|
||||
"processed": 7,
|
||||
"inserted": 4,
|
||||
"duplicates": 0,
|
||||
"skipped": [
|
||||
{
|
||||
"index": 3,
|
||||
"reason": "Rating 3 for factor \"factor_parity_short\" is outside its scale 1..2"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"reason": "invalid_dimensions"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"reason": "unstable_external_id"
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"index": 0,
|
||||
"status": "inserted",
|
||||
"id": "<id:1>",
|
||||
"externalId": "sd-1",
|
||||
"droppedTagKeys": [
|
||||
"no_such_tag"
|
||||
],
|
||||
"droppedFactorKeys": [
|
||||
"<custom-tag-key>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"status": "inserted",
|
||||
"id": "<id:2>"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"status": "inserted",
|
||||
"id": "<id:3>",
|
||||
"externalId": "sd-2"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"status": "skipped",
|
||||
"reason": "Rating 3 for factor \"factor_parity_short\" is outside its scale 1..2",
|
||||
"externalId": "sd-3"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"status": "inserted",
|
||||
"id": "<id:4>",
|
||||
"externalId": "sd-4"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"status": "skipped",
|
||||
"reason": "invalid_dimensions",
|
||||
"externalId": "sd-5"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"status": "skipped",
|
||||
"reason": "unstable_external_id",
|
||||
"externalId": "<MoodEntry: 0x600001b4c2a0>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 200,
|
||||
"action": {
|
||||
"name": "mood.bulk.ingest"
|
||||
},
|
||||
"meta": {
|
||||
"write_bucket": "own",
|
||||
"processed": 7,
|
||||
"inserted": 4,
|
||||
"duplicates": 0,
|
||||
"skipped": 3,
|
||||
"dropped_tag_keys": 1,
|
||||
"dropped_factor_keys": 1,
|
||||
"external_id_rejected": 1,
|
||||
"external_id_shapes": "object_description",
|
||||
"external_id_surface": "mood.bulk",
|
||||
"sync_checkpoint_at": "<now>"
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"entries": [
|
||||
{
|
||||
"id": "<id:4>",
|
||||
"date": "2026-10-12",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-4",
|
||||
"moodLoggedAt": "2026-10-12T10:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:3>",
|
||||
"date": "2026-10-13",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-2",
|
||||
"moodLoggedAt": "2026-10-13T21:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": {
|
||||
"moodEntryId": "<id:3>",
|
||||
"userId": "<id:5>",
|
||||
"workStatus": "regular",
|
||||
"workMinutes": 480,
|
||||
"overtimeMinutes": null,
|
||||
"workLoad": null,
|
||||
"workSatisfaction": null,
|
||||
"contactCircles": null,
|
||||
"contactForm": null,
|
||||
"contactExtent": null,
|
||||
"contactQuality": null,
|
||||
"contactSupport": null,
|
||||
"leisureCategories": null,
|
||||
"leisureMinutes": null,
|
||||
"leisureJoy": null,
|
||||
"leisureRecovery": null,
|
||||
"eventType": null,
|
||||
"eventValence": null,
|
||||
"eventAt": null,
|
||||
"notesEncrypted": null,
|
||||
"createdAt": "<createdAt>",
|
||||
"updatedAt": "<updatedAt>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "<id:1>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": 7,
|
||||
"energyA3": 4,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": "[\"slept well\"]",
|
||||
"note": null,
|
||||
"noteEncrypted": "<bytes:59>",
|
||||
"source": "WEB",
|
||||
"externalId": "sd-1",
|
||||
"moodLoggedAt": "2026-10-14T07:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "alcohol"
|
||||
},
|
||||
"rating": null
|
||||
},
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "factor_parity_short"
|
||||
},
|
||||
"rating": 1
|
||||
}
|
||||
],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:2>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-14T19:30:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"lastSyncedAt": "<now>"
|
||||
}
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"data": {
|
||||
"processed": 7,
|
||||
"inserted": 0,
|
||||
"duplicates": 4,
|
||||
"skipped": [
|
||||
{
|
||||
"index": 3,
|
||||
"reason": "Rating 3 for factor \"factor_parity_short\" is outside its scale 1..2"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"reason": "invalid_dimensions"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"reason": "unstable_external_id"
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"index": 0,
|
||||
"status": "duplicate",
|
||||
"id": "<id:1>",
|
||||
"externalId": "sd-1",
|
||||
"droppedTagKeys": [
|
||||
"no_such_tag"
|
||||
],
|
||||
"droppedFactorKeys": [
|
||||
"<custom-tag-key>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"status": "duplicate",
|
||||
"id": "<id:2>"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"status": "duplicate",
|
||||
"id": "<id:3>",
|
||||
"externalId": "sd-2"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"status": "skipped",
|
||||
"reason": "Rating 3 for factor \"factor_parity_short\" is outside its scale 1..2",
|
||||
"externalId": "sd-3"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"status": "duplicate",
|
||||
"id": "<id:4>",
|
||||
"externalId": "sd-4"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"status": "skipped",
|
||||
"reason": "invalid_dimensions",
|
||||
"externalId": "sd-5"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"status": "skipped",
|
||||
"reason": "unstable_external_id",
|
||||
"externalId": "<MoodEntry: 0x600001b4c2a0>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 200,
|
||||
"action": {
|
||||
"name": "mood.bulk.ingest"
|
||||
},
|
||||
"meta": {
|
||||
"write_bucket": "own",
|
||||
"processed": 7,
|
||||
"inserted": 0,
|
||||
"duplicates": 4,
|
||||
"skipped": 3,
|
||||
"dropped_tag_keys": 1,
|
||||
"dropped_factor_keys": 1,
|
||||
"external_id_rejected": 1,
|
||||
"external_id_shapes": "object_description",
|
||||
"external_id_surface": "mood.bulk",
|
||||
"sync_checkpoint_at": "<now>"
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"entries": [
|
||||
{
|
||||
"id": "<id:4>",
|
||||
"date": "2026-10-12",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-4",
|
||||
"moodLoggedAt": "2026-10-12T10:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:3>",
|
||||
"date": "2026-10-13",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-2",
|
||||
"moodLoggedAt": "2026-10-13T21:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": {
|
||||
"moodEntryId": "<id:3>",
|
||||
"userId": "<id:5>",
|
||||
"workStatus": "regular",
|
||||
"workMinutes": 480,
|
||||
"overtimeMinutes": null,
|
||||
"workLoad": null,
|
||||
"workSatisfaction": null,
|
||||
"contactCircles": null,
|
||||
"contactForm": null,
|
||||
"contactExtent": null,
|
||||
"contactQuality": null,
|
||||
"contactSupport": null,
|
||||
"leisureCategories": null,
|
||||
"leisureMinutes": null,
|
||||
"leisureJoy": null,
|
||||
"leisureRecovery": null,
|
||||
"eventType": null,
|
||||
"eventValence": null,
|
||||
"eventAt": null,
|
||||
"notesEncrypted": null,
|
||||
"createdAt": "<createdAt>",
|
||||
"updatedAt": "<updatedAt>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "<id:1>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": 7,
|
||||
"energyA3": 4,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": "[\"slept well\"]",
|
||||
"note": null,
|
||||
"noteEncrypted": "<bytes:59>",
|
||||
"source": "WEB",
|
||||
"externalId": "sd-1",
|
||||
"moodLoggedAt": "2026-10-14T07:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "alcohol"
|
||||
},
|
||||
"rating": null
|
||||
},
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "factor_parity_short"
|
||||
},
|
||||
"rating": 1
|
||||
}
|
||||
],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:2>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-14T19:30:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"lastSyncedAt": "<now>"
|
||||
}
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"data": {
|
||||
"processed": 1,
|
||||
"inserted": 0,
|
||||
"duplicates": 1,
|
||||
"skipped": [],
|
||||
"entries": [
|
||||
{
|
||||
"index": 0,
|
||||
"status": "duplicate",
|
||||
"id": "<id:1>",
|
||||
"externalId": "sd-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 200,
|
||||
"action": {
|
||||
"name": "mood.bulk.ingest"
|
||||
},
|
||||
"meta": {
|
||||
"write_bucket": "own",
|
||||
"processed": 1,
|
||||
"inserted": 0,
|
||||
"duplicates": 1,
|
||||
"skipped": 0,
|
||||
"dropped_tag_keys": 0,
|
||||
"dropped_factor_keys": 0,
|
||||
"sync_checkpoint_at": "<now>"
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"entries": [
|
||||
{
|
||||
"id": "<id:2>",
|
||||
"date": "2026-10-12",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-4",
|
||||
"moodLoggedAt": "2026-10-12T10:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:1>",
|
||||
"date": "2026-10-13",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-2",
|
||||
"moodLoggedAt": "2026-10-13T21:00:00.000Z",
|
||||
"deletedAt": "2026-10-14T20:00:00.000Z",
|
||||
"tagLinks": [],
|
||||
"context": {
|
||||
"moodEntryId": "<id:1>",
|
||||
"userId": "<id:3>",
|
||||
"workStatus": "regular",
|
||||
"workMinutes": 480,
|
||||
"overtimeMinutes": null,
|
||||
"workLoad": null,
|
||||
"workSatisfaction": null,
|
||||
"contactCircles": null,
|
||||
"contactForm": null,
|
||||
"contactExtent": null,
|
||||
"contactQuality": null,
|
||||
"contactSupport": null,
|
||||
"leisureCategories": null,
|
||||
"leisureMinutes": null,
|
||||
"leisureJoy": null,
|
||||
"leisureRecovery": null,
|
||||
"eventType": null,
|
||||
"eventValence": null,
|
||||
"eventAt": null,
|
||||
"notesEncrypted": null,
|
||||
"createdAt": "<createdAt>",
|
||||
"updatedAt": "<updatedAt>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "<id:4>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": 7,
|
||||
"energyA3": 4,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": "[\"slept well\"]",
|
||||
"note": null,
|
||||
"noteEncrypted": "<bytes:59>",
|
||||
"source": "WEB",
|
||||
"externalId": "sd-1",
|
||||
"moodLoggedAt": "2026-10-14T07:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "alcohol"
|
||||
},
|
||||
"rating": null
|
||||
},
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "factor_parity_short"
|
||||
},
|
||||
"rating": 1
|
||||
}
|
||||
],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:5>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-14T19:30:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"lastSyncedAt": "<now>"
|
||||
}
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"status": 422,
|
||||
"body": {
|
||||
"data": null,
|
||||
"error": "Batch exceeds the 500-entry limit",
|
||||
"meta": {
|
||||
"errorCode": "mood.bulk.too_large",
|
||||
"requestId": "<requestId>"
|
||||
}
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 422,
|
||||
"action": null,
|
||||
"meta": {
|
||||
"write_bucket": "own"
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"entries": [
|
||||
{
|
||||
"id": "<id:1>",
|
||||
"date": "2026-10-12",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-4",
|
||||
"moodLoggedAt": "2026-10-12T10:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:2>",
|
||||
"date": "2026-10-13",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-2",
|
||||
"moodLoggedAt": "2026-10-13T21:00:00.000Z",
|
||||
"deletedAt": "2026-10-14T20:00:00.000Z",
|
||||
"tagLinks": [],
|
||||
"context": {
|
||||
"moodEntryId": "<id:2>",
|
||||
"userId": "<id:3>",
|
||||
"workStatus": "regular",
|
||||
"workMinutes": 480,
|
||||
"overtimeMinutes": null,
|
||||
"workLoad": null,
|
||||
"workSatisfaction": null,
|
||||
"contactCircles": null,
|
||||
"contactForm": null,
|
||||
"contactExtent": null,
|
||||
"contactQuality": null,
|
||||
"contactSupport": null,
|
||||
"leisureCategories": null,
|
||||
"leisureMinutes": null,
|
||||
"leisureJoy": null,
|
||||
"leisureRecovery": null,
|
||||
"eventType": null,
|
||||
"eventValence": null,
|
||||
"eventAt": null,
|
||||
"notesEncrypted": null,
|
||||
"createdAt": "<createdAt>",
|
||||
"updatedAt": "<updatedAt>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "<id:4>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": 7,
|
||||
"energyA3": 4,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": "[\"slept well\"]",
|
||||
"note": null,
|
||||
"noteEncrypted": "<bytes:59>",
|
||||
"source": "WEB",
|
||||
"externalId": "sd-1",
|
||||
"moodLoggedAt": "2026-10-14T07:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "alcohol"
|
||||
},
|
||||
"rating": null
|
||||
},
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "factor_parity_short"
|
||||
},
|
||||
"rating": 1
|
||||
}
|
||||
],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:5>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-14T19:30:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"lastSyncedAt": "<now>"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
{
|
||||
"status": 422,
|
||||
"body": {
|
||||
"data": null,
|
||||
"error": "Validation failed",
|
||||
"details": {
|
||||
"issues": [
|
||||
{
|
||||
"path": "entries.0.mood",
|
||||
"code": "invalid_value",
|
||||
"message": "Invalid option: expected one of \"SUPER_GUT\"|\"GUT\"|\"OKAY\"|\"SCHLECHT\"|\"LAUSIG\""
|
||||
},
|
||||
{
|
||||
"path": "entries.0.moodLoggedAt",
|
||||
"code": "invalid_type",
|
||||
"message": "Invalid input: expected string, received undefined"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"errorCode": "mood.bulk.invalid",
|
||||
"requestId": "<requestId>"
|
||||
}
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"status": 422,
|
||||
"action": {
|
||||
"name": "mood.bulk.validation-failed"
|
||||
},
|
||||
"meta": {
|
||||
"write_bucket": "own",
|
||||
"issue_count": 2
|
||||
},
|
||||
"warnings": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"stored": {
|
||||
"entries": [
|
||||
{
|
||||
"id": "<id:1>",
|
||||
"date": "2026-10-12",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-4",
|
||||
"moodLoggedAt": "2026-10-12T10:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:2>",
|
||||
"date": "2026-10-13",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "SCHLECHT",
|
||||
"score": 2,
|
||||
"moodA1": 3,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": "sd-2",
|
||||
"moodLoggedAt": "2026-10-13T21:00:00.000Z",
|
||||
"deletedAt": "2026-10-14T20:00:00.000Z",
|
||||
"tagLinks": [],
|
||||
"context": {
|
||||
"moodEntryId": "<id:2>",
|
||||
"userId": "<id:3>",
|
||||
"workStatus": "regular",
|
||||
"workMinutes": 480,
|
||||
"overtimeMinutes": null,
|
||||
"workLoad": null,
|
||||
"workSatisfaction": null,
|
||||
"contactCircles": null,
|
||||
"contactForm": null,
|
||||
"contactExtent": null,
|
||||
"contactQuality": null,
|
||||
"contactSupport": null,
|
||||
"leisureCategories": null,
|
||||
"leisureMinutes": null,
|
||||
"leisureJoy": null,
|
||||
"leisureRecovery": null,
|
||||
"eventType": null,
|
||||
"eventValence": null,
|
||||
"eventAt": null,
|
||||
"notesEncrypted": null,
|
||||
"createdAt": "<createdAt>",
|
||||
"updatedAt": "<updatedAt>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "<id:4>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "GUT",
|
||||
"score": 4,
|
||||
"moodA1": 7,
|
||||
"stressA2": 7,
|
||||
"energyA3": 4,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": "[\"slept well\"]",
|
||||
"note": null,
|
||||
"noteEncrypted": "<bytes:59>",
|
||||
"source": "WEB",
|
||||
"externalId": "sd-1",
|
||||
"moodLoggedAt": "2026-10-14T07:00:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "alcohol"
|
||||
},
|
||||
"rating": null
|
||||
},
|
||||
{
|
||||
"moodTag": {
|
||||
"key": "factor_parity_short"
|
||||
},
|
||||
"rating": 1
|
||||
}
|
||||
],
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"id": "<id:5>",
|
||||
"date": "2026-10-14",
|
||||
"tz": "Europe/Berlin",
|
||||
"mood": "OKAY",
|
||||
"score": 3,
|
||||
"moodA1": 5,
|
||||
"stressA2": null,
|
||||
"energyA3": null,
|
||||
"connectionA4": null,
|
||||
"stabilityA5": null,
|
||||
"tags": null,
|
||||
"note": null,
|
||||
"noteEncrypted": null,
|
||||
"source": "MANUAL",
|
||||
"externalId": null,
|
||||
"moodLoggedAt": "2026-10-14T19:30:00.000Z",
|
||||
"deletedAt": null,
|
||||
"tagLinks": [],
|
||||
"context": null
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"lastSyncedAt": "<now>"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Parity for `POST /api/mood-entries/bulk` while its ingest moves into
|
||||
* `src/lib/mood/`.
|
||||
*
|
||||
* A first drain of mixed entries (with and without an external id, free
|
||||
* tags, catalogue tags with an unknown key, a rated factor in and out of its
|
||||
* scale, a day context, dimensions in and out of range, an unstable id),
|
||||
* the same drain again, a re-post of an entry the person deleted, and the
|
||||
* two whole-batch refusals. After each request the snapshot holds the
|
||||
* response, the wide event and the stored entries with their links and
|
||||
* context, a sealed note as its byte length.
|
||||
*/
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { POST } from "@/app/api/mood-entries/bulk/route";
|
||||
|
||||
import { callRoute, type RouteAuth } from "../../../kit/call-route";
|
||||
import { makeSession, makeUser } from "../../../kit/factories";
|
||||
import {
|
||||
eventFacts,
|
||||
expectParity,
|
||||
stable,
|
||||
wideEventRecorder,
|
||||
} from "../../../kit/parity";
|
||||
import { getPrismaClient, truncateAllTables } from "../../setup";
|
||||
import { makeCustomTag } from "../mood-tag-fixtures";
|
||||
|
||||
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);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const prisma = getPrismaClient();
|
||||
const NOW = new Date("2026-10-15T12:00:00.000Z");
|
||||
const SHORT_FACTOR_ID = "mt_parity_short_factor";
|
||||
|
||||
let userId: string;
|
||||
let owner: RouteAuth;
|
||||
let binaryKey: string;
|
||||
let rated: { key: string; scaleMax: number };
|
||||
let customKey: string;
|
||||
|
||||
async function stored() {
|
||||
const [entries, user] = await Promise.all([
|
||||
prisma.moodEntry.findMany({
|
||||
where: { userId },
|
||||
orderBy: [{ moodLoggedAt: "asc" }, { source: "asc" }],
|
||||
select: {
|
||||
id: true,
|
||||
date: true,
|
||||
tz: true,
|
||||
mood: true,
|
||||
score: true,
|
||||
moodA1: true,
|
||||
stressA2: true,
|
||||
energyA3: true,
|
||||
connectionA4: true,
|
||||
stabilityA5: true,
|
||||
tags: true,
|
||||
note: true,
|
||||
noteEncrypted: true,
|
||||
source: true,
|
||||
externalId: true,
|
||||
moodLoggedAt: true,
|
||||
deletedAt: true,
|
||||
tagLinks: {
|
||||
orderBy: { moodTag: { key: "asc" } },
|
||||
select: { moodTag: { select: { key: true } }, rating: true },
|
||||
},
|
||||
context: true,
|
||||
},
|
||||
}),
|
||||
prisma.user.findUniqueOrThrow({
|
||||
where: { id: userId },
|
||||
select: { lastSyncedAt: true },
|
||||
}),
|
||||
]);
|
||||
return { entries, user };
|
||||
}
|
||||
|
||||
async function step(name: string, body: unknown): Promise<void> {
|
||||
wideEventRecorder.take();
|
||||
const res = await callRoute(POST, {
|
||||
method: "POST",
|
||||
route: "/api/mood-entries/bulk",
|
||||
body,
|
||||
auth: owner,
|
||||
});
|
||||
const events = wideEventRecorder.take();
|
||||
expect(events.length).toBeGreaterThan(0);
|
||||
await expectParity(
|
||||
`mood-entries-bulk.${name}`,
|
||||
withoutMintedKey(
|
||||
stable(
|
||||
{
|
||||
status: res.status,
|
||||
body: res.body,
|
||||
events: events.map(eventFacts),
|
||||
stored: await stored(),
|
||||
},
|
||||
{ since: NOW },
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The account's own tag carries a key minted at random (`custom:<uuid>`);
|
||||
* every occurrence is written as one placeholder so the snapshot is the same
|
||||
* on each run.
|
||||
*/
|
||||
function withoutMintedKey(value: unknown): unknown {
|
||||
return JSON.parse(
|
||||
JSON.stringify(value).split(customKey).join("<custom-tag-key>"),
|
||||
);
|
||||
}
|
||||
|
||||
function entry(
|
||||
at: string,
|
||||
mood: string,
|
||||
extra: Record<string, unknown> = {},
|
||||
): Record<string, unknown> {
|
||||
return { mood, moodLoggedAt: at, ...extra };
|
||||
}
|
||||
|
||||
let firstDrain: Record<string, unknown>[];
|
||||
|
||||
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);
|
||||
const binary = await prisma.moodTag.findFirstOrThrow({
|
||||
where: { userId: null, kind: "BINARY", isActive: true },
|
||||
orderBy: { key: "asc" },
|
||||
select: { key: true },
|
||||
});
|
||||
binaryKey = binary.key;
|
||||
// A catalogue factor on a scale shorter than the envelope's 1..5, so a
|
||||
// rating the envelope admits can still fall outside the factor's scale.
|
||||
// Every live catalogue factor runs 1..5, so this file adds one with a
|
||||
// fixed key and removes it afterwards.
|
||||
const ratedTag = await prisma.moodTag.create({
|
||||
data: {
|
||||
id: SHORT_FACTOR_ID,
|
||||
key: "factor_parity_short",
|
||||
labelKey: "mood.tag.factorParityShort",
|
||||
categoryId: "mtc_factors",
|
||||
kind: "RATED",
|
||||
scaleMin: 1,
|
||||
scaleMax: 2,
|
||||
},
|
||||
select: { key: true, scaleMax: true },
|
||||
});
|
||||
rated = ratedTag;
|
||||
// Rated factors resolve against the catalogue only, so one the account
|
||||
// made itself is reported as dropped.
|
||||
const custom = await makeCustomTag(user, "sleep debt", {
|
||||
kind: "RATED",
|
||||
scaleMin: 1,
|
||||
scaleMax: 2,
|
||||
});
|
||||
customKey = custom.key;
|
||||
firstDrain = [
|
||||
entry("2026-10-14T07:00:00.000Z", "GUT", {
|
||||
externalId: "sd-1",
|
||||
source: "WEB",
|
||||
tags: ["slept well"],
|
||||
tagKeys: [binaryKey, "no_such_tag"],
|
||||
ratedFactors: [
|
||||
{ key: rated.key, rating: 1 },
|
||||
{ key: customKey, rating: 1 },
|
||||
],
|
||||
note: "a calm morning",
|
||||
a2: 7,
|
||||
a3: 4,
|
||||
}),
|
||||
entry("2026-10-14T19:30:00.000Z", "OKAY"),
|
||||
entry("2026-10-13T21:00:00.000Z", "SCHLECHT", {
|
||||
externalId: "sd-2",
|
||||
context: { workStatus: "regular", workMinutes: 480 },
|
||||
}),
|
||||
entry("2026-10-12T09:00:00.000Z", "SUPER_GUT", {
|
||||
externalId: "sd-3",
|
||||
ratedFactors: [{ key: rated.key, rating: rated.scaleMax + 1 }],
|
||||
}),
|
||||
entry("2026-10-12T10:00:00.000Z", "GUT", {
|
||||
externalId: "sd-4",
|
||||
context: { notAKey: true },
|
||||
}),
|
||||
entry("2026-10-12T11:00:00.000Z", "GUT", {
|
||||
externalId: "sd-5",
|
||||
a4: 11,
|
||||
}),
|
||||
entry("2026-10-12T12:00:00.000Z", "GUT", {
|
||||
externalId: "<MoodEntry: 0x600001b4c2a0>",
|
||||
}),
|
||||
];
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
vi.useRealTimers();
|
||||
await prisma.moodEntryTagLink.deleteMany({
|
||||
where: { moodTagId: SHORT_FACTOR_ID },
|
||||
});
|
||||
await prisma.moodTag.delete({ where: { id: SHORT_FACTOR_ID } });
|
||||
});
|
||||
|
||||
describe("POST /api/mood-entries/bulk parity", () => {
|
||||
it("01 a first drain", async () => {
|
||||
await step("01-first-drain", { entries: firstDrain });
|
||||
});
|
||||
|
||||
it("02 the same drain again", async () => {
|
||||
await step("02-same-drain", { entries: firstDrain });
|
||||
});
|
||||
|
||||
it("03 a re-post of an entry the person deleted", async () => {
|
||||
await prisma.moodEntry.updateMany({
|
||||
where: { userId, externalId: "sd-2" },
|
||||
data: { deletedAt: new Date("2026-10-14T20:00:00.000Z") },
|
||||
});
|
||||
await step("03-tombstone", {
|
||||
entries: [
|
||||
entry("2026-10-13T21:00:00.000Z", "OKAY", { externalId: "sd-2" }),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("04 the whole-batch refusals", async () => {
|
||||
await step("04-too-large", {
|
||||
entries: Array.from({ length: 501 }, (_, i) =>
|
||||
entry(
|
||||
new Date(NOW.getTime() - (i + 1) * 3_600_000).toISOString(),
|
||||
"OKAY",
|
||||
),
|
||||
),
|
||||
});
|
||||
await step("05-invalid", { entries: [{ mood: "ECSTATIC" }] });
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user