github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/protocol/presentproof/states_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package presentproof 8 9 import ( 10 "errors" 11 "fmt" 12 "testing" 13 14 "github.com/golang/mock/gomock" 15 "github.com/google/uuid" 16 "github.com/stretchr/testify/require" 17 18 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model" 19 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" 20 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" 21 serviceMocks "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/didcomm/common/service" 22 ) 23 24 func TestStart_CanTransitionTo(t *testing.T) { 25 st := &start{} 26 require.Equal(t, stateNameStart, st.Name()) 27 // common states 28 require.False(t, st.CanTransitionTo(&start{})) 29 require.False(t, st.CanTransitionTo(&abandoned{})) 30 require.False(t, st.CanTransitionTo(&done{})) 31 require.False(t, st.CanTransitionTo(&noOp{})) 32 // states for Verifier 33 require.True(t, st.CanTransitionTo(&requestSent{})) 34 require.False(t, st.CanTransitionTo(&presentationReceived{})) 35 require.True(t, st.CanTransitionTo(&proposalReceived{})) 36 // states for Prover 37 require.True(t, st.CanTransitionTo(&requestReceived{})) 38 require.False(t, st.CanTransitionTo(&presentationSent{})) 39 require.True(t, st.CanTransitionTo(&proposalSent{})) 40 } 41 42 func TestStart_Execute(t *testing.T) { 43 followup, action, err := (&start{}).Execute(&metaData{}) 44 require.Contains(t, fmt.Sprintf("%v", err), "is not implemented yet") 45 require.Nil(t, followup) 46 require.Nil(t, action) 47 } 48 49 func TestAbandoning_CanTransitionTo(t *testing.T) { 50 st := &abandoned{} 51 require.Equal(t, StateNameAbandoned, st.Name()) 52 // common states 53 require.False(t, st.CanTransitionTo(&start{})) 54 require.False(t, st.CanTransitionTo(&abandoned{})) 55 require.False(t, st.CanTransitionTo(&done{})) 56 require.False(t, st.CanTransitionTo(&noOp{})) 57 // states for Verifier 58 require.False(t, st.CanTransitionTo(&requestSent{})) 59 require.False(t, st.CanTransitionTo(&presentationReceived{})) 60 require.False(t, st.CanTransitionTo(&proposalReceived{})) 61 // states for Prover 62 require.False(t, st.CanTransitionTo(&requestReceived{})) 63 require.False(t, st.CanTransitionTo(&presentationSent{})) 64 require.False(t, st.CanTransitionTo(&proposalSent{})) 65 } 66 67 func TestAbandoning_Execute(t *testing.T) { 68 t.Run("Internal Error", func(t *testing.T) { 69 md := &metaData{} 70 md.Msg = service.NewDIDCommMsgMap(struct{}{}) 71 md.Msg.SetID(uuid.New().String()) 72 73 followup, action, err := (&abandoned{V: SpecV2, Code: codeInternalError}).Execute(md) 74 require.NoError(t, err) 75 require.Equal(t, &noOp{}, followup) 76 require.NotNil(t, action) 77 78 ctrl := gomock.NewController(t) 79 defer ctrl.Finish() 80 81 messenger := serviceMocks.NewMockMessenger(ctrl) 82 messenger.EXPECT(). 83 ReplyToNested(gomock.Any(), gomock.Any()). 84 Do(func(msg service.DIDCommMsgMap, opts *service.NestedReplyOpts) error { 85 r := &model.ProblemReport{} 86 require.NoError(t, msg.Decode(r)) 87 require.Equal(t, codeInternalError, r.Description.Code) 88 require.Equal(t, ProblemReportMsgTypeV2, r.Type) 89 90 return nil 91 }) 92 93 require.NoError(t, action(messenger)) 94 }) 95 96 t.Run("Internal Error (v3)", func(t *testing.T) { 97 md := &metaData{} 98 md.Msg = service.NewDIDCommMsgMap(struct{}{}) 99 md.Msg.SetID(uuid.New().String()) 100 101 followup, action, err := (&abandoned{V: SpecV3, Code: codeInternalError}).Execute(md) 102 require.NoError(t, err) 103 require.Equal(t, &noOp{}, followup) 104 require.NotNil(t, action) 105 106 ctrl := gomock.NewController(t) 107 defer ctrl.Finish() 108 109 messenger := serviceMocks.NewMockMessenger(ctrl) 110 messenger.EXPECT(). 111 ReplyToNested(gomock.Any(), gomock.Any()). 112 Do(func(msg service.DIDCommMsgMap, opts *service.NestedReplyOpts) error { 113 r := &model.ProblemReportV2{} 114 require.NoError(t, msg.Decode(r)) 115 require.Equal(t, codeInternalError, r.Body.Code) 116 require.Equal(t, ProblemReportMsgTypeV3, r.Type) 117 118 return nil 119 }) 120 121 require.NoError(t, action(messenger)) 122 }) 123 124 t.Run("Invalid message", func(t *testing.T) { 125 followup, action, err := (&abandoned{Code: codeInternalError}).Execute(&metaData{}) 126 require.EqualError(t, errors.Unwrap(err), service.ErrInvalidMessage.Error()) 127 require.Nil(t, followup) 128 require.Nil(t, action) 129 }) 130 131 t.Run("Custom Error", func(t *testing.T) { 132 md := &metaData{err: customError{error: errors.New("error")}} 133 md.Msg = service.NewDIDCommMsgMap(struct{}{}) 134 md.Msg.SetID(uuid.New().String()) 135 136 followup, action, err := (&abandoned{V: SpecV2, Code: codeInternalError}).Execute(md) 137 require.NoError(t, err) 138 require.Equal(t, &noOp{}, followup) 139 require.NotNil(t, action) 140 141 ctrl := gomock.NewController(t) 142 defer ctrl.Finish() 143 144 messenger := serviceMocks.NewMockMessenger(ctrl) 145 messenger.EXPECT(). 146 ReplyToNested(gomock.Any(), gomock.Any()). 147 Do(func(msg service.DIDCommMsgMap, opts *service.NestedReplyOpts) error { 148 r := &model.ProblemReport{} 149 require.NoError(t, msg.Decode(r)) 150 require.Equal(t, codeRejectedError, r.Description.Code) 151 require.Equal(t, ProblemReportMsgTypeV2, r.Type) 152 153 return nil 154 }) 155 156 require.NoError(t, action(messenger)) 157 }) 158 159 t.Run("No error code", func(t *testing.T) { 160 md := &metaData{} 161 md.Msg = service.NewDIDCommMsgMap(struct{}{}) 162 md.Msg.SetID(uuid.New().String()) 163 164 followup, action, err := (&abandoned{}).Execute(md) 165 require.NoError(t, err) 166 require.Equal(t, &noOp{}, followup) 167 require.NotNil(t, action) 168 169 require.NoError(t, action(nil)) 170 }) 171 } 172 173 func TestDone_CanTransitionTo(t *testing.T) { 174 st := &done{} 175 require.Equal(t, StateNameDone, st.Name()) 176 notTransition(t, st) 177 } 178 179 func TestDone_Execute(t *testing.T) { 180 followup, action, err := (&done{}).Execute(&metaData{}) 181 require.NoError(t, err) 182 require.Equal(t, &noOp{}, followup) 183 require.NoError(t, action(nil)) 184 } 185 186 func TestNoOp_CanTransitionTo(t *testing.T) { 187 st := &noOp{} 188 require.Equal(t, stateNameNoop, st.Name()) 189 notTransition(t, st) 190 } 191 192 func TestNoOp_Execute(t *testing.T) { 193 followup, action, err := (&noOp{}).Execute(&metaData{}) 194 require.Contains(t, fmt.Sprintf("%v", err), "cannot execute no-op") 195 require.Nil(t, followup) 196 require.Nil(t, action) 197 } 198 199 func TestRequestReceived_CanTransitionTo(t *testing.T) { 200 st := &requestReceived{} 201 require.Equal(t, stateNameRequestReceived, st.Name()) 202 // common states 203 require.False(t, st.CanTransitionTo(&start{})) 204 require.True(t, st.CanTransitionTo(&abandoned{})) 205 require.False(t, st.CanTransitionTo(&done{})) 206 require.False(t, st.CanTransitionTo(&noOp{})) 207 // states for Verifier 208 require.False(t, st.CanTransitionTo(&requestSent{})) 209 require.False(t, st.CanTransitionTo(&presentationReceived{})) 210 require.False(t, st.CanTransitionTo(&proposalReceived{})) 211 // states for Prover 212 require.False(t, st.CanTransitionTo(&requestReceived{})) 213 require.True(t, st.CanTransitionTo(&presentationSent{})) 214 require.True(t, st.CanTransitionTo(&proposalSent{})) 215 } 216 217 func TestRequestReceived_Execute(t *testing.T) { 218 t.Run("With presentation", func(t *testing.T) { 219 msg := randomInboundMessage(RequestPresentationMsgTypeV2) 220 msg["will_confirm"] = true 221 222 followup, action, err := (&requestReceived{V: SpecV2}).Execute(&metaData{ 223 presentation: &PresentationV2{}, 224 transitionalPayload: transitionalPayload{Action: Action{Msg: msg}}, 225 }) 226 require.NoError(t, err) 227 require.Equal(t, &presentationSent{V: SpecV2, WillConfirm: true}, followup) 228 require.NoError(t, action(nil)) 229 }) 230 231 t.Run("With presentation - Ack is not required", func(t *testing.T) { 232 followup, action, err := (&requestReceived{}).Execute(&metaData{ 233 presentation: &PresentationV2{}, 234 transitionalPayload: transitionalPayload{Action: Action{ 235 Msg: randomInboundMessage(RequestPresentationMsgTypeV2), 236 }}, 237 }) 238 require.NoError(t, err) 239 require.Equal(t, &presentationSent{}, followup) 240 require.NoError(t, action(nil)) 241 }) 242 243 t.Run("Without presentation", func(t *testing.T) { 244 followup, action, err := (&requestReceived{}).Execute(&metaData{}) 245 require.NoError(t, err) 246 require.Equal(t, &proposalSent{}, followup) 247 require.NoError(t, action(nil)) 248 }) 249 250 t.Run("Message decode error", func(t *testing.T) { 251 followup, action, err := (&requestReceived{V: SpecV2}).Execute(&metaData{ 252 presentation: &PresentationV2{}, 253 transitionalPayload: transitionalPayload{Action: Action{ 254 Msg: service.DIDCommMsgMap{"@type": []int{1}}, 255 }}, 256 }) 257 require.Error(t, err) 258 require.Nil(t, followup) 259 require.Nil(t, action) 260 }) 261 } 262 263 func TestRequestSent_CanTransitionTo(t *testing.T) { 264 st := &requestSent{} 265 require.Equal(t, stateNameRequestSent, st.Name()) 266 // common states 267 require.False(t, st.CanTransitionTo(&start{})) 268 require.True(t, st.CanTransitionTo(&abandoned{})) 269 require.False(t, st.CanTransitionTo(&done{})) 270 require.False(t, st.CanTransitionTo(&noOp{})) 271 // states for Verifier 272 require.False(t, st.CanTransitionTo(&requestSent{})) 273 require.True(t, st.CanTransitionTo(&presentationReceived{})) 274 require.True(t, st.CanTransitionTo(&proposalReceived{})) 275 // states for Prover 276 require.False(t, st.CanTransitionTo(&requestReceived{})) 277 require.False(t, st.CanTransitionTo(&presentationSent{})) 278 require.False(t, st.CanTransitionTo(&proposalSent{})) 279 } 280 281 func randomInboundMessage(t string) service.DIDCommMsgMap { 282 return service.NewDIDCommMsgMap(struct { 283 ID string `json:"@id"` 284 Thread decorator.Thread `json:"~thread"` 285 Type string `json:"@type"` 286 }{ 287 ID: uuid.New().String(), 288 Thread: decorator.Thread{ID: uuid.New().String()}, 289 Type: t, 290 }) 291 } 292 293 func randomInboundMessageV3(t string) service.DIDCommMsgMap { 294 return service.DIDCommMsgMap{ 295 "id": uuid.New().String(), 296 "type": t, 297 "thid": uuid.New().String(), 298 } 299 } 300 301 func TestRequestSent_Execute(t *testing.T) { 302 t.Run("Success", func(t *testing.T) { 303 followup, action, err := (&requestSent{V: SpecV2}).Execute(&metaData{ 304 transitionalPayload: transitionalPayload{Action: Action{Msg: randomInboundMessage("")}}, 305 request: &RequestPresentationV2{}, 306 }) 307 require.NoError(t, err) 308 require.Equal(t, &noOp{}, followup) 309 require.NotNil(t, action) 310 311 ctrl := gomock.NewController(t) 312 defer ctrl.Finish() 313 314 messenger := serviceMocks.NewMockMessenger(ctrl) 315 messenger.EXPECT().ReplyToMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 316 317 require.NoError(t, action(messenger)) 318 }) 319 320 t.Run("Invitation presentation is absent", func(t *testing.T) { 321 followup, action, err := (&requestSent{}).Execute(&metaData{ 322 transitionalPayload: transitionalPayload{Action: Action{Msg: randomInboundMessage("")}}, 323 }) 324 require.EqualError(t, err, "request was not provided") 325 require.Nil(t, followup) 326 require.Nil(t, action) 327 }) 328 329 t.Run("Success (outbound)", func(t *testing.T) { 330 followup, action, err := (&requestSent{}).Execute(&metaData{transitionalPayload: transitionalPayload{ 331 Action: Action{Msg: service.NewDIDCommMsgMap(struct { 332 WillConfirm bool `json:"will_confirm"` 333 }{WillConfirm: true})}, 334 Direction: outboundMessage, 335 }}) 336 require.NoError(t, err) 337 require.Equal(t, &noOp{}, followup) 338 require.NotNil(t, action) 339 340 ctrl := gomock.NewController(t) 341 defer ctrl.Finish() 342 343 messenger := serviceMocks.NewMockMessenger(ctrl) 344 messenger.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 345 346 require.NoError(t, action(messenger)) 347 }) 348 349 t.Run("Message decode error", func(t *testing.T) { 350 followup, action, err := (&requestSent{V: SpecV2}).Execute(&metaData{transitionalPayload: transitionalPayload{ 351 Action: Action{Msg: service.DIDCommMsgMap{"@type": []int{1}}}, 352 }}) 353 require.Error(t, err) 354 require.Nil(t, followup) 355 require.Nil(t, action) 356 }) 357 } 358 359 func TestPresentationSent_CanTransitionTo(t *testing.T) { 360 st := &presentationSent{} 361 require.Equal(t, stateNamePresentationSent, st.Name()) 362 // common states 363 require.False(t, st.CanTransitionTo(&start{})) 364 require.True(t, st.CanTransitionTo(&abandoned{})) 365 require.True(t, st.CanTransitionTo(&done{})) 366 require.False(t, st.CanTransitionTo(&noOp{})) 367 // states for Verifier 368 require.False(t, st.CanTransitionTo(&requestSent{})) 369 require.False(t, st.CanTransitionTo(&presentationReceived{})) 370 require.False(t, st.CanTransitionTo(&proposalReceived{})) 371 // states for Prover 372 require.False(t, st.CanTransitionTo(&requestReceived{})) 373 require.False(t, st.CanTransitionTo(&presentationSent{})) 374 require.False(t, st.CanTransitionTo(&proposalSent{})) 375 } 376 377 func TestPresentationSent_Execute(t *testing.T) { 378 t.Run("Success", func(t *testing.T) { 379 followup, action, err := (&presentationSent{}). 380 Execute(&metaData{presentation: &PresentationV2{}}) 381 require.NoError(t, err) 382 require.Equal(t, &done{}, followup) 383 require.NotNil(t, action) 384 385 ctrl := gomock.NewController(t) 386 defer ctrl.Finish() 387 388 messenger := serviceMocks.NewMockMessenger(ctrl) 389 messenger.EXPECT().ReplyToMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 390 391 require.NoError(t, action(messenger)) 392 }) 393 394 t.Run("Success (WillConfirm)", func(t *testing.T) { 395 followup, action, err := (&presentationSent{WillConfirm: true}). 396 Execute(&metaData{presentation: &PresentationV2{}}) 397 require.NoError(t, err) 398 require.Equal(t, &noOp{}, followup) 399 require.NotNil(t, action) 400 401 ctrl := gomock.NewController(t) 402 defer ctrl.Finish() 403 404 messenger := serviceMocks.NewMockMessenger(ctrl) 405 messenger.EXPECT().ReplyToMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 406 407 require.NoError(t, action(messenger)) 408 }) 409 410 t.Run("Presentation is absent", func(t *testing.T) { 411 followup, action, err := (&presentationSent{}).Execute(&metaData{}) 412 require.EqualError(t, err, "presentation was not provided") 413 require.Nil(t, followup) 414 require.Nil(t, action) 415 }) 416 } 417 418 func TestPresentationReceived_CanTransitionTo(t *testing.T) { 419 st := &presentationReceived{} 420 require.Equal(t, stateNamePresentationReceived, st.Name()) 421 // common states 422 require.False(t, st.CanTransitionTo(&start{})) 423 require.True(t, st.CanTransitionTo(&abandoned{})) 424 require.True(t, st.CanTransitionTo(&done{})) 425 require.False(t, st.CanTransitionTo(&noOp{})) 426 // states for Verifier 427 require.False(t, st.CanTransitionTo(&requestSent{})) 428 require.False(t, st.CanTransitionTo(&presentationReceived{})) 429 require.False(t, st.CanTransitionTo(&proposalReceived{})) 430 // states for Prover 431 require.False(t, st.CanTransitionTo(&requestReceived{})) 432 require.False(t, st.CanTransitionTo(&presentationSent{})) 433 require.False(t, st.CanTransitionTo(&proposalSent{})) 434 } 435 436 func TestPresentationReceived_Execute(t *testing.T) { 437 t.Run("Success", func(t *testing.T) { 438 followup, action, err := (&presentationReceived{}).Execute(&metaData{ 439 transitionalPayload: transitionalPayload{AckRequired: true}, 440 presentation: &PresentationV2{}, 441 }) 442 require.NoError(t, err) 443 require.Equal(t, &done{}, followup) 444 require.NotNil(t, action) 445 446 ctrl := gomock.NewController(t) 447 defer ctrl.Finish() 448 449 messenger := serviceMocks.NewMockMessenger(ctrl) 450 messenger.EXPECT().ReplyToMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 451 452 require.NoError(t, action(messenger)) 453 }) 454 455 t.Run("Ack is not required", func(t *testing.T) { 456 followup, action, err := (&presentationReceived{}).Execute(&metaData{ 457 request: &RequestPresentationV2{WillConfirm: true}, 458 presentation: &PresentationV2{}, 459 }) 460 require.NoError(t, err) 461 require.Equal(t, &done{}, followup) 462 require.NotNil(t, action) 463 require.NoError(t, action(nil)) 464 }) 465 } 466 467 func TestProposePresentationSent_CanTransitionTo(t *testing.T) { 468 st := &proposalSent{} 469 require.Equal(t, stateNameProposalSent, st.Name()) 470 // common states 471 require.False(t, st.CanTransitionTo(&start{})) 472 require.True(t, st.CanTransitionTo(&abandoned{})) 473 require.False(t, st.CanTransitionTo(&done{})) 474 require.False(t, st.CanTransitionTo(&noOp{})) 475 // states for Verifier 476 require.False(t, st.CanTransitionTo(&requestSent{})) 477 require.False(t, st.CanTransitionTo(&presentationReceived{})) 478 require.False(t, st.CanTransitionTo(&proposalReceived{})) 479 // states for Prover 480 require.True(t, st.CanTransitionTo(&requestReceived{})) 481 require.False(t, st.CanTransitionTo(&presentationSent{})) 482 require.False(t, st.CanTransitionTo(&proposalSent{})) 483 } 484 485 func TestProposePresentationSent_Execute(t *testing.T) { 486 t.Run("Success", func(t *testing.T) { 487 followup, action, err := (&proposalSent{}).Execute(&metaData{ 488 transitionalPayload: transitionalPayload{Action: Action{Msg: randomInboundMessage("")}}, 489 proposePresentation: &ProposePresentationV2{}, 490 }) 491 require.NoError(t, err) 492 require.Equal(t, &noOp{}, followup) 493 require.NotNil(t, action) 494 495 ctrl := gomock.NewController(t) 496 defer ctrl.Finish() 497 498 messenger := serviceMocks.NewMockMessenger(ctrl) 499 messenger.EXPECT().ReplyToMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 500 501 require.NoError(t, action(messenger)) 502 }) 503 504 t.Run("Propose presentation is absent", func(t *testing.T) { 505 followup, action, err := (&proposalSent{}).Execute(&metaData{ 506 transitionalPayload: transitionalPayload{Action: Action{Msg: randomInboundMessage("")}}, 507 }) 508 require.EqualError(t, err, "propose-presentation was not provided") 509 require.Nil(t, followup) 510 require.Nil(t, action) 511 }) 512 513 t.Run("Success (outbound)", func(t *testing.T) { 514 followup, action, err := (&proposalSent{}).Execute(&metaData{ 515 transitionalPayload: transitionalPayload{Direction: outboundMessage}, 516 }) 517 require.NoError(t, err) 518 require.Equal(t, &noOp{}, followup) 519 require.NotNil(t, action) 520 521 ctrl := gomock.NewController(t) 522 defer ctrl.Finish() 523 524 messenger := serviceMocks.NewMockMessenger(ctrl) 525 messenger.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 526 527 require.NoError(t, action(messenger)) 528 }) 529 } 530 531 func TestProposePresentationReceived_CanTransitionTo(t *testing.T) { 532 st := &proposalReceived{} 533 require.Equal(t, stateNameProposalReceived, st.Name()) 534 // common states 535 require.False(t, st.CanTransitionTo(&start{})) 536 require.True(t, st.CanTransitionTo(&abandoned{})) 537 require.False(t, st.CanTransitionTo(&done{})) 538 require.False(t, st.CanTransitionTo(&noOp{})) 539 // states for Verifier 540 require.True(t, st.CanTransitionTo(&requestSent{})) 541 require.False(t, st.CanTransitionTo(&presentationReceived{})) 542 require.False(t, st.CanTransitionTo(&proposalReceived{})) 543 // states for Prover 544 require.False(t, st.CanTransitionTo(&requestReceived{})) 545 require.False(t, st.CanTransitionTo(&presentationSent{})) 546 require.False(t, st.CanTransitionTo(&proposalSent{})) 547 } 548 549 func TestProposePresentationReceived_Execute(t *testing.T) { 550 followup, action, err := (&proposalReceived{}).Execute(&metaData{}) 551 require.NoError(t, err) 552 require.Equal(t, &requestSent{}, followup) 553 require.NoError(t, action(nil)) 554 } 555 556 func notTransition(t *testing.T, st state) { 557 t.Helper() 558 559 allState := [...]state{ 560 // common states 561 &start{}, &abandoned{}, &done{}, &noOp{}, 562 // states for Verifier 563 &requestSent{}, &presentationReceived{}, &proposalReceived{}, 564 // states for Prover 565 &requestReceived{}, &presentationSent{}, &proposalSent{}, 566 } 567 568 for _, s := range allState { 569 require.False(t, st.CanTransitionTo(s)) 570 } 571 }