github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/cm/credentialresponse_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  	"reflect"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential"
    18  	"github.com/hyperledger/aries-framework-go/pkg/doc/cm"
    19  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    20  )
    21  
    22  var (
    23  	//go:embed testdata/credential_response_university_degree.json
    24  	validCredentialResponse []byte //nolint:gochecknoglobals
    25  	//go:embed testdata/issue_credential_message_university_degree.json
    26  	validIssueCredentialMessage []byte //nolint:gochecknoglobals
    27  	//go:embed testdata/VP_with_drivers_license_VC.json
    28  	vpWithDriversLicenseVC []byte //nolint:gochecknoglobals
    29  	//go:embed testdata/VP_with_drivers_license_VC_and_credential_response.json
    30  	vpWithDriversLicenseVCAndCredentialResponse []byte //nolint:gochecknoglobals
    31  	// The "minimal" VP below is ones that was created by a call to verifiable.NewPresentation() with no
    32  	// arguments/options, which is how the cm.PresentCredentialResponse method generates a VP if the
    33  	// WithExistingPresentationForPresentCredentialResponse option is not used.
    34  	//go:embed testdata/VP_minimal_with_credential_response.json
    35  	vpMinimalWithCredentialResponse []byte //nolint:gochecknoglobals
    36  	//go:embed testdata/VP_with_multiple_VCs_and_credential_response.json
    37  	vpMultipleWithCredentialResponse []byte //nolint:gochecknoglobals
    38  )
    39  
    40  func TestCredentialResponse_Unmarshal(t *testing.T) {
    41  	t.Run("Valid Credential Response", func(t *testing.T) {
    42  		makeValidCredentialResponse(t)
    43  	})
    44  	t.Run("Missing ID", func(t *testing.T) {
    45  		credentialResponseBytes := makeCredentialResponseWithMissingID(t)
    46  
    47  		var credentialResponse cm.CredentialResponse
    48  
    49  		err := json.Unmarshal(credentialResponseBytes, &credentialResponse)
    50  		require.EqualError(t, err, "invalid Credential Response: missing ID")
    51  	})
    52  	t.Run("Missing Manifest ID", func(t *testing.T) {
    53  		credentialResponseBytes := makeCredentialResponseWithMissingManifestID(t)
    54  
    55  		var credentialResponse cm.CredentialResponse
    56  
    57  		err := json.Unmarshal(credentialResponseBytes, &credentialResponse)
    58  		require.EqualError(t, err, "invalid Credential Response: missing manifest ID")
    59  	})
    60  }
    61  
    62  func TestCredentialResponse_ResolveDescriptorMap(t *testing.T) {
    63  	testDocumentLoaderOption := verifiable.WithJSONLDDocumentLoader(createTestDocumentLoader(t))
    64  
    65  	t.Run("Success", func(t *testing.T) {
    66  		credentialResponse := makeValidCredentialResponse(t)
    67  		issueCredentialMessage := makeValidIssueCredentialMessage(t)
    68  
    69  		verifiableCredentials, err := credentialResponse.ResolveDescriptorMaps(
    70  			issueCredentialMessage.Attachments[0].Data.JSON, testDocumentLoaderOption)
    71  		require.NoError(t, err)
    72  		require.Len(t, verifiableCredentials, 1)
    73  
    74  		originalVC := getVCFromValidIssueCredentialMessage(t)
    75  
    76  		require.True(t, reflect.DeepEqual(verifiableCredentials[0], originalVC),
    77  			"resolved VC differs from the original VC")
    78  	})
    79  	t.Run("Invalid JSONPath", func(t *testing.T) {
    80  		credentialResponse := makeCredentialResponseWithInvalidJSONPath(t)
    81  		issueCredentialMessage := makeValidIssueCredentialMessage(t)
    82  
    83  		verifiableCredentials, err := credentialResponse.ResolveDescriptorMaps(
    84  			issueCredentialMessage.Attachments[0].Data.JSON, testDocumentLoaderOption)
    85  		require.EqualError(t, err, "failed to resolve descriptor map at index 0: parsing error: "+
    86  			`%InvalidJSONPath	:1:1 - 1:2 unexpected "%" while scanning extensions`)
    87  		require.Nil(t, verifiableCredentials)
    88  	})
    89  	t.Run("JSON data is not a map[string]interface{}", func(t *testing.T) {
    90  		credentialResponse := makeValidCredentialResponse(t)
    91  
    92  		verifiableCredentials, err := credentialResponse.ResolveDescriptorMaps(1)
    93  		require.EqualError(t, err, "the given JSON data could not be asserted as a map[string]interface{}")
    94  		require.Nil(t, verifiableCredentials)
    95  	})
    96  	t.Run("Failed to parse VC", func(t *testing.T) {
    97  		credentialResponse := makeValidCredentialResponse(t)
    98  		issueCredentialMessage := makeIssueCredentialMessageWithInvalidVC(t)
    99  
   100  		verifiableCredentials, err := credentialResponse.ResolveDescriptorMaps(
   101  			issueCredentialMessage.Attachments[0].Data.JSON)
   102  		require.EqualError(t, err, "failed to resolve descriptor map at index 0: failed to parse "+
   103  			"credential: decode new credential: embedded proof is not JSON: json: cannot unmarshal string "+
   104  			"into Go value of type map[string]interface {}")
   105  		require.Nil(t, verifiableCredentials)
   106  	})
   107  }
   108  
   109  func TestPresentCredentialResponse(t *testing.T) {
   110  	t.Run("Without using WithExistingPresentationForPresentCredentialResponse option", func(t *testing.T) {
   111  		credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestDriversLicenseWithPresentationDefinition)
   112  
   113  		presentation, err := cm.PresentCredentialResponse(&credentialManifest)
   114  		require.NoError(t, err)
   115  		require.NotNil(t, presentation)
   116  
   117  		expectedPresentation := makePresentationFromBytes(t, vpMinimalWithCredentialResponse,
   118  			"Present Credential Response without existing Presentation")
   119  
   120  		reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentation,
   121  			"Present Credential Response without existing presentation")
   122  
   123  		makeCredentialResponseIDsTheSame(t, reunmarshalledPresentation, expectedPresentation)
   124  		require.True(t, reflect.DeepEqual(reunmarshalledPresentation, expectedPresentation),
   125  			"the presentation with a Credential Response added to it differs from what was expected")
   126  	})
   127  	t.Run("Using WithExistingPresentationForPresentCredentialResponse option", func(t *testing.T) {
   128  		t.Run("CustomFields is not nil", func(t *testing.T) {
   129  			testName := "Present Credential Response with existing presentation, CustomFields is not nil"
   130  
   131  			presentation := makePresentationFromBytes(t, vpWithDriversLicenseVC, testName)
   132  
   133  			doPresentCredentialResponseTestWithExistingPresentation(t, presentation, testName)
   134  		})
   135  		t.Run("CustomFields is nil", func(t *testing.T) {
   136  			testName := "Present Credential Response with existing presentation, CustomFields is nil"
   137  
   138  			presentation := makePresentationFromBytes(t, vpWithDriversLicenseVC, testName)
   139  
   140  			presentation.CustomFields = nil
   141  
   142  			doPresentCredentialResponseTestWithExistingPresentation(t, presentation, testName)
   143  		})
   144  	})
   145  }
   146  
   147  func doPresentCredentialResponseTestWithExistingPresentation(t *testing.T,
   148  	presentationToAddCredentialResponseTo *verifiable.Presentation, testName string) {
   149  	credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestDriversLicenseWithPresentationDefinition)
   150  
   151  	presentationWithAddedCredentialResponse, err := cm.PresentCredentialResponse(&credentialManifest,
   152  		cm.WithExistingPresentationForPresentCredentialResponse(presentationToAddCredentialResponseTo))
   153  	require.NoError(t, err)
   154  
   155  	expectedPresentation := makePresentationFromBytes(t, vpWithDriversLicenseVCAndCredentialResponse, testName)
   156  
   157  	reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentationWithAddedCredentialResponse, testName)
   158  
   159  	makeCredentialResponseIDsTheSame(t, reunmarshalledPresentation, expectedPresentation)
   160  
   161  	require.True(t, reflect.DeepEqual(reunmarshalledPresentation, expectedPresentation),
   162  		"the presentation with a Credential Response added to it differs from what was expected")
   163  }
   164  
   165  // The credential Response ID is randomly generated in the PresentCredentialResponse method, so this method
   166  // is useful for allowing two presentations created by that method to be compared using reflect.DeepEqual.
   167  func makeCredentialResponseIDsTheSame(t *testing.T, presentation1,
   168  	presentation2 *verifiable.Presentation) {
   169  	credentialResponseFromPresentation1, ok :=
   170  		presentation1.CustomFields["credential_response"].(map[string]interface{})
   171  	require.True(t, ok)
   172  
   173  	credentialResponseFromPresentation2, ok :=
   174  		presentation2.CustomFields["credential_response"].(map[string]interface{})
   175  	require.True(t, ok)
   176  
   177  	credentialResponseFromPresentation2["id"] = credentialResponseFromPresentation1["id"]
   178  }
   179  
   180  func makeValidCredentialResponse(t *testing.T) cm.CredentialResponse {
   181  	var credentialResponse cm.CredentialResponse
   182  
   183  	err := json.Unmarshal(validCredentialResponse, &credentialResponse)
   184  	require.NoError(t, err)
   185  
   186  	return credentialResponse
   187  }
   188  
   189  func makeValidIssueCredentialMessage(t *testing.T) issuecredential.IssueCredentialV3 {
   190  	var issueCredentialMessage issuecredential.IssueCredentialV3
   191  
   192  	err := json.Unmarshal(validIssueCredentialMessage, &issueCredentialMessage)
   193  	require.NoError(t, err)
   194  
   195  	return issueCredentialMessage
   196  }
   197  
   198  func getVCFromValidIssueCredentialMessage(t *testing.T) verifiable.Credential {
   199  	issueCredentialMessage := makeValidIssueCredentialMessage(t)
   200  
   201  	jsonAttachmentAsMap, ok := issueCredentialMessage.Attachments[0].Data.JSON.(map[string]interface{})
   202  	require.True(t, ok)
   203  
   204  	verifiableCredentialsRaw := jsonAttachmentAsMap["verifiableCredential"]
   205  
   206  	verifiableCredentialsAsArrayOfInterface, ok := verifiableCredentialsRaw.([]interface{})
   207  	require.True(t, ok)
   208  
   209  	vcBytes, err := json.Marshal(verifiableCredentialsAsArrayOfInterface[0])
   210  	require.NoError(t, err)
   211  
   212  	vc, err := verifiable.ParseCredential(vcBytes, verifiable.WithJSONLDDocumentLoader(createTestDocumentLoader(t)))
   213  	require.NoError(t, err)
   214  
   215  	return *vc
   216  }
   217  
   218  func makeCredentialResponseWithMissingID(t *testing.T) []byte {
   219  	credentialResponse := makeValidCredentialResponse(t)
   220  
   221  	credentialResponse.ID = ""
   222  
   223  	credentialResponseBytes, err := json.Marshal(credentialResponse)
   224  	require.NoError(t, err)
   225  
   226  	return credentialResponseBytes
   227  }
   228  
   229  func makeCredentialResponseWithMissingManifestID(t *testing.T) []byte {
   230  	credentialResponse := makeValidCredentialResponse(t)
   231  
   232  	credentialResponse.ManifestID = ""
   233  
   234  	credentialResponseBytes, err := json.Marshal(credentialResponse)
   235  	require.NoError(t, err)
   236  
   237  	return credentialResponseBytes
   238  }
   239  
   240  func makeCredentialResponseWithInvalidJSONPath(t *testing.T) cm.CredentialResponse {
   241  	credentialResponse := makeValidCredentialResponse(t)
   242  
   243  	credentialResponse.OutputDescriptorMappingObjects[0].Path = invalidJSONPath
   244  
   245  	return credentialResponse
   246  }
   247  
   248  func makeIssueCredentialMessageWithInvalidVC(t *testing.T) issuecredential.IssueCredentialV3 {
   249  	var issueCredentialMessage issuecredential.IssueCredentialV3
   250  
   251  	err := json.Unmarshal(validIssueCredentialMessage, &issueCredentialMessage)
   252  	require.NoError(t, err)
   253  
   254  	jsonAttachmentAsMap, ok := issueCredentialMessage.Attachments[0].Data.JSON.(map[string]interface{})
   255  	require.True(t, ok)
   256  
   257  	jsonAttachmentAsMap["verifiableCredential"] = []interface{}{"NotAValidVC"}
   258  
   259  	return issueCredentialMessage
   260  }