github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/cm/credentialmanifest_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cm_test 8 9 import ( 10 _ "embed" 11 "encoding/json" 12 "testing" 13 14 "github.com/stretchr/testify/require" 15 16 "github.com/hyperledger/aries-framework-go/pkg/doc/cm" 17 "github.com/hyperledger/aries-framework-go/pkg/doc/ld" 18 "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" 19 "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" 20 ) 21 22 // Sample Credential Manifests for a university degree. 23 var ( 24 //go:embed testdata/credential_manifest_university_degree.json 25 credentialManifestUniversityDegree []byte //nolint:gochecknoglobals 26 //go:embed testdata/credential_manifest_university_degree_with_format.json 27 credentialManifestUniversityDegreeWithFormat []byte //nolint:gochecknoglobals 28 //go:embed testdata/credential_manifest_university_degree_with_presentation_definition.json 29 credentialManifestUniversityDegreeWithPresentationDefinition []byte //nolint:gochecknoglobals 30 ) 31 32 // Sample Credential Manifests for a driver's license. 33 var ( 34 //go:embed testdata/credential_manifest_drivers_license.json 35 credentialManifestDriversLicense []byte //nolint:gochecknoglobals 36 //go:embed testdata/credential_manifest_drivers_license_with_presentation_definition.json 37 credentialManifestDriversLicenseWithPresentationDefinition []byte //nolint:gochecknoglobals 38 //go:embed testdata/credential_manifest_drivers_license_with_presentation_definition_and_format.json 39 credentialManifestDriversLicenseWithPresentationDefinitionAndFormat []byte //nolint:gochecknoglobals 40 //go:embed testdata/credential_manifest_drivers_license_with_no_display_or_styles.json 41 credentialManifestDriversLicenseWithNoDisplayOrStyles []byte //nolint:gochecknoglobals 42 ) 43 44 // Sample verifiable credential for a university degree. 45 var ( 46 //go:embed testdata/credential_university_degree.jsonld 47 validVC []byte // nolint:gochecknoglobals 48 //go:embed testdata/credential_university_degree_jwt.txt 49 validJWTVC []byte // nolint:gochecknoglobals 50 ) 51 52 // miscellaneous samples. 53 var ( 54 //go:embed testdata/credential_manifest_multiple_vcs.json 55 credentialManifestMultipleVCs []byte // nolint:gochecknoglobals 56 ) 57 58 const invalidJSONPath = "%InvalidJSONPath" 59 60 func TestCredentialManifest_Unmarshal(t *testing.T) { 61 t.Run("Valid Credential Manifest", func(t *testing.T) { 62 t.Run("Without format or Presentation Submission", func(t *testing.T) { 63 makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 64 }) 65 t.Run("With format", func(t *testing.T) { 66 makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) 67 }) 68 t.Run("With Presentation Submission", func(t *testing.T) { 69 makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithPresentationDefinition) 70 }) 71 t.Run("Without output descriptor display", func(t *testing.T) { 72 makeCredentialManifestFromBytes(t, credentialManifestDriversLicenseWithNoDisplayOrStyles) 73 }) 74 t.Run("Without issuer optional properties", func(t *testing.T) { 75 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 76 77 credentialManifest.Issuer = cm.Issuer{ID: "valid ID"} 78 79 invalidCredentialManifest, err := json.Marshal(credentialManifest) 80 require.NoError(t, err) 81 82 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 83 require.NoError(t, err) 84 }) 85 }) 86 t.Run("Invalid Credential Manifest", func(t *testing.T) { 87 t.Run("Missing ID", func(t *testing.T) { 88 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 89 90 credentialManifest.ID = "" 91 92 invalidCredentialManifest, err := json.Marshal(credentialManifest) 93 require.NoError(t, err) 94 95 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 96 require.EqualError(t, err, "invalid credential manifest: ID missing") 97 }) 98 t.Run("Missing issuer", func(t *testing.T) { 99 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 100 101 credentialManifest.Issuer = cm.Issuer{} 102 103 invalidCredentialManifest, err := json.Marshal(credentialManifest) 104 require.NoError(t, err) 105 106 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 107 require.EqualError(t, err, "invalid credential manifest: issuer ID missing") 108 }) 109 t.Run("Missing issuer ID", func(t *testing.T) { 110 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 111 112 credentialManifest.Issuer.ID = "" 113 114 invalidCredentialManifest, err := json.Marshal(credentialManifest) 115 require.NoError(t, err) 116 117 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 118 require.EqualError(t, err, "invalid credential manifest: issuer ID missing") 119 }) 120 t.Run("No output descriptors", func(t *testing.T) { 121 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 122 123 credentialManifest.OutputDescriptors = nil 124 125 invalidCredentialManifest, err := json.Marshal(credentialManifest) 126 require.NoError(t, err) 127 128 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 129 require.EqualError(t, err, "invalid credential manifest: no output descriptors found") 130 }) 131 t.Run("Output descriptor missing ID", func(t *testing.T) { 132 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 133 134 credentialManifest.OutputDescriptors[0].ID = "" 135 136 invalidCredentialManifest, err := json.Marshal(credentialManifest) 137 require.NoError(t, err) 138 139 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 140 require.EqualError(t, err, "invalid credential manifest: missing ID for output descriptor at index 0") 141 }) 142 t.Run("Duplicate output descriptor IDs", func(t *testing.T) { 143 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 144 145 credentialManifest.OutputDescriptors = 146 append(credentialManifest.OutputDescriptors, 147 &cm.OutputDescriptor{ID: credentialManifest.OutputDescriptors[0].ID}) 148 149 invalidCredentialManifest, err := json.Marshal(credentialManifest) 150 require.NoError(t, err) 151 152 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 153 require.EqualError(t, err, "invalid credential manifest: the ID bachelors_degree appears "+ 154 "in multiple output descriptors") 155 }) 156 t.Run("Missing schema for output descriptor", func(t *testing.T) { 157 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 158 159 credentialManifest.OutputDescriptors[0].Schema = "" 160 161 invalidCredentialManifest, err := json.Marshal(credentialManifest) 162 require.NoError(t, err) 163 164 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 165 require.EqualError(t, err, "invalid credential manifest: missing schema for "+ 166 "output descriptor at index 0") 167 }) 168 t.Run("Invalid JSONPath", func(t *testing.T) { 169 t.Run("Display title", func(t *testing.T) { 170 var credentialManifest cm.CredentialManifest 171 172 err := json.Unmarshal(createMarshalledCredentialManifestWithInvalidTitleJSONPath(t), &credentialManifest) 173 require.EqualError(t, err, "invalid credential manifest: display title for output descriptor "+ 174 `at index 0 is invalid: path "%InvalidJSONPath" at index 0 is not a valid JSONPath: parsing error: `+ 175 `%InvalidJSONPath :1:1 - 1:2 unexpected "%" while scanning extensions`) 176 }) 177 t.Run("Display subtitle", func(t *testing.T) { 178 var credentialManifest cm.CredentialManifest 179 180 err := json.Unmarshal(createMarshalledCredentialManifestWithInvalidSubtitleJSONPath(t), &credentialManifest) 181 require.EqualError(t, err, "invalid credential manifest: display subtitle for output descriptor "+ 182 `at index 0 is invalid: path "%InvalidJSONPath" at index 0 is not a valid JSONPath: parsing error: `+ 183 `%InvalidJSONPath :1:1 - 1:2 unexpected "%" while scanning extensions`) 184 }) 185 t.Run("Display description", func(t *testing.T) { 186 var credentialManifest cm.CredentialManifest 187 188 err := json.Unmarshal(createMarshalledCredentialManifestWithInvalidDescriptionJSONPath(t), &credentialManifest) 189 require.EqualError(t, err, "invalid credential manifest: display description for output "+ 190 `descriptor at index 0 is invalid: path "%InvalidJSONPath" at index 0 is not a valid JSONPath: `+ 191 `parsing error: %InvalidJSONPath :1:1 - 1:2 unexpected "%" while scanning extensions`) 192 }) 193 t.Run("Display property", func(t *testing.T) { 194 var credentialManifest cm.CredentialManifest 195 196 err := json.Unmarshal(createMarshalledCredentialManifestWithInvalidPropertyJSONPath(t), &credentialManifest) 197 require.EqualError(t, err, "invalid credential manifest: display property at index 0 for output "+ 198 `descriptor at index 0 is invalid: path "%InvalidJSONPath" at index 0 is not a valid JSONPath: `+ 199 `parsing error: %InvalidJSONPath :1:1 - 1:2 unexpected "%" while scanning extensions`) 200 }) 201 }) 202 t.Run("Invalid schema type", func(t *testing.T) { 203 var credentialManifest cm.CredentialManifest 204 205 err := json.Unmarshal(createMarshalledCredentialManifestWithInvalidSchemaType(t), &credentialManifest) 206 require.EqualError(t, err, "invalid credential manifest: display title for output descriptor at "+ 207 "index 0 is invalid: InvalidSchemaType is not a valid schema type") 208 }) 209 t.Run("Invalid schema format", func(t *testing.T) { 210 var credentialManifest cm.CredentialManifest 211 212 err := json.Unmarshal(createMarshalledCredentialManifestWithInvalidSchemaFormat(t), &credentialManifest) 213 require.EqualError(t, err, "invalid credential manifest: display title for output descriptor at "+ 214 "index 0 is invalid: UnknownFormat is not a valid string schema format") 215 }) 216 t.Run("Missing paths and text", func(t *testing.T) { 217 var credentialManifest cm.CredentialManifest 218 219 err := json.Unmarshal(createMarshalledCredentialManifestWithMissingPropertyJSONPathAndText(t), &credentialManifest) 220 require.EqualError(t, err, "invalid credential manifest: display property at index 0 for output descriptor at "+ 221 "index 0 is invalid: display mapping object must contain either a paths or a text property") 222 }) 223 t.Run("Missing image URI", func(t *testing.T) { 224 credentialManifest := createCredentialManifestWithNoImageURI(t) 225 226 invalidCredentialManifest, err := json.Marshal(credentialManifest) 227 require.NoError(t, err) 228 229 err = json.Unmarshal(invalidCredentialManifest, &credentialManifest) 230 require.EqualError(t, err, "invalid credential manifest: uri missing for image at index 0") 231 }) 232 }) 233 } 234 235 func TestResolveResponse(t *testing.T) { 236 type match struct { 237 Title string 238 Subtitle string 239 Description string 240 Properties map[string]*cm.ResolvedProperty 241 } 242 243 // nolint:lll 244 t.Run("Successes", func(t *testing.T) { 245 testTable := map[string]struct { 246 manifest []byte 247 response []byte 248 expected map[string]*match 249 }{ 250 "single descriptor and credential": { 251 manifest: credentialManifestDriversLicense, 252 response: vpWithDriversLicenseVCAndCredentialResponse, 253 expected: map[string]*match{ 254 "driver_license_output": { 255 Title: "Washington State Driver License", 256 Subtitle: "Class A, Commercial", 257 Description: "License to operate a vehicle with a gross combined weight " + 258 "rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) " + 259 "being towed is over 10,000 pounds.", 260 Properties: map[string]*cm.ResolvedProperty{ 261 "Driving License Number": { 262 Label: "Driving License Number", 263 Value: "34DGE352", 264 Schema: cm.Schema{ 265 Type: "boolean", 266 }, 267 }, 268 }, 269 }, 270 }, 271 }, 272 "multiple descriptor and credentials": { 273 manifest: credentialManifestMultipleVCs, 274 response: vpMultipleWithCredentialResponse, 275 expected: map[string]*match{ 276 "prc_output": { 277 Title: "Permanent Resident Card", 278 Subtitle: "Permanent Resident Card", 279 Description: "PR card of John Smith.", 280 Properties: map[string]*cm.ResolvedProperty{ 281 "Card Holder's family name": {Label: "Card Holder's family name", Value: "SMITH", Schema: cm.Schema{Type: "string"}}, 282 "Card Holder's first name": {Label: "Card Holder's first name", Value: "JOHN", Schema: cm.Schema{Type: "string"}}, 283 }, 284 }, 285 "udc_output": { 286 Title: "Bachelor's Degree", 287 Description: "Awarded for completing a four year program at Example University.", 288 Properties: map[string]*cm.ResolvedProperty{ 289 "Degree": {Label: "Degree", Value: "BachelorDegree", Schema: cm.Schema{Type: "string"}}, 290 "Degree Holder's name": {Label: "Degree Holder's name", Value: "Jayden Doe", Schema: cm.Schema{Type: "string"}}, 291 }, 292 }, 293 }, 294 }, 295 "single descriptor and credentials for multi descriptor manifest": { 296 manifest: credentialManifestMultipleVCs, 297 response: vpWithDriversLicenseVCAndCredentialResponse, 298 expected: map[string]*match{ 299 "driver_license_output": { 300 Title: "Washington State Driver License", 301 Subtitle: "Class A, Commercial", 302 Description: "License to operate a vehicle with a gross combined weight " + 303 "rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) " + 304 "being towed is over 10,000 pounds.", 305 Properties: map[string]*cm.ResolvedProperty{ 306 "Driving License Number": {Label: "Driving License Number", Value: "34DGE352", Schema: cm.Schema{Type: "boolean"}}, 307 }, 308 }, 309 }, 310 }, 311 } 312 313 t.Parallel() 314 315 for testName, testData := range testTable { 316 t.Run(testName, func(t *testing.T) { 317 response := makePresentationFromBytes(t, testData.response, testName) 318 manifest := &cm.CredentialManifest{} 319 require.NoError(t, manifest.UnmarshalJSON(testData.manifest)) 320 321 results, err := manifest.ResolveResponse(response) 322 require.NoError(t, err) 323 require.Len(t, results, len(testData.expected)) 324 325 for _, r := range results { 326 require.NotEmpty(t, r.DescriptorID) 327 expected, ok := testData.expected[r.DescriptorID] 328 require.True(t, ok, "unexpected descriptor ID '%s' in resolved properties", r.DescriptorID) 329 require.Equal(t, expected.Title, r.Title) 330 require.Equal(t, expected.Subtitle, r.Subtitle) 331 require.Equal(t, expected.Description, r.Description) 332 require.NotEmpty(t, r.Styles.Background) 333 require.Len(t, r.Properties, len(expected.Properties)) 334 335 for _, resolvedProperty := range r.Properties { 336 expectedVal, ok := expected.Properties[resolvedProperty.Label] 337 require.True(t, ok, "expected to find '%s' label in resolved properties", resolvedProperty.Label) 338 require.EqualValues(t, expectedVal, resolvedProperty) 339 } 340 } 341 }) 342 } 343 }) 344 345 t.Run("Response format failures", func(t *testing.T) { 346 response := makePresentationFromBytes(t, vpMultipleWithCredentialResponse, t.Name()) 347 manifest := &cm.CredentialManifest{} 348 require.NoError(t, manifest.UnmarshalJSON(credentialManifestMultipleVCs)) 349 350 t.Parallel() 351 352 testTable := map[string]struct { 353 credResponse []byte 354 error string 355 }{ 356 "missing credential": { 357 credResponse: []byte(`{ 358 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 359 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 360 "descriptor_map":[ 361 { 362 "id":"udc_output", 363 "format":"ldp_vc", 364 "path":"$.verifiableCredential[0]" 365 }, 366 { 367 "id":"prc_output", 368 "format":"ldp_vc", 369 "path":"$.verifiableCredential[5]" 370 } 371 ] 372 }`), 373 error: "failed to select vc from descriptor", 374 }, 375 "missing path in descriptor": { 376 credResponse: []byte(`{ 377 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 378 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 379 "descriptor_map":[ 380 { 381 "id":"udc_output", 382 "format":"ldp_vc" 383 }, 384 { 385 "id":"prc_output", 386 "format":"ldp_vc", 387 "path":"$.verifiableCredential[5]" 388 } 389 ] 390 }`), 391 error: "invalid credential path", 392 }, 393 "descriptor id missing": { 394 credResponse: []byte(`{ 395 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 396 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 397 "descriptor_map":[ 398 { 399 "format":"ldp_vc", 400 "path":"$.verifiableCredential[0]" 401 } 402 ] 403 }`), 404 error: "invalid descriptor ID", 405 }, 406 "incorrect descriptor format": { 407 credResponse: []byte(`{ 408 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 409 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 410 "descriptor_map":[ 411 "format", "ldp_vc" 412 ] 413 }`), 414 error: "invalid descriptor format", 415 }, 416 "empty descriptor map": { 417 credResponse: []byte(`{ 418 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 419 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 420 "descriptor_map":[] 421 }`), 422 error: "", 423 }, 424 "invalid descriptor format": { 425 credResponse: []byte(`{ 426 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 427 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 428 "descriptor_map": {} 429 }`), 430 error: "invalid descriptor map", 431 }, 432 "not matching manifest": { 433 credResponse: []byte(`{ 434 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 435 "manifest_id":"invalid", 436 "descriptor_map": {} 437 }`), 438 error: "credential response not matching", 439 }, 440 "matching descriptor not found in manifest": { 441 credResponse: []byte(`{ 442 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 443 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 444 "descriptor_map":[ 445 { 446 "id":"udc_output_missing", 447 "format":"ldp_vc", 448 "path":"$.verifiableCredential[0]" 449 }, 450 { 451 "id":"prc_output", 452 "format":"ldp_vc", 453 "path":"$.verifiableCredential[1]" 454 } 455 ] 456 }`), 457 error: "unable to find matching output descriptor from manifest", 458 }, 459 } 460 461 for testName, testData := range testTable { 462 t.Run(testName, func(t *testing.T) { 463 var credResponse map[string]interface{} 464 require.NoError(t, json.Unmarshal(testData.credResponse, &credResponse)) 465 466 response.CustomFields["credential_response"] = credResponse 467 468 results, err := manifest.ResolveResponse(response) 469 require.Empty(t, results) 470 if testData.error == "" { 471 require.NoError(t, err) 472 473 return 474 } 475 476 require.Error(t, err) 477 require.Contains(t, err.Error(), testData.error) 478 }) 479 } 480 }) 481 482 t.Run("Failures", func(t *testing.T) { 483 response := makePresentationFromBytes(t, vpMultipleWithCredentialResponse, t.Name()) 484 manifest := &cm.CredentialManifest{} 485 require.NoError(t, manifest.UnmarshalJSON(credentialManifestMultipleVCs)) 486 487 // resolve err (resolved value is not string) 488 for _, descr := range manifest.OutputDescriptors { 489 if descr.ID == "udc_output" { 490 incorrectProperties := `[{ 491 "path":[ 492 "$." 493 ], 494 "schema": { 495 "type": "string" 496 }, 497 "fallback":"Unknown", 498 "label":"Driving License Number" 499 }]` 500 501 require.NoError(t, json.Unmarshal([]byte(incorrectProperties), &descr.Display.Properties)) 502 } 503 } 504 results, err := manifest.ResolveResponse(response) 505 require.Empty(t, results) 506 require.Error(t, err) 507 require.Contains(t, err.Error(), "failed to resolve credential by descriptor") 508 509 // unsupported formats 510 var credResponse map[string]interface{} 511 require.NoError(t, json.Unmarshal([]byte(`{ 512 "id":"a30e3b91-fb77-4d22-95fa-871689c322e2", 513 "manifest_id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", 514 "descriptor_map":[ 515 { 516 "id":"udc_output", 517 "format":"jwt_vc", 518 "path":"$.verifiableCredential[0]" 519 }, 520 { 521 "id":"prc_output", 522 "format":"jwt_vc", 523 "path":"$.verifiableCredential[1]" 524 } 525 ] 526 }`), &credResponse)) 527 528 response.CustomFields["credential_response"] = credResponse 529 530 results, err = manifest.ResolveResponse(response) 531 require.Empty(t, results) 532 require.NoError(t, err) 533 534 // marshal presentation error 535 response.CustomFields["invalid"] = make(chan int) 536 results, err = manifest.ResolveResponse(response) 537 require.Empty(t, results) 538 require.Error(t, err) 539 require.Contains(t, err.Error(), "failed to marshal vp") 540 541 // missing credential response 542 delete(response.CustomFields, "credential_response") 543 results, err = manifest.ResolveResponse(response) 544 require.Empty(t, results) 545 require.Error(t, err) 546 require.Contains(t, err.Error(), "invalid credential response") 547 }) 548 } 549 550 func TestResolveCredential(t *testing.T) { 551 t.Run("Successes", func(t *testing.T) { 552 testCases := []struct { 553 name string 554 credResolver func(t *testing.T) cm.CredentialToResolveOption 555 }{ 556 { 557 name: "resolve credential struct", 558 credResolver: func(t *testing.T) cm.CredentialToResolveOption { 559 t.Helper() 560 561 vc := parseTestCredential(t, validVC) 562 563 return cm.CredentialToResolve(vc) 564 }, 565 }, 566 { 567 name: "resolve raw JSON-LD credential", 568 credResolver: func(t *testing.T) cm.CredentialToResolveOption { 569 t.Helper() 570 571 return cm.RawCredentialToResolve(validVC) 572 }, 573 }, 574 { 575 name: "resolve raw JWT credential", 576 credResolver: func(t *testing.T) cm.CredentialToResolveOption { 577 t.Helper() 578 579 return cm.RawCredentialToResolve(validJWTVC) 580 }, 581 }, 582 } 583 584 for _, tc := range testCases { 585 t.Run(tc.name, func(t *testing.T) { 586 manifest := &cm.CredentialManifest{} 587 require.NoError(t, manifest.UnmarshalJSON(credentialManifestUniversityDegree)) 588 589 result, err := manifest.ResolveCredential("bachelors_degree", tc.credResolver(t)) 590 require.NoError(t, err) 591 require.NotEmpty(t, result) 592 require.Equal(t, result.Title, "Bachelor of Applied Science") 593 require.Equal(t, result.Subtitle, "Electrical Systems Specialty") 594 595 expected := map[string]*cm.ResolvedProperty{ 596 "With distinction": { 597 Label: "With distinction", 598 Value: true, 599 Schema: cm.Schema{ 600 Type: "boolean", 601 }, 602 }, 603 "Years studied": { 604 Label: "Years studied", 605 Value: float64(4), 606 Schema: cm.Schema{ 607 Type: "number", 608 }, 609 }, 610 } 611 612 for _, property := range result.Properties { 613 expectedVal, ok := expected[property.Label] 614 require.True(t, ok, "unexpected label '%s' in resolved properties", property.Label) 615 require.EqualValues(t, expectedVal, property) 616 } 617 }) 618 } 619 }) 620 621 t.Run("Failures", func(t *testing.T) { 622 manifest := &cm.CredentialManifest{} 623 require.NoError(t, manifest.UnmarshalJSON(credentialManifestUniversityDegree)) 624 625 // invalid credential to resolve 626 result, err := manifest.ResolveCredential("bachelors_degree", nil) 627 require.Empty(t, result) 628 require.Error(t, err) 629 require.Contains(t, err.Error(), "credential to resolve is not provided") 630 631 // invalid raw credential to resolve 632 result, err = manifest.ResolveCredential("bachelors_degree", cm.RawCredentialToResolve([]byte("---"))) 633 require.Empty(t, result) 634 require.Error(t, err) 635 require.Contains(t, err.Error(), "invalid character") 636 637 vc := parseTestCredential(t, validVC) 638 639 // descriptor not found 640 result, err = manifest.ResolveCredential("bachelors_degree_incorrect", cm.CredentialToResolve(vc)) 641 require.Empty(t, result) 642 require.Error(t, err) 643 require.Contains(t, err.Error(), "unable to find matching descriptor") 644 645 // credential marshal error 646 vc.CustomFields["invalid"] = make(chan int) 647 648 result, err = manifest.ResolveCredential("bachelors_degree", cm.CredentialToResolve(vc)) 649 require.Empty(t, result) 650 require.Error(t, err) 651 require.Contains(t, err.Error(), "JSON marshalling of verifiable credential") 652 }) 653 } 654 655 func createMarshalledCredentialManifestWithInvalidTitleJSONPath(t *testing.T) []byte { 656 credentialManifest := createCredentialManifestWithInvalidTitleJSONPath(t) 657 658 credentialManifestWithInvalidJSONPathBytes, err := json.Marshal(credentialManifest) 659 require.NoError(t, err) 660 661 return credentialManifestWithInvalidJSONPathBytes 662 } 663 664 func createCredentialManifestWithInvalidTitleJSONPath(t *testing.T) cm.CredentialManifest { 665 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 666 667 credentialManifest.OutputDescriptors[0].Display.Title.Paths[0] = invalidJSONPath 668 669 return credentialManifest 670 } 671 672 func createMarshalledCredentialManifestWithInvalidSubtitleJSONPath(t *testing.T) []byte { 673 credentialManifest := createCredentialManifestWithInvalidSubtitleJSONPath(t) 674 675 credentialManifestWithInvalidJSONPathBytes, err := json.Marshal(credentialManifest) 676 require.NoError(t, err) 677 678 return credentialManifestWithInvalidJSONPathBytes 679 } 680 681 func createCredentialManifestWithInvalidSubtitleJSONPath(t *testing.T) cm.CredentialManifest { 682 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 683 684 credentialManifest.OutputDescriptors[0].Display.Subtitle.Paths[0] = invalidJSONPath 685 686 return credentialManifest 687 } 688 689 func createMarshalledCredentialManifestWithInvalidDescriptionJSONPath(t *testing.T) []byte { 690 credentialManifest := createCredentialManifestWithInvalidDescriptionJSONPath(t) 691 692 credentialManifestWithInvalidJSONPathBytes, err := json.Marshal(credentialManifest) 693 require.NoError(t, err) 694 695 return credentialManifestWithInvalidJSONPathBytes 696 } 697 698 func createCredentialManifestWithInvalidDescriptionJSONPath(t *testing.T) cm.CredentialManifest { 699 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 700 701 credentialManifest.OutputDescriptors[0].Display.Description.Paths = []string{invalidJSONPath} 702 703 return credentialManifest 704 } 705 706 func createMarshalledCredentialManifestWithInvalidPropertyJSONPath(t *testing.T) []byte { 707 credentialManifest := createCredentialManifestWithInvalidPropertyJSONPath(t) 708 709 credentialManifestWithInvalidJSONPathBytes, err := json.Marshal(credentialManifest) 710 require.NoError(t, err) 711 712 return credentialManifestWithInvalidJSONPathBytes 713 } 714 715 func createCredentialManifestWithInvalidPropertyJSONPath(t *testing.T) cm.CredentialManifest { 716 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 717 718 credentialManifest.OutputDescriptors[0].Display.Properties[0].Paths[0] = invalidJSONPath 719 720 return credentialManifest 721 } 722 723 func createMarshalledCredentialManifestWithInvalidSchemaType(t *testing.T) []byte { 724 credentialManifest := createCredentialManifestWithInvalidSchemaType(t) 725 726 credentialManifestWithInvalidJSONPathBytes, err := json.Marshal(credentialManifest) 727 require.NoError(t, err) 728 729 return credentialManifestWithInvalidJSONPathBytes 730 } 731 732 func createCredentialManifestWithInvalidSchemaType(t *testing.T) cm.CredentialManifest { 733 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 734 735 credentialManifest.OutputDescriptors[0].Display.Title.Schema.Type = "InvalidSchemaType" 736 737 return credentialManifest 738 } 739 740 func createMarshalledCredentialManifestWithInvalidSchemaFormat(t *testing.T) []byte { 741 credentialManifest := createCredentialManifestWithInvalidSchemaFormat(t) 742 743 credentialManifestWithInvalidJSONPathBytes, err := json.Marshal(credentialManifest) 744 require.NoError(t, err) 745 746 return credentialManifestWithInvalidJSONPathBytes 747 } 748 749 func createCredentialManifestWithInvalidSchemaFormat(t *testing.T) cm.CredentialManifest { 750 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 751 752 credentialManifest.OutputDescriptors[0].Display.Title.Schema = cm.Schema{ 753 Type: "string", 754 Format: "UnknownFormat", 755 } 756 757 return credentialManifest 758 } 759 760 func createCredentialManifestWithMissingPropertyJSONPathAndText(t *testing.T) cm.CredentialManifest { 761 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 762 763 credentialManifest.OutputDescriptors[0].Display.Properties[0].Paths = []string{} 764 credentialManifest.OutputDescriptors[0].Display.Properties[0].Text = "" 765 766 return credentialManifest 767 } 768 769 func createMarshalledCredentialManifestWithMissingPropertyJSONPathAndText(t *testing.T) []byte { 770 credentialManifest := createCredentialManifestWithMissingPropertyJSONPathAndText(t) 771 772 credentialManifestWithNoPathsOrType, err := json.Marshal(credentialManifest) 773 require.NoError(t, err) 774 775 return credentialManifestWithNoPathsOrType 776 } 777 778 func createCredentialManifestWithNilJWTFormat(t *testing.T) cm.CredentialManifest { 779 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) 780 781 credentialManifest.Format.Jwt = nil 782 783 return credentialManifest 784 } 785 786 func createCredentialManifestWithNilLDPFormat(t *testing.T) cm.CredentialManifest { 787 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) 788 789 credentialManifest.Format.Ldp = nil 790 791 return credentialManifest 792 } 793 794 func createCredentialManifestWithNoImageURI(t *testing.T) cm.CredentialManifest { 795 credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) 796 797 credentialManifest.OutputDescriptors[0].Styles.Thumbnail = &cm.ImageURIWithAltText{ 798 Alt: "valid alt", 799 } 800 require.Empty(t, credentialManifest.OutputDescriptors[0].Styles.Thumbnail.URI) 801 802 return credentialManifest 803 } 804 805 func makeCredentialManifestFromBytes(t *testing.T, credentialManifestBytes []byte) cm.CredentialManifest { 806 var credentialManifest cm.CredentialManifest 807 808 err := json.Unmarshal(credentialManifestBytes, &credentialManifest) 809 require.NoError(t, err) 810 811 return credentialManifest 812 } 813 814 func parseTestCredential(t *testing.T, vcData []byte) *verifiable.Credential { 815 t.Helper() 816 817 vc, err := verifiable.ParseCredential(vcData, verifiable.WithJSONLDDocumentLoader(createTestDocumentLoader(t))) 818 require.NoError(t, err) 819 820 return vc 821 } 822 823 func createTestDocumentLoader(t *testing.T) *ld.DocumentLoader { 824 t.Helper() 825 826 loader, err := ldtestutil.DocumentLoader() 827 require.NoError(t, err) 828 829 return loader 830 }