From b8cbb901dce78df9ac691f6e4d9bca367f76088f Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Sat, 9 Dec 2023 08:47:00 +0100 Subject: [PATCH 1/4] PEX: Resolve values mapped by Input Descriptor constraint fields --- vcr/pe/presentation_definition.go | 61 +++++++++++---- vcr/pe/presentation_definition_test.go | 100 +++++++++++++++++++++---- 2 files changed, 131 insertions(+), 30 deletions(-) diff --git a/vcr/pe/presentation_definition.go b/vcr/pe/presentation_definition.go index 19705eadbe..e6e72bd9d9 100644 --- a/vcr/pe/presentation_definition.go +++ b/vcr/pe/presentation_definition.go @@ -73,6 +73,32 @@ func (presentationDefinition PresentationDefinition) Match(vcs []vc.VerifiableCr return selectedVCs, descriptorMaps, nil } +// ResolveConstraintsFields returns a map where each of the InputDescriptor constraints field is mapped, +// to the corresponding value from the Verifiable Credentials that map to the InputDescriptor. +// The credentialMap is a map with the InputDescriptor.Id as key and the VerifiableCredential as value. +// Constraints that contain no ID are ignored. +func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(credentialMap map[string]vc.VerifiableCredential) (map[string]interface{}, error) { + result := make(map[string]interface{}) + for inputDescriptorID, cred := range credentialMap { + // Find the input descriptor + var inputDescriptor InputDescriptor + for _, curr := range presentationDefinition.InputDescriptors { + if curr.Id == inputDescriptorID { + inputDescriptor = *curr + break + } + } + if inputDescriptor.Constraints == nil { + continue + } + _, values, _ := matchConstraint(inputDescriptor.Constraints, cred) + for key, value := range values { + result[key] = value + } + } + return result, nil +} + func (presentationDefinition PresentationDefinition) matchConstraints(vcs []vc.VerifiableCredential) ([]Candidate, error) { var candidates []Candidate @@ -275,7 +301,8 @@ func matchCredential(descriptor InputDescriptor, credential vc.VerifiableCredent // for each constraint in descriptor.constraints: // a vc must match the constraint if descriptor.Constraints != nil { - return matchConstraint(descriptor.Constraints, credential) + matches, _, err := matchConstraint(descriptor.Constraints, credential) + return matches, err } return true, nil } @@ -284,7 +311,8 @@ func matchCredential(descriptor InputDescriptor, credential vc.VerifiableCredent // All Fields need to match according to the Field rules. // IsHolder, SameSubject, SubjectIsIssuer, Statuses are not supported for now. // LimitDisclosure is not supported for now. -func matchConstraint(constraint *Constraints, credential vc.VerifiableCredential) (bool, error) { +// If the constraint matches, it returns true and a map containing constraint field IDs and matched values. +func matchConstraint(constraint *Constraints, credential vc.VerifiableCredential) (bool, map[string]interface{}, error) { // jsonpath works on interfaces, so convert the VC to an interface var credentialAsMap map[string]interface{} var err error @@ -298,26 +326,31 @@ func matchConstraint(constraint *Constraints, credential vc.VerifiableCredential credentialAsMap, err = remarshalToMap(credential) } if err != nil { - return false, err + return false, nil, err } // for each field in constraint.fields: // a vc must match the field + values := make(map[string]interface{}) for _, field := range constraint.Fields { - match, err := matchField(field, credentialAsMap) + match, value, err := matchField(field, credentialAsMap) if err != nil { - return false, err + return false, nil, err } if !match { - return false, nil + return false, nil, nil + } + if field.Id != nil { + values[*field.Id] = value } } - return true, nil + return true, values, nil } // matchField matches the field against the VC. +// If the field matches, it returns true and the matched value. The matched value can be nil if the field is optional. // All fields need to match unless optional is set to true and no values are found for all the paths. -func matchField(field Field, credential map[string]interface{}) (bool, error) { +func matchField(field Field, credential map[string]interface{}) (bool, interface{}, error) { // for each path in field.paths: // a vc must match one of the path var optionalInvalid int @@ -325,23 +358,23 @@ func matchField(field Field, credential map[string]interface{}) (bool, error) { // if path is not found continue value, err := getValueAtPath(path, credential) if err != nil { - return false, err + return false, nil, err } if value == nil { continue } if field.Filter == nil { - return true, nil + return true, value, nil } // if filter at path matches return true match, err := matchFilter(*field.Filter, value) if err != nil { - return false, err + return false, nil, err } if match { - return true, nil + return true, value, nil } // if filter at path does not match continue and set optionalInvalid optionalInvalid++ @@ -349,9 +382,9 @@ func matchField(field Field, credential map[string]interface{}) (bool, error) { // no matches, check optional. Optional is only valid if all paths returned no results // not if a filter did not match if field.Optional != nil && *field.Optional && optionalInvalid == 0 { - return true, nil + return true, nil, nil } - return false, nil + return false, nil, nil } // getValueAtPath uses the JSON path expression to get the value from the VC diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index eef3cb75db..4172c32ec6 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -24,6 +24,7 @@ import ( "crypto/rand" "embed" "encoding/json" + "fmt" "github.com/nuts-foundation/nuts-node/vcr/credential" "testing" @@ -443,39 +444,54 @@ func Test_matchConstraint(t *testing.T) { testCredential := vc.VerifiableCredential{} _ = json.Unmarshal([]byte(testCredentialString), &testCredential) + credSubjectFieldID := "credential_subject_field" typeVal := "VerifiableCredential" - f1True := Field{Path: []string{"$.credentialSubject.field"}} + f1True := Field{Id: &credSubjectFieldID, Path: []string{"$.credentialSubject.field"}} + f1TrueWithoutID := Field{Path: []string{"$.credentialSubject.field"}} f2True := Field{Path: []string{"$.type"}, Filter: &Filter{Type: "string", Const: &typeVal}} f3False := Field{Path: []string{"$.credentialSubject.field"}, Filter: &Filter{Type: "string", Const: &typeVal}} + fieldMap := map[string]interface{}{credSubjectFieldID: "value"} t.Run("single constraint match", func(t *testing.T) { - match, err := matchConstraint(&Constraints{Fields: []Field{f1True}}, testCredential) + match, value, err := matchConstraint(&Constraints{Fields: []Field{f1True}}, testCredential) require.NoError(t, err) + assert.Equal(t, fieldMap, value) + assert.True(t, match) + }) + t.Run("field match without ID is not included in values map", func(t *testing.T) { + match, values, err := matchConstraint(&Constraints{Fields: []Field{f1TrueWithoutID}}, testCredential) + + require.NoError(t, err) + assert.Empty(t, values) assert.True(t, match) }) t.Run("single constraint mismatch", func(t *testing.T) { - match, err := matchConstraint(&Constraints{Fields: []Field{f3False}}, testCredential) + match, values, err := matchConstraint(&Constraints{Fields: []Field{f3False}}, testCredential) require.NoError(t, err) + assert.Nil(t, values) assert.False(t, match) }) t.Run("multi constraint match", func(t *testing.T) { - match, err := matchConstraint(&Constraints{Fields: []Field{f1True, f2True}}, testCredential) + match, values, err := matchConstraint(&Constraints{Fields: []Field{f1True, f2True}}, testCredential) require.NoError(t, err) + assert.Equal(t, fieldMap, values) assert.True(t, match) }) t.Run("multi constraint, single mismatch", func(t *testing.T) { - match, err := matchConstraint(&Constraints{Fields: []Field{f1True, f3False}}, testCredential) + match, values, err := matchConstraint(&Constraints{Fields: []Field{f1True, f3False}}, testCredential) require.NoError(t, err) + assert.Nil(t, values) assert.False(t, match) }) t.Run("error", func(t *testing.T) { - match, err := matchConstraint(&Constraints{Fields: []Field{{Path: []string{"$$"}}}}, testCredential) + match, values, err := matchConstraint(&Constraints{Fields: []Field{{Path: []string{"$$"}}}}, testCredential) require.Error(t, err) + assert.Nil(t, values) assert.False(t, match) }) } @@ -486,50 +502,57 @@ func Test_matchField(t *testing.T) { testCredentialMap, _ := remarshalToMap(testCredential) t.Run("single path match", func(t *testing.T) { - match, err := matchField(Field{Path: []string{"$.credentialSubject.field"}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.credentialSubject.field"}}, testCredentialMap) require.NoError(t, err) + assert.Equal(t, "value", value) assert.True(t, match) }) t.Run("multi path match", func(t *testing.T) { - match, err := matchField(Field{Path: []string{"$.other", "$.credentialSubject.field"}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.other", "$.credentialSubject.field"}}, testCredentialMap) require.NoError(t, err) + assert.Equal(t, "value", value) assert.True(t, match) }) t.Run("no match", func(t *testing.T) { - match, err := matchField(Field{Path: []string{"$.foo", "$.bar"}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.foo", "$.bar"}}, testCredentialMap) require.NoError(t, err) + assert.Nil(t, value) assert.False(t, match) }) t.Run("no match, but optional", func(t *testing.T) { trueVal := true - match, err := matchField(Field{Path: []string{"$.foo", "$.bar"}, Optional: &trueVal}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.foo", "$.bar"}, Optional: &trueVal}, testCredentialMap) require.NoError(t, err) + assert.Nil(t, value) assert.True(t, match) }) t.Run("invalid match and optional", func(t *testing.T) { trueVal := true stringVal := "bar" - match, err := matchField(Field{Path: []string{"$.credentialSubject.field", "$.foo"}, Optional: &trueVal, Filter: &Filter{Const: &stringVal}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.credentialSubject.field", "$.foo"}, Optional: &trueVal, Filter: &Filter{Const: &stringVal}}, testCredentialMap) require.NoError(t, err) + assert.Nil(t, value) assert.False(t, match) }) t.Run("valid match with Filter", func(t *testing.T) { stringVal := "value" - match, err := matchField(Field{Path: []string{"$.credentialSubject.field"}, Filter: &Filter{Type: "string", Const: &stringVal}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.credentialSubject.field"}, Filter: &Filter{Type: "string", Const: &stringVal}}, testCredentialMap) require.NoError(t, err) + assert.Equal(t, stringVal, value) assert.True(t, match) }) t.Run("match on type", func(t *testing.T) { stringVal := "VerifiableCredential" - match, err := matchField(Field{Path: []string{"$.type"}, Filter: &Filter{Type: "string", Const: &stringVal}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.type"}, Filter: &Filter{Type: "string", Const: &stringVal}}, testCredentialMap) require.NoError(t, err) + assert.Equal(t, stringVal, value) assert.True(t, match) }) t.Run("match on type array", func(t *testing.T) { @@ -542,24 +565,27 @@ func Test_matchField(t *testing.T) { }` _ = json.Unmarshal([]byte(testCredentialString), &testCredentialMap) stringVal := "VerifiableCredential" - match, err := matchField(Field{Path: []string{"$.type"}, Filter: &Filter{Type: "string", Const: &stringVal}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.type"}, Filter: &Filter{Type: "string", Const: &stringVal}}, testCredentialMap) require.NoError(t, err) + assert.Equal(t, []interface{}{"VerifiableCredential"}, value) assert.True(t, match) }) t.Run("errors", func(t *testing.T) { t.Run("invalid path", func(t *testing.T) { - match, err := matchField(Field{Path: []string{"$$"}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$$"}}, testCredentialMap) require.Error(t, err) + assert.Nil(t, value) assert.False(t, match) }) t.Run("invalid pattern", func(t *testing.T) { pattern := "[" - match, err := matchField(Field{Path: []string{"$.credentialSubject.field"}, Filter: &Filter{Type: "string", Pattern: &pattern}}, testCredentialMap) + match, value, err := matchField(Field{Path: []string{"$.credentialSubject.field"}, Filter: &Filter{Type: "string", Pattern: &pattern}}, testCredentialMap) require.Error(t, err) + assert.Nil(t, value) assert.False(t, match) }) }) @@ -657,3 +683,45 @@ func credentialToJSONLD(credential vc.VerifiableCredential) vc.VerifiableCredent } return result } + +func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { + type fields struct { + Format *PresentationDefinitionClaimFormatDesignations + Frame *Frame + Id string + InputDescriptors []*InputDescriptor + Name string + Purpose *string + SubmissionRequirements []*SubmissionRequirement + } + type args struct { + credentialMap map[string]vc.VerifiableCredential + } + tests := []struct { + name string + fields fields + args args + want map[string]interface{} + wantErr assert.ErrorAssertionFunc + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + presentationDefinition := PresentationDefinition{ + Format: tt.fields.Format, + Frame: tt.fields.Frame, + Id: tt.fields.Id, + InputDescriptors: tt.fields.InputDescriptors, + Name: tt.fields.Name, + Purpose: tt.fields.Purpose, + SubmissionRequirements: tt.fields.SubmissionRequirements, + } + got, err := presentationDefinition.ResolveConstraintsFields(tt.args.credentialMap) + if !tt.wantErr(t, err, fmt.Sprintf("ResolveConstraintsFields(%v)", tt.args.credentialMap)) { + return + } + assert.Equalf(t, tt.want, got, "ResolveConstraintsFields(%v)", tt.args.credentialMap) + }) + } +} From e3c2e7eebd2aa18b909315d23ec1d3de5dac733e Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Sat, 9 Dec 2023 13:22:41 +0100 Subject: [PATCH 2/4] test --- vcr/pe/presentation_definition.go | 4 +- vcr/pe/presentation_definition_test.go | 89 +++++++++++++------------- vcr/pe/test/pd_jsonld_jwt.json | 3 + 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/vcr/pe/presentation_definition.go b/vcr/pe/presentation_definition.go index e6e72bd9d9..d4cd38912f 100644 --- a/vcr/pe/presentation_definition.go +++ b/vcr/pe/presentation_definition.go @@ -77,7 +77,7 @@ func (presentationDefinition PresentationDefinition) Match(vcs []vc.VerifiableCr // to the corresponding value from the Verifiable Credentials that map to the InputDescriptor. // The credentialMap is a map with the InputDescriptor.Id as key and the VerifiableCredential as value. // Constraints that contain no ID are ignored. -func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(credentialMap map[string]vc.VerifiableCredential) (map[string]interface{}, error) { +func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(credentialMap map[string]vc.VerifiableCredential) map[string]interface{} { result := make(map[string]interface{}) for inputDescriptorID, cred := range credentialMap { // Find the input descriptor @@ -96,7 +96,7 @@ func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(cr result[key] = value } } - return result, nil + return result } func (presentationDefinition PresentationDefinition) matchConstraints(vcs []vc.VerifiableCredential) ([]Candidate, error) { diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index 4172c32ec6..403cff972a 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -24,7 +24,6 @@ import ( "crypto/rand" "embed" "encoding/json" - "fmt" "github.com/nuts-foundation/nuts-node/vcr/credential" "testing" @@ -671,6 +670,52 @@ func Test_matchFilter(t *testing.T) { }) } +func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { + jwtCredential := credential.JWTNutsOrganizationCredential(t) + jsonldCredential := credential.JWTNutsOrganizationCredential(t) + definition := definitions().JSONLDorJWT + t.Run("match JWT", func(t *testing.T) { + credentialMap := map[string]vc.VerifiableCredential{ + "organization_credential": jwtCredential, + } + + fieldValues := definition.ResolveConstraintsFields(credentialMap) + + require.Len(t, fieldValues, 2) + assert.Equal(t, "IJbergen", fieldValues["credentialsubject_organization_city"]) + assert.Equal(t, "care", fieldValues["credentialsubject_organization_name"]) + }) + t.Run("match JSON-LD", func(t *testing.T) { + credentialMap := map[string]vc.VerifiableCredential{ + "organization_credential": jsonldCredential, + } + + fieldValues := definition.ResolveConstraintsFields(credentialMap) + + require.Len(t, fieldValues, 2) + assert.Equal(t, "IJbergen", fieldValues["credentialsubject_organization_city"]) + assert.Equal(t, "care", fieldValues["credentialsubject_organization_name"]) + }) + t.Run("input descriptor without constraints", func(t *testing.T) { + format := PresentationDefinitionClaimFormatDesignations(map[string]map[string][]string{"jwt_vc": {"alg": {"ES256"}}}) + definition := PresentationDefinition{ + InputDescriptors: []*InputDescriptor{ + { + Id: "any_credential", + Format: &format, + }, + }, + } + credentialMap := map[string]vc.VerifiableCredential{ + "any_credential": jwtCredential, + } + + fieldValues := definition.ResolveConstraintsFields(credentialMap) + + assert.Empty(t, fieldValues) + }) +} + func credentialToJSONLD(credential vc.VerifiableCredential) vc.VerifiableCredential { bytes, err := credential.MarshalJSON() if err != nil { @@ -683,45 +728,3 @@ func credentialToJSONLD(credential vc.VerifiableCredential) vc.VerifiableCredent } return result } - -func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { - type fields struct { - Format *PresentationDefinitionClaimFormatDesignations - Frame *Frame - Id string - InputDescriptors []*InputDescriptor - Name string - Purpose *string - SubmissionRequirements []*SubmissionRequirement - } - type args struct { - credentialMap map[string]vc.VerifiableCredential - } - tests := []struct { - name string - fields fields - args args - want map[string]interface{} - wantErr assert.ErrorAssertionFunc - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - presentationDefinition := PresentationDefinition{ - Format: tt.fields.Format, - Frame: tt.fields.Frame, - Id: tt.fields.Id, - InputDescriptors: tt.fields.InputDescriptors, - Name: tt.fields.Name, - Purpose: tt.fields.Purpose, - SubmissionRequirements: tt.fields.SubmissionRequirements, - } - got, err := presentationDefinition.ResolveConstraintsFields(tt.args.credentialMap) - if !tt.wantErr(t, err, fmt.Sprintf("ResolveConstraintsFields(%v)", tt.args.credentialMap)) { - return - } - assert.Equalf(t, tt.want, got, "ResolveConstraintsFields(%v)", tt.args.credentialMap) - }) - } -} diff --git a/vcr/pe/test/pd_jsonld_jwt.json b/vcr/pe/test/pd_jsonld_jwt.json index 45f77f383a..5c95549874 100644 --- a/vcr/pe/test/pd_jsonld_jwt.json +++ b/vcr/pe/test/pd_jsonld_jwt.json @@ -2,9 +2,11 @@ "id": "Definition requesting NutsOrganizationCredential", "input_descriptors": [ { + "id": "organization_credential", "constraints": { "fields": [ { + "id": "credentialsubject_organization_city", "path": [ "$.credentialSubject.organization.city", "$.credentialSubject[0].organization.city" @@ -15,6 +17,7 @@ } }, { + "id": "credentialsubject_organization_name", "path": [ "$.credentialSubject.organization.name", "$.credentialSubject[0].organization.name" From 5c58bd1fec6e6e8b2e68a9e6a1c8a6c0eb608241 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 11 Dec 2023 11:01:59 +0100 Subject: [PATCH 3/4] feedback --- vcr/pe/presentation_definition.go | 9 ++++++--- vcr/pe/presentation_definition_test.go | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/vcr/pe/presentation_definition.go b/vcr/pe/presentation_definition.go index d4cd38912f..9774e06e63 100644 --- a/vcr/pe/presentation_definition.go +++ b/vcr/pe/presentation_definition.go @@ -77,7 +77,7 @@ func (presentationDefinition PresentationDefinition) Match(vcs []vc.VerifiableCr // to the corresponding value from the Verifiable Credentials that map to the InputDescriptor. // The credentialMap is a map with the InputDescriptor.Id as key and the VerifiableCredential as value. // Constraints that contain no ID are ignored. -func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(credentialMap map[string]vc.VerifiableCredential) map[string]interface{} { +func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(credentialMap map[string]vc.VerifiableCredential) (map[string]interface{}, error) { result := make(map[string]interface{}) for inputDescriptorID, cred := range credentialMap { // Find the input descriptor @@ -91,12 +91,15 @@ func (presentationDefinition PresentationDefinition) ResolveConstraintsFields(cr if inputDescriptor.Constraints == nil { continue } - _, values, _ := matchConstraint(inputDescriptor.Constraints, cred) + _, values, err := matchConstraint(inputDescriptor.Constraints, cred) + if err != nil { + return nil, fmt.Errorf("failed to match constraint for input descriptor '%s' and credential '%s': %w", inputDescriptorID, cred.ID, err) + } for key, value := range values { result[key] = value } } - return result + return result, nil } func (presentationDefinition PresentationDefinition) matchConstraints(vcs []vc.VerifiableCredential) ([]Candidate, error) { diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index 403cff972a..3392cad76c 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -679,7 +679,7 @@ func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { "organization_credential": jwtCredential, } - fieldValues := definition.ResolveConstraintsFields(credentialMap) + fieldValues, _ := definition.ResolveConstraintsFields(credentialMap) require.Len(t, fieldValues, 2) assert.Equal(t, "IJbergen", fieldValues["credentialsubject_organization_city"]) @@ -690,7 +690,7 @@ func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { "organization_credential": jsonldCredential, } - fieldValues := definition.ResolveConstraintsFields(credentialMap) + fieldValues, _ := definition.ResolveConstraintsFields(credentialMap) require.Len(t, fieldValues, 2) assert.Equal(t, "IJbergen", fieldValues["credentialsubject_organization_city"]) @@ -710,7 +710,7 @@ func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { "any_credential": jwtCredential, } - fieldValues := definition.ResolveConstraintsFields(credentialMap) + fieldValues, _ := definition.ResolveConstraintsFields(credentialMap) assert.Empty(t, fieldValues) }) From f36817c1f89d5603121dedd0abf8fe69cf0c600a Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 11 Dec 2023 13:40:40 +0100 Subject: [PATCH 4/4] err check --- vcr/pe/presentation_definition_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index 3392cad76c..431d264d6a 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -710,8 +710,9 @@ func TestPresentationDefinition_ResolveConstraintsFields(t *testing.T) { "any_credential": jwtCredential, } - fieldValues, _ := definition.ResolveConstraintsFields(credentialMap) + fieldValues, err := definition.ResolveConstraintsFields(credentialMap) + require.NoError(t, err) assert.Empty(t, fieldValues) }) }