github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/verifiable/credential_extension_switch_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package verifiable 7 8 import ( 9 _ "embed" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "testing" 14 15 "github.com/stretchr/testify/require" 16 17 "github.com/hyperledger/aries-framework-go/pkg/doc/ldcontext" 18 ) 19 20 const ( 21 validCred1 = ` 22 { 23 "@context": [ 24 "https://www.w3.org/2018/credentials/v1", 25 "%s" 26 ], 27 "id": "http://example.edu/credentials/1872", 28 "type": [ 29 "VerifiableCredential", 30 "CredType1" 31 ], 32 "credentialSubject": { 33 "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 34 "s1": "custom subject 1" 35 }, 36 37 "c1": "custom field 1", 38 39 "issuer": { 40 "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 41 "name": "Example University" 42 }, 43 44 "issuanceDate": "2010-01-01T19:23:24Z" 45 } 46 ` 47 48 validCred2 = ` 49 { 50 "@context": [ 51 "https://www.w3.org/2018/credentials/v1", 52 "%s" 53 ], 54 "id": "http://example.edu/credentials/1872", 55 "type": [ 56 "VerifiableCredential", 57 "CredType2" 58 ], 59 "credentialSubject": { 60 "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 61 "s2": "custom subject 2" 62 }, 63 64 "c2": "custom field 2", 65 66 "issuer": { 67 "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 68 "name": "Example University" 69 }, 70 71 "issuanceDate": "2010-01-01T19:23:24Z" 72 }` 73 74 credMissingMandatoryFields = ` 75 { 76 "@context": [ 77 "https://www.w3.org/2018/credentials/v1", 78 "https://www.w3.org/2018/credentials/examples/ext/type2" 79 ], 80 "id": "http://example.edu/credentials/1872", 81 "type": [ 82 "VerifiableCredential" 83 ] 84 }` 85 ) 86 87 //nolint:gochecknoglobals 88 var ( 89 //go:embed testdata/context/context1.jsonld 90 context1 []byte 91 //go:embed testdata/context/context2.jsonld 92 context2 []byte 93 ) 94 95 // Cred1 can produce itself. 96 type Cred1 struct { 97 Base Credential 98 Subject struct { 99 ID string `json:"id,omitempty"` 100 CustomSubjectField string `json:"s1,omitempty"` 101 } `json:"credentialSubject,omitempty"` 102 CustomField string `json:"c1,omitempty"` 103 } 104 105 func (c1 *Cred1) Accept(vc *Credential) bool { 106 return hasType(vc.Types, "CredType1") 107 } 108 109 func (c1 *Cred1) Apply(vc *Credential, dataJSON []byte) (interface{}, error) { 110 err := json.Unmarshal(dataJSON, c1) 111 if err != nil { 112 return nil, err 113 } 114 115 c1.Base = *vc 116 117 return c1, nil 118 } 119 120 // There is a separate producer for Cred2. 121 type Cred2 struct { 122 Base Credential 123 Subject struct { 124 ID string `json:"id,omitempty"` 125 CustomSubjectField string `json:"s2,omitempty"` 126 } `json:"credentialSubject,omitempty"` 127 CustomField string `json:"c2,omitempty"` 128 } 129 130 type Cred2Producer struct{} 131 132 func (c2p *Cred2Producer) Accept(vc *Credential) bool { 133 return hasType(vc.Types, "CredType2") 134 } 135 136 func (c2p *Cred2Producer) Apply(vc *Credential, dataJSON []byte) (interface{}, error) { 137 c2 := Cred2{} 138 139 err := json.Unmarshal(dataJSON, &c2) 140 if err != nil { 141 return nil, err 142 } 143 144 c2.Base = *vc 145 146 return &c2, nil 147 } 148 149 func NewCred1Producer() CustomCredentialProducer { 150 return &Cred1{} 151 } 152 153 func NewCred2Producer() CustomCredentialProducer { 154 return &Cred2Producer{} 155 } 156 157 type FailingCredentialProducer struct{} 158 159 func (fp *FailingCredentialProducer) Accept(*Credential) bool { 160 return true 161 } 162 163 func (fp *FailingCredentialProducer) Apply(*Credential, []byte) (interface{}, error) { 164 return nil, errors.New("failed to apply credential extension") 165 } 166 167 func hasType(allTypes []string, targetType string) bool { 168 for _, thatType := range allTypes { 169 if thatType == targetType { 170 return true 171 } 172 } 173 174 return false 175 } 176 177 func TestCredentialExtensibilitySwitch(t *testing.T) { 178 producers := []CustomCredentialProducer{NewCred1Producer(), NewCred2Producer()} 179 180 contextURL := "http://127.0.0.1" 181 182 // Producer1 applied. 183 i1, err := createTestCustomCredential(t, []byte(fmt.Sprintf(validCred1, contextURL+"?context=1")), producers, 184 ldcontext.Document{ 185 URL: "http://127.0.0.1?context=1", 186 Content: context1, 187 }) 188 require.NoError(t, err) 189 require.IsType(t, &Cred1{}, i1) 190 cred1, correct := i1.(*Cred1) 191 require.True(t, correct) 192 require.NotNil(t, cred1.Base) 193 require.Equal(t, []string{"VerifiableCredential", "CredType1"}, cred1.Base.Types) 194 require.Equal(t, "custom field 1", cred1.CustomField) 195 require.Equal(t, "custom subject 1", cred1.Subject.CustomSubjectField) 196 197 // Producer2 applied. 198 i2, err := createTestCustomCredential(t, []byte(fmt.Sprintf(validCred2, contextURL+"?context=2")), producers, 199 ldcontext.Document{ 200 URL: "http://127.0.0.1?context=2", 201 Content: context2, 202 }) 203 require.NoError(t, err) 204 require.IsType(t, &Cred2{}, i2) 205 cred2, correct := i2.(*Cred2) 206 require.True(t, correct) 207 require.NotNil(t, cred2.Base) 208 require.Equal(t, []string{"VerifiableCredential", "CredType2"}, cred2.Base.Types) 209 require.Equal(t, "custom field 2", cred2.CustomField) 210 require.Equal(t, "custom subject 2", cred2.Subject.CustomSubjectField) 211 212 // No producers are applied, returned base credential. 213 i3, err := createTestCustomCredential(t, []byte(validCredential), producers) 214 require.NoError(t, err) 215 require.IsType(t, &Credential{}, i3) 216 217 // Invalid credential. 218 i4, err := createTestCustomCredential(t, []byte(credMissingMandatoryFields), producers) 219 require.Error(t, err) 220 require.Contains(t, err.Error(), "build base verifiable credential") 221 require.Nil(t, i4) 222 223 // Failing ext producer. 224 i5, err := createTestCustomCredential(t, []byte(validCredential), 225 []CustomCredentialProducer{&FailingCredentialProducer{}}) 226 require.Error(t, err) 227 require.Contains(t, err.Error(), "failed to apply credential extension") 228 require.Nil(t, i5) 229 } 230 231 func createTestCustomCredential(t *testing.T, vcData []byte, producers []CustomCredentialProducer, 232 extraContexts ...ldcontext.Document) (interface{}, error) { 233 t.Helper() 234 235 return CreateCustomCredential(vcData, producers, 236 WithJSONLDDocumentLoader(createTestDocumentLoader(t, extraContexts...))) 237 }