From 4dbc3d605cd5ed275a48aea1a394125cf67f6801 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Mon, 27 Apr 2026 17:03:21 +0200 Subject: [PATCH 1/4] fix: preserve mapping result range cardinality Symptom: REST and import-mapping result handling can change cardinality metadata during roundtrip when ForceSingleOccurrence and Range.SingleObject differ. Root cause: ResultHandlingMapping had only one SingleObject flag, so parsing collapsed ForceSingleOccurrence into result cardinality and writing forced both BSON fields to the same value. Fix: store ForceSingleOccurrence separately, parse Range.SingleObject and ObjectType as result-cardinality signals, and write ForceSingleOccurrence independently from Range.SingleObject for REST and XML import mappings. Tests: add parser/writer coverage for object-valued mapping results with ForceSingleOccurrence=false. make build, make lint-go, and make test pass. --- sdk/microflows/microflows_actions.go | 9 ++- sdk/mpr/parser_microflow_actions.go | 17 ++++- sdk/mpr/parser_microflow_test.go | 109 +++++++++++++++++++++++++++ sdk/mpr/writer_microflow_actions.go | 20 +++-- 4 files changed, 144 insertions(+), 11 deletions(-) diff --git a/sdk/microflows/microflows_actions.go b/sdk/microflows/microflows_actions.go index ccc6f10e..c7e3a7de 100644 --- a/sdk/microflows/microflows_actions.go +++ b/sdk/microflows/microflows_actions.go @@ -822,10 +822,11 @@ func (ResultHandlingHttpResponse) isResultHandling() {} // ResultHandlingMapping uses an import mapping. type ResultHandlingMapping struct { model.BaseElement - MappingID model.ID `json:"mappingId"` - ResultEntityID model.ID `json:"resultEntityId,omitempty"` - ResultVariable string `json:"resultVariable,omitempty"` - SingleObject bool `json:"singleObject,omitempty"` // true when mapping returns a single object (not a list) + MappingID model.ID `json:"mappingId"` + ResultEntityID model.ID `json:"resultEntityId,omitempty"` + ResultVariable string `json:"resultVariable,omitempty"` + SingleObject bool `json:"singleObject,omitempty"` // true when mapping returns a single object (not a list) + ForceSingleOccurrence *bool `json:"forceSingleOccurrence,omitempty"` } func (ResultHandlingMapping) isResultHandling() {} diff --git a/sdk/mpr/parser_microflow_actions.go b/sdk/mpr/parser_microflow_actions.go index d7095bd9..991e94a8 100644 --- a/sdk/mpr/parser_microflow_actions.go +++ b/sdk/mpr/parser_microflow_actions.go @@ -562,9 +562,17 @@ func parseResultHandling(raw map[string]any, handlingType string) microflows.Res mappingRef = extractString(call["ReturnValueMapping"]) } result.MappingID = model.ID(mappingRef) + forceSingleOccurrence := extractBool(call["ForceSingleOccurrence"], false) + result.ForceSingleOccurrence = &forceSingleOccurrence + if rangeMap := toMap(call["Range"]); rangeMap != nil { + result.SingleObject = extractBool(rangeMap["SingleObject"], false) + } } if varType := toMap(raw["VariableType"]); varType != nil { result.ResultEntityID = model.ID(extractString(varType["Entity"])) + if extractString(varType["$Type"]) == "DataTypes$ObjectType" { + result.SingleObject = true + } } return result case "None": @@ -666,7 +674,14 @@ func parseImportXmlAction(raw map[string]any) *microflows.ImportXmlAction { if varType := toMap(call["VariableType"]); varType != nil { handling.ResultEntityID = model.ID(extractString(varType["Entity"])) } - handling.SingleObject = extractBool(call["ForceSingleOccurrence"], false) + forceSingleOccurrence := extractBool(call["ForceSingleOccurrence"], false) + handling.ForceSingleOccurrence = &forceSingleOccurrence + if rangeMap := toMap(call["Range"]); rangeMap != nil { + handling.SingleObject = extractBool(rangeMap["SingleObject"], false) + } + if !handling.SingleObject { + handling.SingleObject = forceSingleOccurrence + } } action.ResultHandling = handling } diff --git a/sdk/mpr/parser_microflow_test.go b/sdk/mpr/parser_microflow_test.go index 41307a84..0ebd8d91 100644 --- a/sdk/mpr/parser_microflow_test.go +++ b/sdk/mpr/parser_microflow_test.go @@ -5,6 +5,7 @@ package mpr import ( "testing" + "github.com/mendixlabs/mxcli/model" "github.com/mendixlabs/mxcli/sdk/microflows" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -81,3 +82,111 @@ func TestParseCommitAction_ErrorHandlingTypeDefaultsToRollback(t *testing.T) { t.Errorf("expected default Rollback, got %q", action.ErrorHandlingType) } } + +func TestParseResultHandlingMappingUsesRangeForSingleObject(t *testing.T) { + got := parseResultHandling(map[string]any{ + "$ID": "result-handling-1", + "ResultVariableName": "RemoteApp", + "ImportMappingCall": map[string]any{ + "ReturnValueMapping": "SampleRuntimeApi.IMM_RemoteApp", + "ForceSingleOccurrence": false, + "Range": map[string]any{ + "SingleObject": true, + }, + }, + "VariableType": map[string]any{ + "$Type": "DataTypes$ObjectType", + "Entity": "SampleRuntimeApi.RemoteApp", + }, + }, "Mapping") + + rh, ok := got.(*microflows.ResultHandlingMapping) + if !ok { + t.Fatalf("got %T, want *microflows.ResultHandlingMapping", got) + } + if !rh.SingleObject { + t.Fatal("Range.SingleObject=true must make the result object-valued") + } + if rh.ForceSingleOccurrence == nil || *rh.ForceSingleOccurrence { + t.Fatalf("ForceSingleOccurrence = %v, want explicit false", rh.ForceSingleOccurrence) + } +} + +func TestSerializeRestResultHandlingPreservesForceSingleOccurrenceSeparately(t *testing.T) { + forceSingleOccurrence := false + doc := serializeRestResultHandling(µflows.ResultHandlingMapping{ + BaseElement: model.BaseElement{ID: model.ID("result-handling-1")}, + MappingID: model.ID("SampleRuntimeApi.IMM_RemoteApp"), + ResultEntityID: model.ID("SampleRuntimeApi.RemoteApp"), + ResultVariable: "RemoteApp", + SingleObject: true, + ForceSingleOccurrence: &forceSingleOccurrence, + }, "RemoteApp") + + importCall, ok := bsonDMap(doc)["ImportMappingCall"].(primitive.D) + if !ok { + t.Fatalf("ImportMappingCall missing or wrong type: %T", bsonDMap(doc)["ImportMappingCall"]) + } + callFields := bsonDMap(importCall) + if got := callFields["ForceSingleOccurrence"]; got != false { + t.Fatalf("ForceSingleOccurrence = %v, want false", got) + } + rangeDoc, ok := callFields["Range"].(primitive.D) + if !ok { + t.Fatalf("Range missing or wrong type: %T", callFields["Range"]) + } + if got := bsonDMap(rangeDoc)["SingleObject"]; got != true { + t.Fatalf("Range.SingleObject = %v, want true", got) + } + varType, ok := bsonDMap(doc)["VariableType"].(primitive.D) + if !ok { + t.Fatalf("VariableType missing or wrong type: %T", bsonDMap(doc)["VariableType"]) + } + if got := bsonDMap(varType)["$Type"]; got != "DataTypes$ObjectType" { + t.Fatalf("VariableType.$Type = %v, want DataTypes$ObjectType", got) + } +} + +func TestSerializeImportXmlActionPreservesSingleObjectRange(t *testing.T) { + forceSingleOccurrence := false + doc := serializeImportXmlAction(µflows.ImportXmlAction{ + BaseElement: model.BaseElement{ID: model.ID("import-action-1")}, + ResultHandling: µflows.ResultHandlingMapping{ + BaseElement: model.BaseElement{ID: model.ID("result-handling-1")}, + MappingID: model.ID("SampleRest.IMM_ErrorResponse"), + ResultEntityID: model.ID("SampleRest.Error"), + ResultVariable: "ErrorResponse", + SingleObject: true, + ForceSingleOccurrence: &forceSingleOccurrence, + }, + XmlDocumentVariable: "LatestHttpResponse", + }) + + resultHandling, ok := bsonDMap(doc)["ResultHandling"].(primitive.D) + if !ok { + t.Fatalf("ResultHandling missing or wrong type: %T", bsonDMap(doc)["ResultHandling"]) + } + importCall, ok := bsonDMap(resultHandling)["ImportMappingCall"].(primitive.D) + if !ok { + t.Fatalf("ImportMappingCall missing or wrong type: %T", bsonDMap(resultHandling)["ImportMappingCall"]) + } + callFields := bsonDMap(importCall) + if got := callFields["ForceSingleOccurrence"]; got != false { + t.Fatalf("ForceSingleOccurrence = %v, want false", got) + } + rangeDoc, ok := callFields["Range"].(primitive.D) + if !ok { + t.Fatalf("Range missing or wrong type: %T", callFields["Range"]) + } + if got := bsonDMap(rangeDoc)["SingleObject"]; got != true { + t.Fatalf("Range.SingleObject = %v, want true", got) + } +} + +func bsonDMap(doc primitive.D) map[string]any { + out := make(map[string]any, len(doc)) + for _, elem := range doc { + out[elem.Key] = elem.Value + } + return out +} diff --git a/sdk/mpr/writer_microflow_actions.go b/sdk/mpr/writer_microflow_actions.go index 5fdf6cda..a6c93e84 100644 --- a/sdk/mpr/writer_microflow_actions.go +++ b/sdk/mpr/writer_microflow_actions.go @@ -838,15 +838,18 @@ func serializeRestResultHandling(rh microflows.ResultHandling, outputVar string) {Key: "$Type", Value: "Microflows$ResultHandling"}, {Key: "Bind", Value: true}, } - // ImportMappingCall - uses ReturnValueMapping (Studio Pro field name) - // with all required fields to make the mapping link visible in Studio Pro. - // SingleObject drives ForceSingleOccurrence and Range.SingleObject. + // ImportMappingCall uses ReturnValueMapping (Studio Pro field name) with + // all required fields to make the mapping link visible in Studio Pro. + forceSingleOccurrence := h.SingleObject + if h.ForceSingleOccurrence != nil { + forceSingleOccurrence = *h.ForceSingleOccurrence + } importCall := bson.D{ {Key: "$ID", Value: idToBsonBinary(GenerateID())}, {Key: "$Type", Value: "Microflows$ImportMappingCall"}, {Key: "Commit", Value: "YesWithoutEvents"}, {Key: "ContentType", Value: "Json"}, - {Key: "ForceSingleOccurrence", Value: h.SingleObject}, + {Key: "ForceSingleOccurrence", Value: forceSingleOccurrence}, {Key: "ObjectHandlingBackup", Value: "Create"}, {Key: "ParameterVariableName", Value: ""}, {Key: "Range", Value: bson.D{ @@ -1240,19 +1243,24 @@ func serializeExecuteDatabaseQueryAction(a *microflows.ExecuteDatabaseQueryActio } func serializeImportXmlAction(a *microflows.ImportXmlAction) bson.D { + forceSingleOccurrence := false + if a.ResultHandling.ForceSingleOccurrence != nil { + forceSingleOccurrence = *a.ResultHandling.ForceSingleOccurrence + } + // Build ImportMappingCall importCall := bson.D{ {Key: "$ID", Value: idToBsonBinary(GenerateID())}, {Key: "$Type", Value: "Microflows$ImportMappingCall"}, {Key: "Commit", Value: "YesWithoutEvents"}, {Key: "ContentType", Value: "Json"}, - {Key: "ForceSingleOccurrence", Value: false}, + {Key: "ForceSingleOccurrence", Value: forceSingleOccurrence}, {Key: "ObjectHandlingBackup", Value: "Create"}, {Key: "ParameterVariableName", Value: ""}, {Key: "Range", Value: bson.D{ {Key: "$ID", Value: idToBsonBinary(GenerateID())}, {Key: "$Type", Value: "Microflows$ConstantRange"}, - {Key: "SingleObject", Value: false}, + {Key: "SingleObject", Value: a.ResultHandling.SingleObject}, }}, {Key: "ReturnValueMapping", Value: string(a.ResultHandling.MappingID)}, } From c799107b7f262fcf091ca1df069a611ab965cb52 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Mon, 27 Apr 2026 18:40:23 +0200 Subject: [PATCH 2/4] test: add bug-test reproducer for REST/import mapping cardinality Adds an MDL script under mdl-examples/bug-tests/ exercising an `import from mapping` action that uses the same ResultHandlingMapping BSON shape that #372 splits apart. Pure-MDL authoring cannot reproduce the divergent Force/Range flag split (no Studio Pro REST client setup), so the script is a positive control: `mx check` must continue to report 0 errors. The negative case (Force vs Range divergence) is covered by the parser/writer Go tests in sdk/mpr/parser_microflow_test.go. Co-Authored-By: Claude Opus 4.7 --- .../369-rest-mapping-result-cardinality.mdl | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 mdl-examples/bug-tests/369-rest-mapping-result-cardinality.mdl diff --git a/mdl-examples/bug-tests/369-rest-mapping-result-cardinality.mdl b/mdl-examples/bug-tests/369-rest-mapping-result-cardinality.mdl new file mode 100644 index 00000000..8657f06a --- /dev/null +++ b/mdl-examples/bug-tests/369-rest-mapping-result-cardinality.mdl @@ -0,0 +1,72 @@ +-- ============================================================================ +-- Bug #369: REST/import mapping result range cardinality conflated +-- ============================================================================ +-- +-- Symptom (before fix): +-- Studio Pro stores two related but distinct bits of import-mapping +-- metadata in `Forms$ResultHandlingMapping`: +-- - `ForceSingleOccurrence` controls force-single-occurrence behavior. +-- - `Range.SingleObject` (with object/list variable type) describes the +-- result cardinality. +-- The mxcli SDK shape collapsed these to a single `SingleObject` field. +-- Roundtripping a REST client call action whose Studio Pro source had +-- `ForceSingleOccurrence = false` and `Range.SingleObject = true` +-- forced both fields to the same value. The resulting MPR had subtly +-- different runtime behaviour than the original. +-- +-- After fix: +-- - `microflows.ResultHandlingMapping` stores `ForceSingleOccurrence` +-- separately from `SingleObject`. +-- - Parser reads `Range.SingleObject` and the object/list ObjectType as +-- result-cardinality signals; ForceSingleOccurrence stays its own bit. +-- - Writer emits ForceSingleOccurrence independently of Range for both +-- REST and XML import mappings. +-- +-- Reproducibility note: +-- This PR is a parser/writer roundtrip preservation fix at the BSON +-- level. There is no new MDL syntax. The negative case (a real Studio +-- Pro REST client call with the divergent flags) is covered by the +-- SDK-level Go tests in `sdk/mpr/parser_microflow_test.go`. +-- +-- This script provides a positive control: an `import from mapping` +-- call whose result handling exercises the related codepath and must +-- keep producing a valid MPR. +-- +-- Usage: +-- mxcli exec mdl-examples/bug-tests/369-rest-mapping-result-cardinality.mdl -p app.mpr +-- mxcli -p app.mpr -c "describe microflow BugTest369.MF_ImportSinglePet" +-- `mx check` against the resulting MPR must report 0 errors. +-- ============================================================================ + +create module BugTest369; + +create json structure BugTest369.JSON_Pet +snippet '{"id": 1, "name": "Fido", "status": "available"}'; + +create non-persistent entity BugTest369.PetResponse ( + PetId : integer, + Name : string, + Status : string +); +/ + +create import mapping BugTest369.IMM_Pet + with json structure BugTest369.JSON_Pet +{ + create BugTest369.PetResponse { + PetId = id, + Name = name, + Status = status + } +}; + +-- import from mapping result handling — exercises the same +-- ResultHandlingMapping BSON shape that #372 splits apart. +create microflow BugTest369.MF_ImportSinglePet ( + $Json: string +) +returns BugTest369.PetResponse as $Pet +begin + $Pet = import from mapping BugTest369.IMM_Pet ($Json); +end; +/ From d11ff708fc4665ac932837872c7d39fc6f93ca37 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Thu, 30 Apr 2026 18:22:34 +0200 Subject: [PATCH 3/4] fix: preserve explicit REST mapping output variables Symptom: REST calls that returned import mappings could rename an explicit assignment such as $Items to the mapped entity name during exec, leaving later statements that still referenced $Items invalid. Root cause: the REST mapping builder always overwrote OutputVariable with the result entity short name, even when the MDL assignment already supplied a variable. Fix: only derive the entity-name fallback when the statement has no explicit output variable. Tests: add a synthetic builder regression that asserts both the action output and ResultHandlingMapping.ResultVariable preserve the authored name. --- mdl/executor/cmd_microflows_builder_calls.go | 8 ++- ...d_microflows_builder_rest_response_test.go | 62 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/mdl/executor/cmd_microflows_builder_calls.go b/mdl/executor/cmd_microflows_builder_calls.go index eb9cb6a9..87458867 100644 --- a/mdl/executor/cmd_microflows_builder_calls.go +++ b/mdl/executor/cmd_microflows_builder_calls.go @@ -909,9 +909,11 @@ func (fb *flowBuilder) addRestCallAction(s *ast.RestCallStmt) model.ID { case ast.RestResultMapping: mappingQN := s.Result.MappingName.Module + "." + s.Result.MappingName.Name entityQN := s.Result.ResultEntity.Module + "." + s.Result.ResultEntity.Name - // Derive the output variable name from the root entity's short name so - // callers don't need to hard-code it in the MDL assignment. - s.OutputVariable = s.Result.ResultEntity.Name + if s.OutputVariable == "" { + // Derive a fallback output variable from the root entity only when the + // MDL did not explicitly assign one. + s.OutputVariable = s.Result.ResultEntity.Name + } // Determine whether the import mapping returns a single object or a list by // looking at the JSON structure it references. If the root JSON element is // an Object, the mapping produces one object; if it is an Array, a list. diff --git a/mdl/executor/cmd_microflows_builder_rest_response_test.go b/mdl/executor/cmd_microflows_builder_rest_response_test.go index d0f1fb04..e1d111ff 100644 --- a/mdl/executor/cmd_microflows_builder_rest_response_test.go +++ b/mdl/executor/cmd_microflows_builder_rest_response_test.go @@ -3,9 +3,13 @@ package executor import ( + "fmt" "testing" "github.com/mendixlabs/mxcli/mdl/ast" + "github.com/mendixlabs/mxcli/mdl/backend/mock" + mdltypes "github.com/mendixlabs/mxcli/mdl/types" + "github.com/mendixlabs/mxcli/model" "github.com/mendixlabs/mxcli/sdk/microflows" ) @@ -53,3 +57,61 @@ func TestAddRestCallAction_ReturnsResponseUsesHttpResponseHandling(t *testing.T) t.Errorf("VariableName = %q, want %q", httpResponse.VariableName, "Response") } } + +func TestAddRestCallAction_MappingResultPreservesExplicitOutputVariable(t *testing.T) { + fb := &flowBuilder{ + posX: 100, + posY: 100, + spacing: HorizontalSpacing, + varTypes: map[string]string{}, + declaredVars: map[string]string{}, + measurer: &layoutMeasurer{}, + backend: &mock.MockBackend{ + GetImportMappingByQualifiedNameFunc: func(moduleName, name string) (*model.ImportMapping, error) { + if moduleName != "Synthetic" || name != "ImportItems" { + return nil, fmt.Errorf("unexpected import mapping %s.%s", moduleName, name) + } + return &model.ImportMapping{JsonStructure: "Synthetic.ItemsPayload"}, nil + }, + GetJsonStructureByQualifiedNameFunc: func(moduleName, name string) (*mdltypes.JsonStructure, error) { + if moduleName != "Synthetic" || name != "ItemsPayload" { + return nil, fmt.Errorf("unexpected json structure %s.%s", moduleName, name) + } + return &mdltypes.JsonStructure{ + Elements: []*mdltypes.JsonElement{{ElementType: "Array"}}, + }, nil + }, + }, + } + + stmt := &ast.RestCallStmt{ + OutputVariable: "Items", + Method: ast.HttpMethodGet, + URL: &ast.LiteralExpr{Kind: ast.LiteralString, Value: "https://example.com"}, + Result: ast.RestResult{ + Type: ast.RestResultMapping, + MappingName: ast.QualifiedName{Module: "Synthetic", Name: "ImportItems"}, + ResultEntity: ast.QualifiedName{Module: "Synthetic", Name: "Item"}, + }, + } + fb.addRestCallAction(stmt) + + activity, ok := fb.objects[0].(*microflows.ActionActivity) + if !ok { + t.Fatalf("first object is %T, want *microflows.ActionActivity", fb.objects[0]) + } + action, ok := activity.Action.(*microflows.RestCallAction) + if !ok { + t.Fatalf("activity.Action is %T, want *microflows.RestCallAction", activity.Action) + } + mapping, ok := action.ResultHandling.(*microflows.ResultHandlingMapping) + if !ok { + t.Fatalf("ResultHandling is %T, want *microflows.ResultHandlingMapping", action.ResultHandling) + } + if action.OutputVariable != "Items" { + t.Fatalf("OutputVariable = %q, want Items", action.OutputVariable) + } + if mapping.ResultVariable != "Items" { + t.Fatalf("ResultVariable = %q, want Items", mapping.ResultVariable) + } +} From 7ddf08b3840be571c0bc92bf72c6a6465ca9cd44 Mon Sep 17 00:00:00 2001 From: Henrique Costa Date: Fri, 1 May 2026 09:22:49 +0200 Subject: [PATCH 4/4] test: cover XML import mapping result cardinality parse Symptom: review noted that the REST parser had coverage for Range.SingleObject with ForceSingleOccurrence=false, but the XML import parser path only had writer coverage. Root cause: parseImportXmlAction has an XML-specific legacy fallback from ForceSingleOccurrence to SingleObject, but no test pinned the newer Range.SingleObject source of truth. Fix: add a parser regression test for XML import result handling where Range.SingleObject=true and ForceSingleOccurrence=false, and document why the fallback remains XML-specific. Tests: ran go test ./sdk/mpr -run 'ResultHandling|ImportXmlAction' -count=1 and make build && make test. --- sdk/mpr/parser_microflow_actions.go | 4 ++++ sdk/mpr/parser_microflow_test.go | 33 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/sdk/mpr/parser_microflow_actions.go b/sdk/mpr/parser_microflow_actions.go index c487e97c..eafb63e6 100644 --- a/sdk/mpr/parser_microflow_actions.go +++ b/sdk/mpr/parser_microflow_actions.go @@ -737,6 +737,10 @@ func parseImportXmlAction(raw map[string]any) *microflows.ImportXmlAction { if rangeMap := toMap(call["Range"]); rangeMap != nil { handling.SingleObject = extractBool(rangeMap["SingleObject"], false) } + // Older XML import mappings may omit Range and encode single-object + // handling only through ForceSingleOccurrence. REST result handling + // stores Range consistently, so this compatibility fallback stays + // XML-specific. if !handling.SingleObject { handling.SingleObject = forceSingleOccurrence } diff --git a/sdk/mpr/parser_microflow_test.go b/sdk/mpr/parser_microflow_test.go index b94eda31..df39d7d1 100644 --- a/sdk/mpr/parser_microflow_test.go +++ b/sdk/mpr/parser_microflow_test.go @@ -185,6 +185,39 @@ func TestSerializeImportXmlActionPreservesSingleObjectRange(t *testing.T) { } } +func TestParseImportXmlActionUsesRangeForSingleObject(t *testing.T) { + got := parseImportXmlAction(map[string]any{ + "$ID": "import-action-1", + "XmlDocumentVariable": "LatestHttpResponse", + "XmlDocumentVariableName": "LatestHttpResponse", + "ResultHandling": map[string]any{ + "$ID": "result-handling-1", + "ResultVariableName": "ErrorResponse", + "ImportMappingCall": map[string]any{ + "ReturnValueMapping": "SampleRest.IMM_ErrorResponse", + "ForceSingleOccurrence": false, + "Range": map[string]any{ + "SingleObject": true, + }, + "VariableType": map[string]any{ + "$Type": "DataTypes$ObjectType", + "Entity": "SampleRest.Error", + }, + }, + }, + }) + + if got.ResultHandling == nil { + t.Fatal("ResultHandling missing") + } + if !got.ResultHandling.SingleObject { + t.Fatal("Range.SingleObject=true must make XML import result object-valued") + } + if got.ResultHandling.ForceSingleOccurrence == nil || *got.ResultHandling.ForceSingleOccurrence { + t.Fatalf("ForceSingleOccurrence = %v, want explicit false", got.ResultHandling.ForceSingleOccurrence) + } +} + func bsonDMap(doc primitive.D) map[string]any { out := make(map[string]any, len(doc)) for _, elem := range doc {