From b69abec08d2038e614871f692a377d17626da82c Mon Sep 17 00:00:00 2001 From: Vladimir Saraikin Date: Mon, 13 Jul 2026 18:50:23 +0200 Subject: [PATCH] Validate each token of multi-value enum fields MULTIPLEVALUESTRING/MULTIPLESTRINGVALUE/MULTIPLECHARVALUE fields carry a space-delimited set of enum tokens, but validateField looked up the whole value in the enum map, so any legal combination of two or more values (e.g. ExecInst "1 5") was rejected as out of range. Split on space and validate each component. Fixes #754. --- validation.go | 19 ++++++++++++++++--- validation_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/validation.go b/validation.go index 82b535aca..29cd9534e 100644 --- a/validation.go +++ b/validation.go @@ -16,6 +16,8 @@ package quickfix import ( + "bytes" + "github.com/quickfixgo/quickfix/datadictionary" ) @@ -385,10 +387,21 @@ func validateField(d *datadictionary.DataDictionary, return nil } - allowedValues := d.FieldTypeByTag[int(field.tag)].Enums + allowedValues := fieldType.Enums if len(allowedValues) != 0 { - if _, validValue := allowedValues[string(field.value)]; !validValue { - return ValueIsIncorrect(field.tag) + switch fieldType.Type { + case "MULTIPLESTRINGVALUE", "MULTIPLEVALUESTRING", "MULTIPLECHARVALUE": + // These fields carry a space-delimited set of enum tokens, each of + // which must be individually valid (e.g. ExecInst "1 5" = two enums). + for _, component := range bytes.Split(field.value, []byte{' '}) { + if _, validValue := allowedValues[string(component)]; !validValue { + return ValueIsIncorrect(field.tag) + } + } + default: + if _, validValue := allowedValues[string(field.value)]; !validValue { + return ValueIsIncorrect(field.tag) + } } } diff --git a/validation_test.go b/validation_test.go index 7973464d1..7c1360ab2 100644 --- a/validation_test.go +++ b/validation_test.go @@ -86,6 +86,8 @@ func TestValidate(t *testing.T) { tcCheckUserDefinedFieldsDisabled(), tcCheckUserDefinedFieldsDisabledFixT(), tcMultipleRepeatingGroupFields(), + tcMultiValueEnumValid(), + tcMultiValueEnumInvalidComponent(), } msg := NewMessage() @@ -662,6 +664,42 @@ func tcValueIsIncorrectFixT() validateTest { } } +func tcMultiValueEnumValid() validateTest { + dict, _ := datadictionary.Parse("spec/FIX43.xml") + validator := NewValidator(defaultValidatorSettings, dict, nil) + + // ExecInst (tag 18) is a MULTIPLEVALUESTRING: a space-delimited set of + // individually-valid enum tokens (e.g. "Y M") must be accepted. + builder := createFIX43NewOrderSingle() + builder.Body.SetField(Tag(18), FIXString("Y M")) + msgBytes := builder.build() + + return validateTest{ + TestName: "Multi-value enum valid", + Validator: validator, + MessageBytes: msgBytes, + DoNotExpectReject: true, + } +} + +func tcMultiValueEnumInvalidComponent() validateTest { + dict, _ := datadictionary.Parse("spec/FIX43.xml") + validator := NewValidator(defaultValidatorSettings, dict, nil) + + tag := Tag(18) + builder := createFIX43NewOrderSingle() + builder.Body.SetField(tag, FIXString("Y ZZ")) // ZZ is not a valid ExecInst enum + msgBytes := builder.build() + + return validateTest{ + TestName: "Multi-value enum invalid component", + Validator: validator, + MessageBytes: msgBytes, + ExpectedRejectReason: rejectReasonValueIsIncorrect, + ExpectedRefTagID: &tag, + } +} + func tcIncorrectDataFormatForValue() validateTest { dict, _ := datadictionary.Parse("spec/FIX40.xml") validator := NewValidator(defaultValidatorSettings, dict, nil)