//stm: #chaos package chaos import ( "context" "testing" "github.com/ipfs/go-cid" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/exitcode" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" "github.com/filecoin-project/specs-actors/v2/support/mock" atesting2 "github.com/filecoin-project/specs-actors/v2/support/testing" ) func TestSingleton(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_BUILDER_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor msg := "constructor should not be called; the Chaos actor is a singleton actor" rt.ExpectAssertionFailure(msg, func() { rt.Call(a.Constructor, abi.Empty) }) rt.Verify() } func TestCallerValidationNone(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CALLER_VALIDATION_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.Call(a.CallerValidation, &CallerValidationArgs{Branch: CallerValidationBranchNone}) rt.Verify() } func TestCallerValidationIs(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CALLER_VALIDATION_001 caller := atesting2.NewIDAddr(t, 100) receiver := atesting2.NewIDAddr(t, 101) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) rt.SetCaller(caller, builtin2.AccountActorCodeID) var a Actor caddrs := []address.Address{atesting2.NewIDAddr(t, 101)} rt.ExpectValidateCallerAddr(caddrs...) // fixed in: https://github.com/filecoin-project/specs-actors/pull/1155 rt.ExpectAbort(exitcode.SysErrForbidden, func() { rt.Call(a.CallerValidation, &CallerValidationArgs{ Branch: CallerValidationBranchIsAddress, Addrs: caddrs, }) }) rt.Verify() rt.ExpectValidateCallerAddr(caller) rt.Call(a.CallerValidation, &CallerValidationArgs{ Branch: CallerValidationBranchIsAddress, Addrs: []address.Address{caller}, }) rt.Verify() } func TestCallerValidationType(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CALLER_VALIDATION_001 caller := atesting2.NewIDAddr(t, 100) receiver := atesting2.NewIDAddr(t, 101) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) rt.SetCaller(caller, builtin2.AccountActorCodeID) var a Actor rt.ExpectValidateCallerType(builtin2.CronActorCodeID) rt.ExpectAbort(exitcode.SysErrForbidden, func() { rt.Call(a.CallerValidation, &CallerValidationArgs{ Branch: CallerValidationBranchIsType, Types: []cid.Cid{builtin2.CronActorCodeID}, }) }) rt.Verify() rt.ExpectValidateCallerType(builtin2.AccountActorCodeID) rt.Call(a.CallerValidation, &CallerValidationArgs{ Branch: CallerValidationBranchIsType, Types: []cid.Cid{builtin2.AccountActorCodeID}, }) rt.Verify() } func TestCallerValidationInvalidBranch(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CALLER_VALIDATION_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.ExpectAssertionFailure("invalid branch passed to CallerValidation", func() { rt.Call(a.CallerValidation, &CallerValidationArgs{Branch: -1}) }) rt.Verify() } func TestDeleteActor(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CREATE_ACTOR_001 receiver := atesting2.NewIDAddr(t, 100) beneficiary := atesting2.NewIDAddr(t, 101) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.ExpectValidateCallerAny() rt.ExpectDeleteActor(beneficiary) rt.Call(a.DeleteActor, &beneficiary) rt.Verify() } func TestMutateStateInTransaction(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CREATE_STATE_001, @CHAIN_ACTOR_CHAOS_MUTATE_STATE_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.ExpectValidateCallerAny() rt.Call(a.CreateState, nil) rt.ExpectValidateCallerAny() val := "__mutstat test" rt.Call(a.MutateState, &MutateStateArgs{ Value: val, Branch: MutateInTransaction, }) var st State rt.GetState(&st) if st.Value != val { t.Fatal("state was not updated") } rt.Verify() } func TestMutateStateAfterTransaction(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CREATE_STATE_001, @CHAIN_ACTOR_CHAOS_MUTATE_STATE_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.ExpectValidateCallerAny() rt.Call(a.CreateState, nil) rt.ExpectValidateCallerAny() val := "__mutstat test" defer func() { if r := recover(); r == nil { t.Fatal("The code did not panic") } else { var st State rt.GetState(&st) // state should be updated successfully _in_ the transaction but not outside if st.Value != val+"-in" { t.Fatal("state was not updated") } rt.Verify() } }() rt.Call(a.MutateState, &MutateStateArgs{ Value: val, Branch: MutateAfterTransaction, }) } func TestMutateStateReadonly(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_CREATE_STATE_001, @CHAIN_ACTOR_CHAOS_MUTATE_STATE_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.ExpectValidateCallerAny() rt.Call(a.CreateState, nil) rt.ExpectValidateCallerAny() val := "__mutstat test" defer func() { if r := recover(); r == nil { t.Fatal("The code did not panic") } else { var st State rt.GetState(&st) if st.Value != "" { t.Fatal("state was not expected to be updated") } rt.Verify() } }() rt.Call(a.MutateState, &MutateStateArgs{ Value: val, Branch: MutateReadonly, }) } func TestMutateStateInvalidBranch(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_MUTATE_STATE_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor rt.ExpectValidateCallerAny() rt.ExpectAssertionFailure("unknown mutation type", func() { rt.Call(a.MutateState, &MutateStateArgs{Branch: -1}) }) rt.Verify() } func TestAbortWith(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_ABORT_WITH_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor msg := "__test forbidden" rt.ExpectAbortContainsMessage(exitcode.ErrForbidden, msg, func() { rt.Call(a.AbortWith, &AbortWithArgs{ Code: exitcode.ErrForbidden, Message: msg, Uncontrolled: false, }) }) rt.Verify() } func TestAbortWithUncontrolled(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_ABORT_WITH_001 receiver := atesting2.NewIDAddr(t, 100) builder := mock.NewBuilder(context.Background(), receiver) rt := builder.Build(t) var a Actor msg := "__test uncontrolled panic" rt.ExpectAssertionFailure(msg, func() { rt.Call(a.AbortWith, &AbortWithArgs{ Message: msg, Uncontrolled: true, }) }) rt.Verify() } func TestInspectRuntime(t *testing.T) { //stm: @CHAIN_ACTOR_CHAOS_INSPECT_RUNTIME_001, @CHAIN_ACTOR_CHAOS_CREATE_STATE_001 caller := atesting2.NewIDAddr(t, 100) receiver := atesting2.NewIDAddr(t, 101) builder := mock.NewBuilder(context.Background(), receiver) var a Actor rt := builder.Build(t) rt.ExpectValidateCallerAny() rt.Call(a.CreateState, nil) rt.SetCaller(caller, builtin2.AccountActorCodeID) rt.ExpectValidateCallerAny() ret := rt.Call(a.InspectRuntime, abi.Empty) rtr, ok := ret.(*InspectRuntimeReturn) if !ok { t.Fatal("invalid return value") } if rtr.Caller != caller { t.Fatal("unexpected runtime caller") } if rtr.Receiver != receiver { t.Fatal("unexpected runtime receiver") } rt.Verify() }