github.com/hyperledger/aries-framework-go@v0.3.2/pkg/wallet/query_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package wallet
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/google/uuid"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/hyperledger/aries-framework-go/internal/testdata"
    21  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    22  	"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
    23  	"github.com/hyperledger/aries-framework-go/pkg/doc/util"
    24  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    25  	vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    26  	"github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil"
    27  	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
    28  	"github.com/hyperledger/aries-framework-go/pkg/vdr/key"
    29  )
    30  
    31  // nolint: lll
    32  const (
    33  	sampleVCFmt = `{
    34        "@context": [
    35          "https://www.w3.org/2018/credentials/v1",
    36          "https://www.w3.org/2018/credentials/examples/v1",
    37  		"https://w3id.org/security/bbs/v1"
    38        ],
    39       "credentialSchema": [{"id": "%s", "type": "JsonSchemaValidator2018"}],
    40        "credentialSubject": {
    41          "degree": {
    42            "type": "BachelorDegree",
    43            "university": "MIT"
    44          },
    45          "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
    46          "name": "Jayden Doe",
    47          "spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1"
    48        },
    49        "expirationDate": "2020-01-01T19:23:24Z",
    50        "id": "http://example.edu/credentials/1872",
    51        "issuanceDate": "2010-01-01T19:23:24Z",
    52        "issuer": {
    53          "id": "did:example:76e12ec712ebc6f1c221ebfeb1f",
    54          "name": "Example University"
    55        },
    56        "referenceNumber": 83294847,
    57        "type": [
    58          "VerifiableCredential",
    59          "UniversityDegreeCredential"
    60        ]
    61      }`
    62  	samplePRCVC = `{
    63  	 	"@context": [
    64  	   		"https://www.w3.org/2018/credentials/v1",
    65  	   		"https://w3id.org/citizenship/v1",
    66  	   		"https://w3id.org/security/bbs/v1"
    67  	 	],
    68  	 	"id": "https://issuer.oidp.uscis.gov/credentials/83627465",
    69  	 	"type": [
    70  	   		"VerifiableCredential",
    71  	   		"PermanentResidentCard"
    72  	 	],
    73  	 	"issuer": "did:example:489398593",
    74  	 	"identifier": "83627465",
    75  	 	"name": "Permanent Resident Card",
    76  	 	"description": "Government of Example Permanent Resident Card.",
    77  	 	"issuanceDate": "2019-12-03T12:19:52Z",
    78  	 	"expirationDate": "2029-12-03T12:19:52Z",
    79  		"credentialSchema": [],
    80  	 	"credentialSubject": {
    81  	   		"id": "did:example:b34ca6cd37bbf23",
    82  	   		"type": [
    83  	     		"PermanentResident",
    84  	     		"Person"
    85  	   		],
    86  	   		"givenName": "JOHN",
    87  	   		"familyName": "SMITH",
    88  	   		"gender": "Male",
    89  	   		"image": "data:image/png;base64,iVBORw0KGgokJggg==",
    90  	   		"residentSince": "2015-01-01",
    91  	   		"lprCategory": "C09",
    92  	   		"lprNumber": "999-999-999",
    93  	   		"commuterClassification": "C1",
    94  	   		"birthCountry": "Bahamas",
    95  	   		"birthDate": "1958-07-17"
    96  	 	}
    97  	}`
    98  	sampleBBSVC = `{
    99              "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1", "https://w3id.org/security/bbs/v1"],
   100              "credentialSubject": {
   101                  "degree": {"type": "BachelorDegree", "university": "MIT"},
   102                  "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
   103                  "name": "Jayden Doe",
   104                  "spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1"
   105              },
   106              "expirationDate": "2020-01-01T19:23:24Z",
   107              "id": "http://example.edu/credentials/1872",
   108              "issuanceDate": "2010-01-01T19:23:24Z",
   109              "issuer": {"id": "did:example:76e12ec712ebc6f1c221ebfeb1f", "name": "Example University"},
   110              "proof": {
   111                  "created": "2021-03-29T13:27:36.483097-04:00",
   112                  "proofPurpose": "assertionMethod",
   113                  "proofValue": "rw7FeV6K1wimnYogF9qd-N0zmq5QlaIoszg64HciTca-mK_WU4E1jIusKTT6EnN2GZz04NVPBIw4yhc0kTwIZ07etMvfWUlHt_KMoy2CfTw8FBhrf66q4h7Qcqxh_Kxp6yCHyB4A-MmURlKKb8o-4w",
   114                  "type": "BbsBlsSignature2020",
   115                  "verificationMethod": "did:key:zUC72c7u4BYVmfYinDceXkNAwzPEyuEE23kUmJDjLy8495KH3pjLwFhae1Fww9qxxRdLnS2VNNwni6W3KbYZKsicDtiNNEp76fYWR6HCD8jAz6ihwmLRjcHH6kB294Xfg1SL1qQ#zUC72c7u4BYVmfYinDceXkNAwzPEyuEE23kUmJDjLy8495KH3pjLwFhae1Fww9qxxRdLnS2VNNwni6W3KbYZKsicDtiNNEp76fYWR6HCD8jAz6ihwmLRjcHH6kB294Xfg1SL1qQ"
   116              },
   117              "referenceNumber": 83294847,
   118              "type": ["VerifiableCredential", "UniversityDegreeCredential"]
   119          }`
   120  	sampleQueryByExFmt = `{
   121                          "reason": "Please present your identity document.",
   122                          "example": {
   123                              "@context": [
   124  								"https://www.w3.org/2018/credentials/v1",
   125  								"https://www.w3.org/2018/credentials/examples/v1",
   126  								"https://w3id.org/security/bbs/v1"
   127                              ],
   128                              "type": ["UniversityDegreeCredential"],
   129  							"trustedIssuer": [
   130                					{
   131                  					"issuer": "urn:some:required:issuer"
   132                					},
   133  								{
   134                  					"required": true,
   135                  					"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f"
   136                					}
   137  							],
   138  							"credentialSubject": {
   139  								"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"	
   140  							},
   141  							"credentialSchema": {
   142  								"id": "%s",
   143  								"type": "JsonSchemaValidator2018"
   144  							}
   145                          }
   146                  	}`
   147  	sampleQueryByFrame = `{
   148                      "reason": "Please provide your Passport details.",
   149                      "frame": {
   150                          "@context": [
   151                              "https://www.w3.org/2018/credentials/v1",
   152                              "https://w3id.org/citizenship/v1",
   153                              "https://w3id.org/security/bbs/v1"
   154                          ],
   155                          "type": ["VerifiableCredential", "PermanentResidentCard"],
   156                          "@explicit": true,
   157                          "identifier": {},
   158                          "issuer": {},
   159                          "issuanceDate": {},
   160                          "credentialSubject": {
   161                              "@explicit": true,
   162                              "name": {},
   163                              "spouse": {}
   164                          }
   165                      },
   166                      "trustedIssuer": [
   167                          {
   168                              "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f",
   169                              "required": true
   170                          }
   171                      ],
   172                      "required": true
   173                  }`
   174  )
   175  
   176  func TestGetQueryType(t *testing.T) {
   177  	t.Run("test get query type by string", func(t *testing.T) {
   178  		tests := []struct {
   179  			name         string
   180  			typeStr      []string
   181  			expected     QueryType
   182  			expectedName string
   183  			error        string
   184  		}{
   185  			{
   186  				name:         "test for QueryByExample",
   187  				typeStr:      []string{"QueryByExample", "QuerybyExample", "querybyexample"},
   188  				expected:     QueryByExample,
   189  				expectedName: "QueryByExample",
   190  			},
   191  			{
   192  				name:         "test for QueryByFrame",
   193  				typeStr:      []string{"QueryByFrame", "Querybyframe", "querybyframe"},
   194  				expected:     QueryByFrame,
   195  				expectedName: "QueryByFrame",
   196  			},
   197  			{
   198  				name:         "test for PresentationExchange",
   199  				typeStr:      []string{"PresentationExchange", "Presentationexchange", "presentationExchange"},
   200  				expected:     PresentationExchange,
   201  				expectedName: "PresentationExchange",
   202  			},
   203  			{
   204  				name:         "test for DIDAuth",
   205  				typeStr:      []string{"didAuth", "didauth", "DIDAuth", "DIDauth"},
   206  				expected:     DIDAuth,
   207  				expectedName: "DIDAuth",
   208  			},
   209  			{
   210  				name:         "test for invalid types",
   211  				typeStr:      []string{"", "QueryByFram", "QueryByExamples", "DIDAuthorization", "invalid"},
   212  				error:        "unsupported query type",
   213  				expectedName: "",
   214  			},
   215  		}
   216  
   217  		t.Parallel()
   218  
   219  		for _, test := range tests {
   220  			tc := test
   221  			t.Run(tc.name, func(t *testing.T) {
   222  				for _, str := range tc.typeStr {
   223  					qType, err := GetQueryType(str)
   224  					require.Equal(t, qType, tc.expected)
   225  					if tc.error != "" {
   226  						require.Error(t, err)
   227  						require.Contains(t, err.Error(), tc.error)
   228  					} else {
   229  						require.NoError(t, err)
   230  					}
   231  				}
   232  			})
   233  		}
   234  	})
   235  }
   236  
   237  func TestQuery_PerformQuery(t *testing.T) {
   238  	vc1, err := (&verifiable.Credential{
   239  		Context: []string{verifiable.ContextURI},
   240  		Types:   []string{verifiable.VCType},
   241  		ID:      "http://example.edu/credentials/9999",
   242  		CustomFields: map[string]interface{}{
   243  			"first_name": "Jesse",
   244  		},
   245  		Issued: &util.TimeWrapper{
   246  			Time: time.Now(),
   247  		},
   248  		Issuer: verifiable.Issuer{
   249  			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
   250  		},
   251  		Subject: uuid.New().String(),
   252  	}).MarshalJSON()
   253  	require.NoError(t, err)
   254  
   255  	// presentation exchange query
   256  	pd := &presexch.PresentationDefinition{
   257  		ID: uuid.New().String(),
   258  		InputDescriptors: []*presexch.InputDescriptor{{
   259  			ID: uuid.New().String(),
   260  			Schema: []*presexch.Schema{{
   261  				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
   262  			}},
   263  			Constraints: &presexch.Constraints{
   264  				Fields: []*presexch.Field{{
   265  					Path: []string{"$.first_name"},
   266  				}},
   267  			},
   268  		}},
   269  	}
   270  
   271  	// query by example
   272  	queryByExample := []byte(fmt.Sprintf(sampleQueryByExFmt, verifiable.ContextURI))
   273  	// query by frame
   274  	queryByFrame := []byte(sampleQueryByFrame)
   275  
   276  	pdJSON, err := json.Marshal(pd)
   277  	require.NoError(t, err)
   278  	require.NotEmpty(t, pdJSON)
   279  
   280  	udcVC := testdata.SampleUDCVC
   281  	vcForQuery := []byte(fmt.Sprintf(sampleVCFmt, verifiable.ContextURI))
   282  	vcForDerive := []byte(sampleBBSVC)
   283  
   284  	customVDR := &mockvdr.MockVDRegistry{
   285  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   286  			if strings.HasPrefix(didID, "did:key:") {
   287  				k := key.New()
   288  
   289  				d, e := k.Read(didID)
   290  				if e != nil {
   291  					return nil, e
   292  				}
   293  
   294  				return d, nil
   295  			}
   296  
   297  			return nil, fmt.Errorf("did not found")
   298  		},
   299  	}
   300  	pubKeyFetcher := verifiable.NewVDRKeyResolver(customVDR).PublicKeyFetcher()
   301  
   302  	loader, err := ldtestutil.DocumentLoader()
   303  	require.NoError(t, err)
   304  
   305  	t.Run("test wallet queries", func(t *testing.T) {
   306  		tests := []struct {
   307  			name        string
   308  			query       []*QueryParams
   309  			credentials []json.RawMessage
   310  			resultCount int
   311  			vcCount     map[int]int
   312  			error       string
   313  		}{
   314  			// Presentation Exchange tests
   315  			{
   316  				name: "query by presentation exchange - success",
   317  				query: []*QueryParams{
   318  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   319  				},
   320  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   321  				resultCount: 1,
   322  				vcCount:     map[int]int{0: 1},
   323  			},
   324  			{
   325  				name: "query by presentation exchange - multi query frame - success",
   326  				query: []*QueryParams{
   327  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON, pdJSON}},
   328  				},
   329  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   330  				resultCount: 2,
   331  				vcCount:     map[int]int{0: 1, 1: 1},
   332  			},
   333  			{
   334  				name: "query by presentation exchange - multiple - success",
   335  				query: []*QueryParams{
   336  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON, pdJSON}},
   337  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   338  				},
   339  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   340  				resultCount: 3,
   341  				vcCount:     map[int]int{0: 1, 1: 1, 2: 1},
   342  			},
   343  			{
   344  				name: "query by presentation exchange - no results",
   345  				query: []*QueryParams{
   346  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   347  				},
   348  				credentials: []json.RawMessage{udcVC, vcForQuery, vcForDerive},
   349  				resultCount: 0,
   350  				error:       ErrQueryNoResultFound.Error(),
   351  			},
   352  			{
   353  				name: "query by presentation exchange - invalid definition",
   354  				query: []*QueryParams{
   355  					{Type: "PresentationExchange", Query: []json.RawMessage{[]byte(sampleInvalidDIDContent)}},
   356  				},
   357  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   358  				resultCount: 0,
   359  				error:       "input_descriptors is required",
   360  			},
   361  			{
   362  				name: "query by presentation exchange - invalid query frame",
   363  				query: []*QueryParams{
   364  					{Type: "PresentationExchange", Query: []json.RawMessage{[]byte("---")}},
   365  				},
   366  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   367  				resultCount: 0,
   368  				error:       "invalid character",
   369  			},
   370  			// QueryByExample tests
   371  			{
   372  				name: "query by example - success",
   373  				query: []*QueryParams{
   374  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   375  				},
   376  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   377  				resultCount: 1,
   378  				vcCount:     map[int]int{0: 1},
   379  			},
   380  			{
   381  				name: "query by example - success & normalize results",
   382  				query: []*QueryParams{
   383  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample, queryByExample}},
   384  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   385  				},
   386  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   387  				resultCount: 1,
   388  				vcCount:     map[int]int{0: 1},
   389  			},
   390  			{
   391  				name: "query by example - invalid query",
   392  				query: []*QueryParams{
   393  					{Type: "QueryByExample", Query: []json.RawMessage{[]byte(sampleInvalidDIDContent)}},
   394  				},
   395  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   396  				resultCount: 0,
   397  				error:       "failed to parse QueryByExample query",
   398  			},
   399  			// QueryByFrame tests
   400  			{
   401  				name: "query by frame - success",
   402  				query: []*QueryParams{
   403  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame}},
   404  				},
   405  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   406  				resultCount: 1,
   407  				vcCount:     map[int]int{0: 1},
   408  			},
   409  			{
   410  				name: "query by frame - multiple results",
   411  				query: []*QueryParams{
   412  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame, queryByFrame}},
   413  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame}},
   414  				},
   415  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   416  				resultCount: 1,
   417  				vcCount:     map[int]int{0: 3},
   418  			},
   419  			{
   420  				name: "query by frame - invalid query",
   421  				query: []*QueryParams{
   422  					{Type: "QueryByFrame", Query: []json.RawMessage{[]byte(sampleInvalidDIDContent)}},
   423  				},
   424  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   425  				resultCount: 0,
   426  				error:       "failed to parse QueryByFrame query",
   427  			},
   428  			// DIDAuth tests
   429  			{
   430  				name: "didAuth - success",
   431  				query: []*QueryParams{
   432  					{Type: "DIDAuth"},
   433  				},
   434  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   435  				resultCount: 1,
   436  				vcCount:     map[int]int{0: 0},
   437  			},
   438  
   439  			// Mixed Query Types
   440  			{
   441  				name: "query by PresentationExchange,QueryByExample,QueryByFrame - success",
   442  				query: []*QueryParams{
   443  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   444  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   445  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame}},
   446  					{Type: "DIDAuth"},
   447  				},
   448  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   449  				resultCount: 3,
   450  				vcCount:     map[int]int{0: 1, 1: 0, 2: 2},
   451  			},
   452  			{
   453  				name: "query by PresentationExchange,QueryByExample,QueryByFrame - normalized result - success",
   454  				query: []*QueryParams{
   455  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   456  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   457  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   458  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   459  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame}},
   460  				},
   461  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   462  				resultCount: 2,
   463  				vcCount:     map[int]int{0: 1, 1: 2},
   464  			},
   465  
   466  			// Validations
   467  			{
   468  				name: "unsupported query type",
   469  				query: []*QueryParams{
   470  					{Type: "QueryByInvalid", Query: []json.RawMessage{queryByExample}},
   471  				},
   472  				credentials: []json.RawMessage{vc1, udcVC, vcForQuery, vcForDerive},
   473  				resultCount: 0,
   474  				error:       "unsupported query type",
   475  			},
   476  			{
   477  				name: "empty credentials",
   478  				query: []*QueryParams{
   479  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   480  				},
   481  				credentials: []json.RawMessage{},
   482  				resultCount: 0,
   483  				error:       ErrQueryNoResultFound.Error(),
   484  			},
   485  			{
   486  				name: "credential parsing error",
   487  				query: []*QueryParams{
   488  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   489  				},
   490  				credentials: []json.RawMessage{[]byte("----")},
   491  				resultCount: 0,
   492  				error:       "unmarshal new credential",
   493  			},
   494  		}
   495  
   496  		t.Parallel()
   497  
   498  		for _, test := range tests {
   499  			tc := test
   500  			t.Run(tc.name, func(t *testing.T) {
   501  				credentials := make(map[string]json.RawMessage)
   502  				for i, v := range tc.credentials {
   503  					credentials[strconv.Itoa(i)] = v
   504  				}
   505  
   506  				results, err := NewQuery(pubKeyFetcher, loader, tc.query...).PerformQuery(credentials)
   507  
   508  				if tc.error != "" {
   509  					require.Empty(t, results)
   510  					require.Error(t, err)
   511  					require.Contains(t, err.Error(), tc.error)
   512  					require.Len(t, results, tc.resultCount)
   513  
   514  					return
   515  				}
   516  
   517  				require.NoError(t, err)
   518  				require.Len(t, results, tc.resultCount)
   519  
   520  				for i, result := range results {
   521  					require.Len(t, result.Credentials(), tc.vcCount[i])
   522  				}
   523  			})
   524  		}
   525  	})
   526  }
   527  
   528  func TestQueryByExample(t *testing.T) {
   529  	loader, err := ldtestutil.DocumentLoader()
   530  	require.NoError(t, err)
   531  
   532  	vc1, err := verifiable.ParseCredential([]byte(fmt.Sprintf(sampleVCFmt, verifiable.ContextURI)),
   533  		verifiable.WithDisabledProofCheck(),
   534  		verifiable.WithJSONLDDocumentLoader(loader))
   535  	require.NoError(t, err)
   536  
   537  	vc2, err := verifiable.ParseCredential([]byte(samplePRCVC), verifiable.WithDisabledProofCheck(),
   538  		verifiable.WithJSONLDDocumentLoader(loader))
   539  	require.NoError(t, err)
   540  
   541  	// sample queries
   542  	queryByExampleAll := []byte(fmt.Sprintf(sampleQueryByExFmt, verifiable.ContextURI))
   543  
   544  	queryByExampleContext1 := `{
   545                          "reason": "Please present your identity document.",
   546                          "example": {
   547                              "@context": [
   548  								"https://www.w3.org/2018/credentials/v1"
   549                              ],
   550                              "type": "VerifiableCredential",
   551  							"trustedIssuer": [],
   552  							"credentialSubject": {},
   553  							"credentialSchema": {}
   554                          }
   555                  	}`
   556  
   557  	queryByExampleContext2 := `{
   558                          "reason": "Please present your identity document.",
   559                          "example": {
   560                              "@context": [
   561  								"https://www.w3.org/2018/credentials/v1",
   562  								"https://w3id.org/citizenship/v1"
   563                              ],
   564  							"type": "VerifiableCredential"
   565                          }
   566                  	}`
   567  
   568  	queryByExampleContextType1 := `{
   569                          "reason": "Please present your identity document.",
   570                          "example": {
   571                              "@context": [
   572  								"https://www.w3.org/2018/credentials/v1",
   573  								"https://w3id.org/citizenship/v1"
   574                              ],
   575                              "type": "PermanentResidentCard",
   576  							"trustedIssuer": [],
   577  							"credentialSubject": {},
   578  							"credentialSchema": {}
   579                          }
   580                  	}`
   581  
   582  	queryByExampleContextType2 := `{
   583                          "reason": "Please present your identity document.",
   584                          "example": {
   585                              "@context": [
   586  								"https://www.w3.org/2018/credentials/v1",
   587  								"https://www.w3.org/2018/credentials/examples/v1"
   588                              ],
   589                              "type": ["UniversityDegreeCredential"],
   590  							"trustedIssuer": [],
   591  							"credentialSubject": {},
   592  							"credentialSchema": {}
   593                          }
   594                  	}`
   595  
   596  	queryByExampleContextTypeIssuer1 := `{
   597                          "reason": "Please present your identity document.",
   598                          "example": {
   599                              "@context": [
   600  								"https://www.w3.org/2018/credentials/v1",
   601  								"https://www.w3.org/2018/credentials/examples/v1"
   602                              ],
   603                              "type": ["UniversityDegreeCredential"],
   604  							"trustedIssuer": [
   605                					{
   606                  					"issuer": "urn:some:required:issuer"
   607                					},
   608  								{
   609                  					"required": true,
   610                  					"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f"
   611                					}
   612  							]
   613                          }
   614                  	}`
   615  
   616  	queryByExampleContextTypeIssuer2 := `{
   617                          "reason": "Please present your identity document.",
   618                          "example": {
   619                              "@context": [
   620  								"https://www.w3.org/2018/credentials/v1"
   621                              ],
   622                              "type": ["PermanentResidentCard"],
   623  							"trustedIssuer": [
   624  								{
   625                  					"required": true,
   626                  					"issuer": "did:example:489398593"
   627                					}
   628  							]
   629                          }
   630                  	}`
   631  
   632  	queryByExampleContextTypeIssuer3 := `{
   633                          "reason": "Please present your identity document.",
   634                          "example": {
   635                              "@context": [
   636  								"https://www.w3.org/2018/credentials/v1"
   637                              ],
   638                              "type": ["PermanentResidentCard"],
   639  							"trustedIssuer": [
   640  								{
   641                  					"issuer": "did:example:489398593"
   642                					},
   643  								{
   644                  					"required": true,
   645                  					"issuer": "did:example:1234"
   646                					}
   647  							]
   648                          }
   649                  	}`
   650  
   651  	queryByExampleContextTypeIssuer4 := `{
   652                          "reason": "Please present your identity document.",
   653                          "example": {
   654                              "@context": [
   655  								"https://www.w3.org/2018/credentials/v1"
   656                              ],
   657                              "type": ["PermanentResidentCard"],
   658  							"trustedIssuer": [
   659  								{
   660                  					"issuer": "did:example:7777"
   661                					},
   662  								{
   663                  					"issuer": "did:example:1234"
   664                					}
   665  							]
   666                          }
   667                  	}`
   668  
   669  	queryByExampleContextTypeCredSubject1 := `{
   670                          "reason": "Please present your identity document.",
   671                          "example": {
   672                              "@context": [
   673  								"https://www.w3.org/2018/credentials/v1"
   674                              ],
   675                              "type": ["PermanentResidentCard"],
   676  							"trustedIssuer": [
   677  								{
   678                  					"required": true,
   679                  					"issuer": "did:example:489398593"
   680                					}
   681  							],
   682  							"credentialSubject": {
   683  								"id": "did:example:b34ca6cd37bbf23"	
   684  							},
   685  							"credentialSchema": {}
   686                          }
   687                  	}`
   688  
   689  	queryByExampleContextTypeCredSubject2 := `{
   690                          "reason": "Please present your identity document.",
   691                          "example": {
   692                              "@context": [
   693  								"https://www.w3.org/2018/credentials/v1"
   694                              ],
   695                              "type": ["UniversityDegreeCredential"],
   696  							"trustedIssuer": [
   697  								{
   698                  					"required": true,
   699                  					"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f"
   700                					}
   701  							],
   702  							"credentialSubject": {
   703  								"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"	
   704  							},
   705  							"credentialSchema": {}
   706                          }
   707                  	}`
   708  
   709  	queryByExampleContextTypeCredSubject3 := `{
   710                          "reason": "Please present your identity document.",
   711                          "example": {
   712                              "@context": [
   713  								"https://www.w3.org/2018/credentials/v1"
   714                              ],
   715                              "type": ["UniversityDegreeCredential"],
   716  							"trustedIssuer": [
   717  								{
   718                  					"required": true,
   719                  					"issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f"
   720                					}
   721  							],
   722  							"credentialSubject": {
   723  								"id": "did:example:ebfeb1f712ebc6f1c276e12ec22"	
   724  							},
   725  							"credentialSchema": {}
   726                          }
   727                  	}`
   728  
   729  	queryByExampleContextTypeCredSchema1 := fmt.Sprintf(`{
   730                          "reason": "Please present your identity document.",
   731                          "example": {
   732                              "@context": [
   733  								"https://www.w3.org/2018/credentials/v1"
   734                              ],
   735                              "type": "UniversityDegreeCredential",
   736  							"credentialSubject": {
   737  								"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"	
   738  							},
   739  							"credentialSchema": {
   740  								"id": "%s",
   741  								"type": "JsonSchemaValidator2018"
   742  							}
   743                          }
   744                  	}`, verifiable.ContextURI)
   745  
   746  	queryByExampleContextTypeCredSchema2 := fmt.Sprintf(`{
   747                          "reason": "Please present your identity document.",
   748                          "example": {
   749                              "@context": [
   750  								"https://www.w3.org/2018/credentials/v1"
   751                              ],
   752                              "type": "UniversityDegreeCredential",
   753  							"credentialSubject": {
   754  								"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"	
   755  							},
   756  							"credentialSchema": {
   757  								"id": "%s",
   758  								"type": "JsonSchemaValidator2020"
   759  							}
   760                          }
   761                  	}`, verifiable.ContextURI)
   762  
   763  	queryByExampleInvalid1 := `{
   764                          "reason": "Please present your identity document.",
   765                          "example": {
   766  							"trustedIssuer": [
   767  								{
   768                  					"required": true,
   769                  					"issuer": "did:example:489398593"
   770                					}
   771  							],
   772  							"credentialSubject": {
   773  								"id": "did:example:ebfeb1f712ebc6f1c276e12ec21"	
   774  							},
   775  							"credentialSchema": {}
   776                          }
   777                  	}`
   778  
   779  	queryByExampleInvalid2 := `{
   780                          "reason": "Please present your identity document.",
   781                          "example": {
   782  							"@context": [
   783  								"https://www.w3.org/2018/credentials/v1"
   784                              ],
   785                              "type": []
   786                          }
   787                  	}`
   788  
   789  	queryByExampleInvalid3 := `{
   790                          "reason": "Please present your identity document.",
   791                          "example": {
   792  							"credentialSubject": "invalid",
   793  							"credentialSchema": true
   794                          }
   795                  	}`
   796  
   797  	vc3 := &verifiable.Credential{
   798  		Context: []string{verifiable.ContextURI},
   799  		Types:   []string{verifiable.VCType},
   800  		ID:      "http://example.edu/credentials/9999",
   801  		CustomFields: map[string]interface{}{
   802  			"first_name": "Jesse",
   803  		},
   804  		Issued: &util.TimeWrapper{
   805  			Time: time.Now(),
   806  		},
   807  		Issuer: verifiable.Issuer{
   808  			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
   809  		},
   810  		Subject: uuid.New().String(),
   811  	}
   812  
   813  	t.Run("test query", func(t *testing.T) {
   814  		tests := []struct {
   815  			name        string
   816  			credentials []*verifiable.Credential
   817  			example     []json.RawMessage
   818  			resultCount int
   819  			error       string
   820  			skip        bool
   821  		}{
   822  			{
   823  				name:        "QueryByExample all criteria matched - success",
   824  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   825  				example:     []json.RawMessage{queryByExampleAll},
   826  				resultCount: 1,
   827  			},
   828  			{
   829  				name:        "QueryByExample  multiple query frame - success",
   830  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   831  				example:     []json.RawMessage{queryByExampleAll, queryByExampleAll},
   832  				resultCount: 2,
   833  			},
   834  			{
   835  				name:        "QueryByExample context matching #1 - success",
   836  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   837  				example:     []json.RawMessage{[]byte(queryByExampleContext1)},
   838  				resultCount: 3,
   839  			},
   840  			{
   841  				name:        "QueryByExample context matching #2 - success",
   842  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   843  				example:     []json.RawMessage{[]byte(queryByExampleContext2)},
   844  				resultCount: 1,
   845  			},
   846  			{
   847  				name:        "QueryByExample context & type matching #1 - success",
   848  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   849  				example:     []json.RawMessage{[]byte(queryByExampleContextType1)},
   850  				resultCount: 1,
   851  			},
   852  			{
   853  				name:        "QueryByExample context & type matching #2 - success",
   854  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   855  				example:     []json.RawMessage{[]byte(queryByExampleContextType2)},
   856  				resultCount: 1,
   857  			},
   858  			{
   859  				name:        "QueryByExample context, type & issuer matching #1 - success",
   860  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   861  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeIssuer1)},
   862  				resultCount: 1,
   863  			},
   864  			{
   865  				name:        "QueryByExample context, type & issuer matching #2 - success",
   866  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   867  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeIssuer2)},
   868  				resultCount: 1,
   869  			},
   870  			{
   871  				name:        "QueryByExample context, type & issuer matching #3 - success",
   872  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   873  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeIssuer3)},
   874  				resultCount: 0,
   875  			},
   876  			{
   877  				name:        "QueryByExample context, type & issuer matching #4 - success",
   878  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   879  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeIssuer4)},
   880  				resultCount: 0,
   881  			},
   882  			{
   883  				name:        "QueryByExample context, type, issuer & subject matching #1 - success",
   884  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   885  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeCredSubject1)},
   886  				resultCount: 1,
   887  			},
   888  			{
   889  				name:        "QueryByExample context, type, issuer & subject matching #2 - success",
   890  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   891  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeCredSubject2)},
   892  				resultCount: 1,
   893  			},
   894  			{
   895  				name:        "QueryByExample context, type, issuer & subject matching #3 - success",
   896  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   897  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeCredSubject3)},
   898  				resultCount: 0,
   899  			},
   900  			{
   901  				name:        "QueryByExample context, type, issuer, schema matching #1 - success",
   902  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   903  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeCredSchema1)},
   904  				resultCount: 1,
   905  			},
   906  			{
   907  				name:        "QueryByExample context, type, issuer, schema matching #2- success",
   908  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   909  				example:     []json.RawMessage{[]byte(queryByExampleContextTypeCredSchema2)},
   910  				resultCount: 0,
   911  			},
   912  			{
   913  				name:        "QueryByExample invalid query #1",
   914  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   915  				example:     []json.RawMessage{[]byte(queryByExampleInvalid1)},
   916  				resultCount: 0,
   917  				error:       "failed to parse QueryByExample query",
   918  			},
   919  			{
   920  				name:        "QueryByExample invalid query #2",
   921  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   922  				example:     []json.RawMessage{[]byte(queryByExampleInvalid2)},
   923  				resultCount: 0,
   924  				error:       "failed to parse QueryByExample query",
   925  			},
   926  			{
   927  				name:        "QueryByExample invalid query #3",
   928  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
   929  				example:     []json.RawMessage{[]byte(queryByExampleInvalid3)},
   930  				resultCount: 0,
   931  				error:       "failed to parse QueryByExample query",
   932  			},
   933  		}
   934  
   935  		t.Parallel()
   936  
   937  		for _, test := range tests {
   938  			tc := test
   939  			t.Run(tc.name, func(t *testing.T) {
   940  				if tc.skip {
   941  					return
   942  				}
   943  
   944  				results, err := queryByExample(tc.credentials, tc.example...)
   945  
   946  				if tc.error != "" {
   947  					require.Empty(t, results)
   948  					require.Error(t, err)
   949  					require.Contains(t, err.Error(), tc.error)
   950  					require.Len(t, results, tc.resultCount)
   951  
   952  					return
   953  				}
   954  
   955  				require.NoError(t, err)
   956  				require.Len(t, results, tc.resultCount)
   957  			})
   958  		}
   959  	})
   960  }
   961  
   962  func TestQueryByFrame(t *testing.T) {
   963  	loader, err := ldtestutil.DocumentLoader()
   964  	require.NoError(t, err)
   965  
   966  	vc1, err := verifiable.ParseCredential([]byte(fmt.Sprintf(sampleVCFmt, verifiable.ContextURI)),
   967  		verifiable.WithDisabledProofCheck(),
   968  		verifiable.WithJSONLDDocumentLoader(loader))
   969  	require.NoError(t, err)
   970  
   971  	vc2, err := verifiable.ParseCredential([]byte(samplePRCVC), verifiable.WithDisabledProofCheck(),
   972  		verifiable.WithJSONLDDocumentLoader(loader))
   973  	require.NoError(t, err)
   974  
   975  	vc3, err := verifiable.ParseCredential([]byte(sampleBBSVC), verifiable.WithDisabledProofCheck(),
   976  		verifiable.WithJSONLDDocumentLoader(loader))
   977  	require.NoError(t, err)
   978  
   979  	tampered := strings.ReplaceAll(sampleBBSVC, `rw7FeV6K1wimnYogF9qd-N0zmq5QlaIoszg64HciTca`, ``)
   980  	vc4, err := verifiable.ParseCredential([]byte(tampered), verifiable.WithDisabledProofCheck(),
   981  		verifiable.WithJSONLDDocumentLoader(loader))
   982  	require.NoError(t, err)
   983  
   984  	queryByFrameExampleNoIssuer := `{
   985  	                "reason": "Please provide your Passport details.",
   986  	                "frame": {
   987  	                    "@context": [
   988  	                        "https://www.w3.org/2018/credentials/v1",
   989  	                        "https://w3id.org/citizenship/v1",
   990  	                        "https://w3id.org/security/bbs/v1"
   991  	                    ],
   992  	                    "type": ["VerifiableCredential", "PermanentResidentCard"],
   993  	                    "@explicit": true,
   994  	                    "identifier": {},
   995  	                    "issuer": {},
   996  	                    "issuanceDate": {},
   997  	                    "credentialSubject": {
   998  	                        "@explicit": true,
   999  	                        "name": {},
  1000  	                        "spouse": {}
  1001  	                    }
  1002  	                }
  1003  	            }`
  1004  
  1005  	queryByFrameExampleIssuerNotMatched := strings.ReplaceAll(sampleQueryByFrame,
  1006  		"did:example:76e12ec712ebc6f1c221ebfeb1f", "1234")
  1007  
  1008  	customVDR := &mockvdr.MockVDRegistry{
  1009  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
  1010  			if strings.HasPrefix(didID, "did:key:") {
  1011  				k := key.New()
  1012  
  1013  				d, e := k.Read(didID)
  1014  				if e != nil {
  1015  					return nil, e
  1016  				}
  1017  
  1018  				return d, nil
  1019  			}
  1020  
  1021  			return nil, fmt.Errorf("did not found")
  1022  		},
  1023  	}
  1024  	pubKeyFetcher := verifiable.NewVDRKeyResolver(customVDR).PublicKeyFetcher()
  1025  
  1026  	t.Run("test query", func(t *testing.T) {
  1027  		tests := []struct {
  1028  			name        string
  1029  			credentials []*verifiable.Credential
  1030  			example     []json.RawMessage
  1031  			resultCount int
  1032  			error       string
  1033  			skip        bool
  1034  		}{
  1035  			{
  1036  				name:        "QueryByFrame all criteria matched - success",
  1037  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
  1038  				example:     []json.RawMessage{[]byte(sampleQueryByFrame)},
  1039  				resultCount: 1,
  1040  			},
  1041  			{
  1042  				name:        "QueryByFrame multiple query - success",
  1043  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
  1044  				example: []json.RawMessage{
  1045  					[]byte(sampleQueryByFrame), []byte(sampleQueryByFrame),
  1046  					[]byte(sampleQueryByFrame),
  1047  				},
  1048  				resultCount: 3,
  1049  			},
  1050  			{
  1051  				name:        "QueryByFrame without issuer criteria - success",
  1052  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
  1053  				example:     []json.RawMessage{[]byte(queryByFrameExampleNoIssuer)},
  1054  				resultCount: 1,
  1055  			},
  1056  			{
  1057  				name:        "QueryByFrame issuer not matched - success",
  1058  				credentials: []*verifiable.Credential{vc1, vc2, vc3},
  1059  				example:     []json.RawMessage{[]byte(queryByFrameExampleIssuerNotMatched)},
  1060  				resultCount: 0,
  1061  			},
  1062  			{
  1063  				name:        "QueryByFrame NO BBS signature - success",
  1064  				credentials: []*verifiable.Credential{vc1, vc2},
  1065  				example:     []json.RawMessage{[]byte(queryByFrameExampleIssuerNotMatched)},
  1066  				resultCount: 0,
  1067  			},
  1068  			{
  1069  				name:        "QueryByFrame invalid BBS signature - success",
  1070  				credentials: []*verifiable.Credential{vc1, vc2, vc4},
  1071  				example:     []json.RawMessage{[]byte(sampleQueryByFrame)},
  1072  				resultCount: 0,
  1073  			},
  1074  			{
  1075  				name:        "QueryByFrame invalid query - success",
  1076  				credentials: []*verifiable.Credential{vc1, vc3},
  1077  				example:     []json.RawMessage{[]byte(sampleInvalidDIDContent)},
  1078  				error:       "failed to parse QueryByFrame query",
  1079  			},
  1080  			{
  1081  				name:        "QueryByFrame parse query error - success",
  1082  				credentials: []*verifiable.Credential{vc1, vc3},
  1083  				example:     []json.RawMessage{[]byte("---")},
  1084  				error:       "failed to parse QueryByFrame query",
  1085  			},
  1086  		}
  1087  
  1088  		t.Parallel()
  1089  
  1090  		for _, test := range tests {
  1091  			tc := test
  1092  			t.Run(tc.name, func(t *testing.T) {
  1093  				if tc.skip {
  1094  					return
  1095  				}
  1096  
  1097  				results, err := queryByFrame(tc.credentials, pubKeyFetcher, loader, tc.example...)
  1098  
  1099  				if tc.error != "" {
  1100  					require.Empty(t, results)
  1101  					require.Error(t, err)
  1102  					require.Contains(t, err.Error(), tc.error)
  1103  					require.Len(t, results, tc.resultCount)
  1104  
  1105  					return
  1106  				}
  1107  
  1108  				require.NoError(t, err)
  1109  				require.Len(t, results, tc.resultCount)
  1110  			})
  1111  		}
  1112  	})
  1113  }
  1114  
  1115  func TestUtilFunctions(t *testing.T) {
  1116  	require.True(t, isEmpty(""))
  1117  	require.True(t, isEmpty([]string{}))
  1118  	require.True(t, isEmpty([]interface{}{}))
  1119  	require.True(t, isEmpty(nil))
  1120  
  1121  	require.False(t, isEmpty("x"))
  1122  	require.False(t, isEmpty([]string{""}))
  1123  	require.False(t, isEmpty([]interface{}{&QueryParams{}}))
  1124  	require.False(t, isEmpty(&QueryParams{}))
  1125  
  1126  	require.False(t, contains([]string{"a", "b"}, nil))
  1127  }