实现M002 V3字段控件契约后端第一版
This commit is contained in:
@@ -96,6 +96,13 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
|
||||
.andExpect(jsonPath("$.items[0].system_task_type").value("SOURCE_MESSAGE_ONLY"))
|
||||
.andReturn();
|
||||
|
||||
String s10TaskId = com.jayway.jsonpath.JsonPath.read(
|
||||
s10Result.getResponse().getContentAsString(),
|
||||
"$.items[0].task_id");
|
||||
mockMvc.perform(get("/api/reservation/tasks/{taskId}", s10TaskId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.fields.length()").value(0));
|
||||
|
||||
assertSourceMessageOnlyTaskCount(s10Result, "S10");
|
||||
assertSourceMessageOnlyTaskCount(s99Result, "S99");
|
||||
}
|
||||
@@ -407,6 +414,92 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
|
||||
assertThat(parentReviewTaskCount).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveParentKeyConflictManualReviewWithGroupCodeAlias() throws Exception {
|
||||
ObjectNode root = fixture("parent_split_two_children.json").deepCopy();
|
||||
useParentSplitBusinessKeys(root, "PARENT-2608-RESOLVE", "CHILD-2608-RESOLVE-A", "CHILD-2608-RESOLVE-B");
|
||||
convertParentSplitRootToP01(root);
|
||||
ObjectNode parentEvent = (ObjectNode) root.path("message_events").get(2);
|
||||
ObjectNode parentCaseKeys = (ObjectNode) parentEvent.path("case_keys");
|
||||
parentCaseKeys.putNull("group_code");
|
||||
parentCaseKeys.putNull("block_code");
|
||||
ObjectNode contextUsed = (ObjectNode) parentEvent.path("context_used");
|
||||
ArrayNode candidates = contextUsed.putArray("parent_identity_candidates");
|
||||
candidates.addObject()
|
||||
.put("field", "group_code")
|
||||
.put("value", "PARENT-2608-RESOLVE-A")
|
||||
.put("evidence_source", "subject");
|
||||
candidates.addObject()
|
||||
.put("field", "block_code")
|
||||
.put("value", "PARENT-2608-RESOLVE-B")
|
||||
.put("evidence_source", "attachment");
|
||||
ObjectNode manualReview = parentEvent.putObject("manual_review");
|
||||
manualReview.put("reason_code", "target_object_unclear");
|
||||
manualReview.put("visible_reason", "Parent Group 的 group_code 与 block_code 原始候选冲突,请人工确认目标。");
|
||||
manualReview.put("review_record_type", "business_event_review");
|
||||
manualReview.putArray("missing_fields")
|
||||
.add("/case_keys/group_code")
|
||||
.add("/case_keys/block_code");
|
||||
manualReview.putArray("blocking_points")
|
||||
.add("Parent Group identity cannot be safely normalized.");
|
||||
manualReview.putArray("conflicting_points")
|
||||
.add("PARENT-2608-RESOLVE-A")
|
||||
.add("PARENT-2608-RESOLVE-B");
|
||||
manualReview.putArray("suggested_human_actions")
|
||||
.add("confirm_parent_group_identity");
|
||||
manualReview.putArray("evidence_to_check")
|
||||
.add("parent_identity_candidates");
|
||||
manualReview.putObject("known_fields");
|
||||
String externalId = "p0-parent-split-key-conflict-resolve-001";
|
||||
captureSourceMessage(externalId);
|
||||
|
||||
MvcResult createResult = mockMvc.perform(signedPost(
|
||||
withSourceMessageId(root, externalId),
|
||||
"nonce-p0-parent-key-conflict-resolve-001"))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.items[2].route_code").value("R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_REVIEW"))
|
||||
.andReturn();
|
||||
String taskId = com.jayway.jsonpath.JsonPath.read(
|
||||
createResult.getResponse().getContentAsString(),
|
||||
"$.items[2].task_id");
|
||||
String orderId = com.jayway.jsonpath.JsonPath.read(
|
||||
createResult.getResponse().getContentAsString(),
|
||||
"$.items[2].order_id");
|
||||
|
||||
mockMvc.perform(get("/api/reservation/tasks/{taskId}", taskId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='case_keys.group_code')].field_pointer")
|
||||
.value(contains("/case_keys/group_code")));
|
||||
|
||||
mockMvc.perform(post("/api/reservation/tasks/{taskId}/manual-review-resolutions", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"confirmed_order_id": "%s",
|
||||
"reason": "确认 Parent Group 目标。",
|
||||
"field_overrides": [
|
||||
{
|
||||
"field_pointer": "/case_keys/group_code",
|
||||
"value": "PARENT-2608-RESOLVED"
|
||||
}
|
||||
]
|
||||
}
|
||||
""".formatted(orderId)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.task_status").value("READY"))
|
||||
.andExpect(jsonPath("$.review_status").value("RESOLVED"))
|
||||
.andExpect(jsonPath("$.review_resolution.field_overrides[0].field_pointer")
|
||||
.value("/case_keys/group_code"))
|
||||
.andExpect(jsonPath("$.review_resolution.field_overrides[0].field_path")
|
||||
.value("case_keys.group_code"))
|
||||
.andExpect(jsonPath("$.confirmed_payload.field_values['case_keys.group_code']")
|
||||
.value("PARENT-2608-RESOLVED"))
|
||||
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.cancel_object_type']")
|
||||
.value("allotment_control_block"))
|
||||
.andExpect(jsonPath("$.confirmed_payload.field_values['extracted_fields.cancel_scope']")
|
||||
.value("entire_allotment_control_block"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailClosedWhenCurrentProducerUsesLegacyParentCancelBooking() throws Exception {
|
||||
ObjectNode root = fixture("parent_split_two_children.json").deepCopy();
|
||||
@@ -590,12 +683,67 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].field_pointer")
|
||||
.value(contains("/extracted_fields/room_items/0/room_quantity")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].control_type")
|
||||
.value(contains("number")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].edit_scope")
|
||||
.value(contains("normal_task")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].write_target")
|
||||
.value(contains("draft_payload.field_values")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].options_source")
|
||||
.value(contains("none")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_quantity')].raw_readonly")
|
||||
.value(contains(false)))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].field_pointer")
|
||||
.value(contains("/extracted_fields/room_items/0/room_type_raw")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].control_type")
|
||||
.value(contains("readonly")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].edit_scope")
|
||||
.value(contains("never")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].raw_readonly")
|
||||
.value(contains(true)))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].field_pointer")
|
||||
.value(contains("/extracted_fields/room_items/0/pms_room_type_code")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].control_type")
|
||||
.value(contains("select")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].options_source")
|
||||
.value(contains("active_pms_room_type_catalog")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].value")
|
||||
.value(contains("RM2")));
|
||||
.value(contains("RM2")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='relevant_message_excerpt')].control_type")
|
||||
.value(contains("readonly")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='relevant_message_excerpt')].edit_scope")
|
||||
.value(contains("never")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='relevant_message_excerpt')].write_target")
|
||||
.value(contains("none")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='relevant_message_excerpt')].raw_readonly")
|
||||
.value(contains(true)));
|
||||
|
||||
mockMvc.perform(put("/api/reservation/tasks/{taskId}/draft", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"field_values": {
|
||||
"extracted_fields.room_items.0.room_type_raw": "SHOULD-NOT-OVERWRITE-RAW",
|
||||
"extracted_fields.room_items.0.room_quantity": 5
|
||||
}
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("TASK_FIELD_VALIDATION_FAILED"))
|
||||
.andExpect(jsonPath("$.details[0]").value(containsString("extracted_fields.room_items.0.room_type_raw")));
|
||||
|
||||
mockMvc.perform(put("/api/reservation/tasks/{taskId}/draft", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"field_values": {
|
||||
"extracted_fields.room_type": "SHOULD-NOT-OVERWRITE-LEGACY-RAW"
|
||||
}
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("TASK_FIELD_VALIDATION_FAILED"))
|
||||
.andExpect(jsonPath("$.details[0]").value(containsString("extracted_fields.room_items.0.room_type_raw")));
|
||||
|
||||
mockMvc.perform(put("/api/reservation/tasks/{taskId}/draft", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
@@ -708,9 +856,59 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].field_pointer")
|
||||
.value(contains("/extracted_fields/room_items/0/pms_room_type_code")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].control_type")
|
||||
.value(contains("select")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].edit_scope")
|
||||
.value(contains("manual_review_only")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].write_target")
|
||||
.value(contains("review_resolution.field_overrides")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].options_source")
|
||||
.value(contains("active_pms_room_type_catalog")))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.pms_room_type_code')].raw_readonly")
|
||||
.value(contains(false)))
|
||||
.andExpect(jsonPath("$.fields[?(@.field_path=='extracted_fields.room_items.0.room_type_raw')].value")
|
||||
.value(contains("SUITE")));
|
||||
|
||||
mockMvc.perform(post("/api/reservation/tasks/{taskId}/manual-review-resolutions", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"confirmed_order_id": "%s",
|
||||
"field_overrides": [
|
||||
{
|
||||
"field_pointer": "/extracted_fields/room_items/0/room_type_raw",
|
||||
"value": "SHOULD-NOT-OVERWRITE-RAW"
|
||||
}
|
||||
]
|
||||
}
|
||||
""".formatted(orderId)))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("TASK_REVIEW_POINTER_INVALID"))
|
||||
.andExpect(jsonPath("$.details[0]")
|
||||
.value(containsString("/extracted_fields/room_items/0/room_type_raw")));
|
||||
|
||||
mockMvc.perform(post("/api/reservation/tasks/{taskId}/manual-review-resolutions", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"confirmed_order_id": "%s",
|
||||
"field_overrides": [
|
||||
{
|
||||
"field_pointer": "/extracted_fields/room_items/0/pms_room_type_code",
|
||||
"value": "SU1"
|
||||
},
|
||||
{
|
||||
"field_pointer": "/extracted_fields/room_items/0/room_type_raw",
|
||||
"value": "SHOULD-NOT-OVERWRITE-RAW"
|
||||
}
|
||||
]
|
||||
}
|
||||
""".formatted(orderId)))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("TASK_REVIEW_POINTER_INVALID"))
|
||||
.andExpect(jsonPath("$.details[0]")
|
||||
.value(containsString("/extracted_fields/room_items/0/room_type_raw")));
|
||||
|
||||
mockMvc.perform(post("/api/reservation/tasks/{taskId}/manual-review-resolutions", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
@@ -758,6 +956,45 @@ class SuperAgentTaskResultP0FixtureRegressionTest {
|
||||
assertThat(aiPayloadJson).doesNotContain("\"pms_room_type_code\":\"SU1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectBlockCodeAliasOutsideCancelAllotmentManualReview() throws Exception {
|
||||
JsonNode manualReview = fixture("manual_review_resolution.json");
|
||||
ObjectNode event = manualReview.path("known_subtype_manual_review").path("event").deepCopy();
|
||||
useManualReviewGroupCode(event, "CHILD-SUITE-BLOCK-ALIAS-001");
|
||||
String externalId = "p0-manual-review-block-alias-scope-001";
|
||||
captureSourceMessage(externalId);
|
||||
|
||||
MvcResult createResult = mockMvc.perform(signedPost(
|
||||
businessRoot(externalId, event),
|
||||
"nonce-p0-manual-review-block-alias-scope-001"))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.items[0].route_code").value("R02_NEW_GROUP_BLOCK_REVIEW"))
|
||||
.andReturn();
|
||||
String taskId = com.jayway.jsonpath.JsonPath.read(
|
||||
createResult.getResponse().getContentAsString(),
|
||||
"$.items[0].task_id");
|
||||
String orderId = com.jayway.jsonpath.JsonPath.read(
|
||||
createResult.getResponse().getContentAsString(),
|
||||
"$.items[0].order_id");
|
||||
|
||||
mockMvc.perform(post("/api/reservation/tasks/{taskId}/manual-review-resolutions", taskId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"confirmed_order_id": "%s",
|
||||
"field_overrides": [
|
||||
{
|
||||
"field_pointer": "/case_keys/block_code",
|
||||
"value": "SHOULD-NOT-BECOME-GROUP-CODE"
|
||||
}
|
||||
]
|
||||
}
|
||||
""".formatted(orderId)))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.error_code").value("TASK_REVIEW_POINTER_INVALID"))
|
||||
.andExpect(jsonPath("$.details[0]").value(containsString("/case_keys/block_code")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailClosedWhenManualReviewMissingFieldPointerDoesNotResolve() throws Exception {
|
||||
ObjectNode event = fixture("manual_review_resolution.json")
|
||||
|
||||
Reference in New Issue
Block a user