Project

General

Profile

QA验收 #1 » http-integration-results.json

Redmine Admin, 10/06/2026 04:31 PM

 
{
"run": "2026-10-07 isolated real HTTP",
"ports": {
"postgres": 56488,
"backend": 18000,
"bff": 13000,
"notificationMock": 13001
},
"fixture": {
"meetings": {
"A_started": 1,
"B1_minutes_attend": 2,
"B2_attend_minutes": 3,
"D_paid_no_contract": 4
},
"contact_id": 2,
"contract_count": 0
},
"checks": [
{
"id": "HTTP-001",
"name": "Real NDB+BFF channel readiness",
"precondition": "Dedicated ports/databases",
"action": "GET /health/ready",
"expected": "NDB signed ping and PG both up",
"at": "2026-10-06T16:29:26.804Z",
"actual": {
"status": "ok",
"channelDb": "up",
"ndb": "up"
},
"status": "Pass"
},
{
"id": "HTTP-002",
"name": "Synthetic zero-contract empty baseline",
"precondition": "Fresh migration and seed",
"action": "Read isolated ORM snapshot",
"expected": "0 Registration, 0 Tasks, 0 Contracts",
"at": "2026-10-06T16:29:26.804Z",
"actual": {
"registrations": [],
"tasks": [],
"contract_count": 0
},
"status": "Pass"
},
{
"id": "HTTP-003",
"name": "Anonymous minutes write denied",
"precondition": "No JWT",
"action": "PUT A minutes",
"expected": "401 UNAUTHENTICATED, no mutation",
"at": "2026-10-06T16:29:27.556Z",
"actual": {
"status": 401,
"data": {
"error": {
"code": "UNAUTHENTICATED",
"message": "请先登录"
},
"requestId": "6def3d9e-6172-415e-863e-151132724f73"
}
},
"status": "Pass"
},
{
"id": "HTTP-004",
"name": "Unbound synthetic JWT denied",
"precondition": "User lacks phone/contact",
"action": "PUT A minutes",
"expected": "403 CONTACT_NOT_BOUND",
"at": "2026-10-06T16:29:27.558Z",
"actual": {
"status": 403,
"data": {
"error": {
"code": "CONTACT_NOT_BOUND",
"message": "请先绑定客户联系人"
},
"requestId": "682e586e-8537-4a7a-95e8-eafc68a29d72"
}
},
"status": "Pass"
},
{
"id": "HTTP-005",
"name": "Legacy subscription is not reservation",
"precondition": "Only old channel subscription seeded",
"action": "GET A minutes",
"expected": "JSON null, no NDB Task",
"at": "2026-10-06T16:29:27.567Z",
"actual": {
"reservation": null,
"taskCount": 0
},
"status": "Pass"
},
{
"id": "HTTP-006",
"name": "A started free minutes-only created",
"precondition": "A started,0h,no contract",
"action": "PUT A minutes",
"expected": "reserved+unified Registration/Task",
"at": "2026-10-06T16:29:28.357Z",
"actual": {
"id": "1",
"meetingId": "1",
"clientContactId": "2",
"taskId": "1",
"status": "reserved",
"createdAt": "2026-10-07T00:29:28.409211"
},
"status": "Pass"
},
{
"id": "HTTP-007",
"name": "A same-key retry reuses ids",
"precondition": "A reserved",
"action": "Repeat PUT same key",
"expected": "Same registration/task ids",
"at": "2026-10-06T16:29:28.414Z",
"actual": {
"id": "1",
"meetingId": "1",
"clientContactId": "2",
"taskId": "1",
"status": "reserved",
"createdAt": "2026-10-07T00:29:28.409211"
},
"status": "Pass"
},
{
"id": "HTTP-008",
"name": "A authoritative GET preserves server naive microseconds",
"precondition": "A reserved after replay",
"action": "GET A minutes",
"expected": "same ids and exact createdAt string",
"at": "2026-10-06T16:29:28.452Z",
"actual": {
"id": "1",
"meetingId": "1",
"clientContactId": "2",
"taskId": "1",
"status": "reserved",
"createdAt": "2026-10-07T00:29:28.409211"
},
"status": "Pass"
},
{
"id": "HTTP-009",
"name": "A concurrent natural retries on real PG",
"precondition": "A reserved; threaded real Backend",
"action": "6 parallel PUT with distinct keys",
"expected": "All same ids and only one Task",
"at": "2026-10-06T16:29:28.487Z",
"actual": {
"responses": 6,
"registrationId": "1",
"taskId": "1",
"taskCount": 1
},
"status": "Pass"
},
{
"id": "HTTP-010",
"name": "A minutes-only has no attendance projection",
"precondition": "A minutes requested only",
"action": "GET A registration",
"expected": "JSON null",
"at": "2026-10-06T16:29:29.352Z",
"actual": null,
"status": "Pass"
},
{
"id": "HTTP-011",
"name": "A absent myMeetings and present myMinutes",
"precondition": "A minutes-only",
"action": "GET both lists",
"expected": "Attendance empty; minutes contains A",
"at": "2026-10-06T16:29:29.394Z",
"actual": {
"myMeetings": 0,
"myMinutes": 1
},
"status": "Pass"
},
{
"id": "HTTP-012",
"name": "A minutes-only cannot obtain join link",
"precondition": "A already started",
"action": "GET A join-link",
"expected": "visible=false,joinUrl=null",
"at": "2026-10-06T16:29:29.483Z",
"actual": {
"visible": false,
"joinUrl": null,
"reason": "REGISTRATION_REQUIRED"
},
"status": "Pass"
},
{
"id": "HTTP-013",
"name": "A cannot add attendance after start",
"precondition": "A started,minutes-only",
"action": "POST A registration",
"expected": "MEETING_NOT_OPEN without new Task",
"at": "2026-10-06T16:29:29.527Z",
"actual": {
"status": 409,
"data": {
"error": {
"code": "MEETING_NOT_OPEN",
"message": "会议当前不可报名"
},
"requestId": "5f775969-8e2c-48fe-bd92-b16e7b15eb49"
}
},
"status": "Pass"
},
{
"id": "HTTP-014",
"name": "B1 minutes first while not started",
"precondition": "B1 future,0h",
"action": "PUT B1 minutes",
"expected": "reserved",
"at": "2026-10-06T16:29:30.361Z",
"actual": {
"id": "2",
"meetingId": "2",
"clientContactId": "2",
"taskId": "2",
"status": "reserved",
"createdAt": "2026-10-07T00:29:30.412283"
},
"status": "Pass"
},
{
"id": "HTTP-015",
"name": "B1 then attendance shares Registration/Task",
"precondition": "B1 minutes first",
"action": "POST B1 registration + isolated snapshot",
"expected": "same id/task, both flags",
"at": "2026-10-06T16:29:30.416Z",
"actual": {
"projection": {
"id": "2",
"meetingId": "2",
"status": "confirmed"
},
"record": {
"id": 2,
"meeting_id": 2,
"client_contact_id": 2,
"task_id": 2,
"attend_requested": true,
"minutes_requested": true,
"status": "confirmed"
}
},
"status": "Pass"
},
{
"id": "HTTP-016",
"name": "B1 future join link withheld",
"precondition": "B1 both intents before start",
"action": "GET join-link",
"expected": "visible=false",
"at": "2026-10-06T16:29:31.223Z",
"actual": {
"visible": false,
"joinUrl": null,
"reason": "MEETING_NOT_OPEN"
},
"status": "Pass"
},
{
"id": "HTTP-017",
"name": "B2 attendance first while not started",
"precondition": "B2 future,0h",
"action": "POST B2 registration",
"expected": "confirmed",
"at": "2026-10-06T16:29:31.254Z",
"actual": {
"id": "3",
"meetingId": "3",
"status": "confirmed"
},
"status": "Pass"
},
{
"id": "HTTP-018",
"name": "B2 then minutes shares Registration/Task",
"precondition": "B2 attend first",
"action": "PUT B2 minutes + snapshot",
"expected": "same id,task, both flags",
"at": "2026-10-06T16:29:31.301Z",
"actual": {
"reservation": {
"id": "3",
"meetingId": "3",
"clientContactId": "2",
"taskId": "3",
"status": "reserved",
"createdAt": "2026-10-07T00:29:31.297439"
},
"record": {
"id": 3,
"meeting_id": 3,
"client_contact_id": 2,
"task_id": 3,
"attend_requested": true,
"minutes_requested": true,
"status": "confirmed"
}
},
"status": "Pass"
},
{
"id": "HTTP-019",
"name": "Both intentions appear in separate lists",
"precondition": "A minutes-only,B1/B2 both",
"action": "GET both my lists",
"expected": "myMeetings=2,myMinutes=3",
"at": "2026-10-06T16:29:32.908Z",
"actual": {
"myMeetings": 2,
"myMinutes": 3
},
"status": "Pass"
},
{
"id": "HTTP-020",
"name": "Future B1 cancel keeps minutes and Task",
"precondition": "B1 future,both",
"action": "DELETE registration",
"expected": "cancelled;same reserved minutes and ongoing Task",
"at": "2026-10-06T16:29:32.989Z",
"actual": {
"registration": {
"id": "2",
"meetingId": "2",
"status": "cancelled"
},
"reservation": {
"id": "2",
"meetingId": "2",
"clientContactId": "2",
"taskId": "2",
"status": "reserved",
"createdAt": "2026-10-07T00:29:30.412283"
},
"taskStatus": "ongoing"
},
"status": "Pass"
},
{
"id": "HTTP-021",
"name": "Future B1 reattend reuses id/Task",
"precondition": "B1 cancelled but future",
"action": "POST registration fresh key",
"expected": "confirmed,same ids,3 Tasks total",
"at": "2026-10-06T16:29:33.910Z",
"actual": {
"id": "2",
"meetingId": "2",
"status": "confirmed"
},
"status": "Pass"
},
{
"id": "HTTP-022",
"name": "Advance only isolated B fixtures to started",
"precondition": "B1/B2 future registration complete",
"action": "Guarded synthetic ORM time update",
"expected": "Only B1/B2 updated",
"at": "2026-10-06T16:29:34.732Z",
"actual": {
"advanced": [
2,
3
]
},
"status": "Pass"
},
{
"id": "HTTP-023",
"name": "Started attendee B1 obtains link",
"precondition": "B1 advanced,confirmed",
"action": "GET join-link",
"expected": "visible=true synthetic invalid-domain link",
"at": "2026-10-06T16:29:35.508Z",
"actual": {
"visible": true,
"joinUrl": "https://qa-meeting.example.invalid/join/B1_minutes_attend",
"reason": null
},
"status": "Pass"
},
{
"id": "HTTP-024",
"name": "Started attendee B2 obtains link",
"precondition": "B2 advanced,confirmed",
"action": "GET join-link",
"expected": "visible=true",
"at": "2026-10-06T16:29:35.550Z",
"actual": {
"visible": true,
"joinUrl": "https://qa-meeting.example.invalid/join/B2_attend_minutes",
"reason": null
},
"status": "Pass"
},
{
"id": "HTTP-025",
"name": "Started B1 cancel preserves minutes Task",
"precondition": "B1 started,both",
"action": "DELETE registration + snapshot",
"expected": "cancelled and minutes same ids/ongoing Task",
"at": "2026-10-06T16:29:35.586Z",
"actual": {
"registration": {
"id": "2",
"meetingId": "2",
"status": "cancelled"
},
"reservation": {
"id": "2",
"meetingId": "2",
"clientContactId": "2",
"taskId": "2",
"status": "reserved",
"createdAt": "2026-10-07T00:29:30.412283"
}
},
"status": "Pass"
},
{
"id": "HTTP-026",
"name": "Cancelled started B1 loses link",
"precondition": "B1 cancelled,minutes retained",
"action": "GET join-link",
"expected": "visible=false,no link",
"at": "2026-10-06T16:29:36.486Z",
"actual": {
"visible": false,
"joinUrl": null,
"reason": "REGISTRATION_REQUIRED"
},
"status": "Pass"
},
{
"id": "HTTP-027",
"name": "Started B1 cannot restore attendance",
"precondition": "B1 cancelled after start",
"action": "POST attend fresh key",
"expected": "MEETING_NOT_OPEN,no extra Task",
"at": "2026-10-06T16:29:36.520Z",
"actual": {
"status": 409,
"data": {
"error": {
"code": "MEETING_NOT_OPEN",
"message": "会议当前不可报名"
},
"requestId": "f261e04e-d31e-4dbc-ae31-145be5e9c02f"
}
},
"status": "Pass"
},
{
"id": "HTTP-028",
"name": "Started B1 repeated minutes retained",
"precondition": "B1 cancelled attendee,minutes active",
"action": "PUT minutes fresh key",
"expected": "same reserved id/task",
"at": "2026-10-06T16:29:37.344Z",
"actual": {
"id": "2",
"meetingId": "2",
"clientContactId": "2",
"taskId": "2",
"status": "reserved",
"createdAt": "2026-10-07T00:29:30.412283"
},
"status": "Pass"
},
{
"id": "HTTP-029",
"name": "Started B2 cancel preserves same minutes Task",
"precondition": "B2 opposite order",
"action": "DELETE registration",
"expected": "cancelled,same minutes id/task",
"at": "2026-10-06T16:29:37.390Z",
"actual": {
"registration": {
"id": "3",
"meetingId": "3",
"status": "cancelled"
},
"reservation": {
"id": "3",
"meetingId": "3",
"clientContactId": "2",
"taskId": "3",
"status": "reserved",
"createdAt": "2026-10-07T00:29:31.297439"
}
},
"status": "Pass"
},
{
"id": "HTTP-030",
"name": "All free Tasks remain zero and unique",
"precondition": "A/B1/B2 complete",
"action": "Read isolated snapshot",
"expected": "3 registrations,3 unique Tasks,0 contracts,all prices0",
"at": "2026-10-06T16:29:37.464Z",
"actual": {
"registrations": [
{
"id": 1,
"meeting_id": 1,
"client_contact_id": 2,
"task_id": 1,
"attend_requested": false,
"minutes_requested": true,
"status": "confirmed"
},
{
"id": 2,
"meeting_id": 2,
"client_contact_id": 2,
"task_id": 2,
"attend_requested": true,
"minutes_requested": true,
"status": "cancelled"
},
{
"id": 3,
"meeting_id": 3,
"client_contact_id": 2,
"task_id": 3,
"attend_requested": true,
"minutes_requested": true,
"status": "cancelled"
}
],
"tasks": [
{
"id": 1,
"status": "ongoing",
"contract_id": null,
"charge_total_hours": "0.0000",
"actual_price": "0.00",
"total_price": "0.00"
},
{
"id": 2,
"status": "ongoing",
"contract_id": null,
"charge_total_hours": "0.0000",
"actual_price": "0.00",
"total_price": "0.00"
},
{
"id": 3,
"status": "ongoing",
"contract_id": null,
"charge_total_hours": "0.0000",
"actual_price": "0.00",
"total_price": "0.00"
}
],
"contract_count": 0
},
"status": "Pass"
},
{
"id": "HTTP-031",
"name": "Synthetic paid no contract rejects without mutation",
"precondition": "D synthetic1h,no contracts",
"action": "PUT D minutes",
"expected": "NO_ACTIVE_CONTRACT,still3 Tasks",
"at": "2026-10-06T16:29:38.248Z",
"actual": {
"status": 409,
"data": {
"error": {
"code": "NO_ACTIVE_CONTRACT",
"message": "当前客户暂无有效合同,请联系客户经理开通后再报名"
},
"requestId": "e5e32c49-8b3e-48f0-ad21-a43a50da7cde"
}
},
"status": "Pass"
},
{
"id": "HTTP-032",
"name": "Real PG refusal outbox durable and deduplicated",
"precondition": "D denied",
"action": "Repeat rejection+PG SELECT",
"expected": "one pending row,not reservation",
"at": "2026-10-06T16:29:39.079Z",
"actual": [
{
"meeting_id": "4",
"state": "pending",
"attempts": 0,
"accepted_at": null
}
],
"status": "Pass"
},
{
"id": "HTTP-033",
"name": "Refusal persists across real BFF restart and mock acceptance",
"precondition": "Outbox stored in real PG",
"action": "Stop/restart BFF; bootstrap dispatch to mock13001",
"expected": "same row accepted once;no real email",
"at": "2026-10-06T16:29:39.132Z",
"actual": {
"outbox": {
"state": "accepted",
"attempts": 1,
"accepted_at": "2026-10-06T16:29:39.777Z"
},
"mockRequests": 1,
"realEmails": 0
},
"status": "Pass"
},
{
"id": "HTTP-034",
"name": "Accepted refusal never becomes successful reservation",
"precondition": "D notice accepted by mock",
"action": "GET D minutes;GET myMinutes;snapshot",
"expected": "null,D absent,3 Tasks",
"at": "2026-10-06T16:29:39.930Z",
"actual": {
"reservation": null,
"myMinutes": 3,
"taskCount": 3
},
"status": "Pass"
}
],
"summary": {
"executed": 34,
"pass": 34,
"fail": 0
},
"cleanup": {
"services": [
{
"role": "Backend",
"pid": 53599,
"exitCode": null,
"signal": "SIGTERM"
},
{
"role": "BFF",
"pid": 53601,
"exitCode": null,
"signal": "SIGTERM"
},
{
"role": "BFF",
"pid": 53625,
"exitCode": null,
"signal": "SIGTERM"
}
],
"container": "ndb-qa-minutes-http-20261007-56488",
"containerStopped": true
},
"boundaries": {
"realWechatLogin": false,
"miniUI": false,
"realEmails": 0,
"sharedBusinessDbWrites": 0
},
"mockNotifications": [
{
"path": "/v1/notifications",
"key": "minutes-no-contract-e0cdd570c83130ab69bfbdce67ee9d375a6b100c05156ebbc74abe0630fb5929",
"body": {
"metadata": {
"source": "mp-conference-minutes-no-contract"
},
"deliveries": [
{
"channel": "email",
"to": {
"emails": [
"qa-staff@example.invalid"
]
},
"subject": "纪要预约因无有效合同被拒绝",
"content": {
"kind": "normal",
"contentType": "text",
"body": "会议ID:4\n客户联系人ID:2\n渠道用户ID:qa-http-bound-user\n拒绝原因:NO_ACTIVE_CONTRACT\n预约未成功,请工作人员核实合同。"
}
}
]
}
}
]
}
(49-49/78)