github.com/ergo-services/ergo@v1.999.224/tests/saga_cancel_test.go (about)

     1  package tests
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/ergo-services/ergo"
     9  	"github.com/ergo-services/ergo/etf"
    10  	"github.com/ergo-services/ergo/gen"
    11  	"github.com/ergo-services/ergo/node"
    12  )
    13  
    14  // this test implements cases below
    15  // 1. Single saga cancels Tx
    16  // 2. Saga1 -> Tx -> Saga2 -> Tx -> Saga3
    17  //    a) Saga1 cancels Tx
    18  //       Saga1 -> cancel -> Saga2 -> cancel -> Saga3
    19  //    b) Saga2 cancels Tx
    20  //       Saga1 <- cancel <- Saga2 -> cancel -> Saga3
    21  //    c) Saga3 cancels Tx
    22  //       Saga1 <- cancel <- Saga2 <- cancel <- Saga3
    23  //    d) Saga1 sets TrapCancel, Saga2 process/node is going down, Saga1 sends Tx to the Saga4
    24  //              -> Tx -> Saga4 -> Tx -> Saga3
    25  //            /
    26  //       Saga1 <- signal Down <- Saga2 (terminates) -> signal Down -> Saga3
    27  
    28  //
    29  // Case 1
    30  //
    31  
    32  type taskSagaCancelCase1 struct {
    33  	workerRes chan interface{}
    34  	sagaRes   chan interface{}
    35  }
    36  
    37  type testSagaCancelWorker1 struct {
    38  	gen.SagaWorker
    39  }
    40  
    41  func (w *testSagaCancelWorker1) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error {
    42  	process.State = job.Value
    43  	return nil
    44  }
    45  func (w *testSagaCancelWorker1) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) {
    46  	if err := process.SendInterim(1); err != gen.ErrSagaTxCanceled {
    47  		panic("shouldn't be able to send interim result")
    48  	}
    49  	if err := process.SendResult(1); err != gen.ErrSagaTxCanceled {
    50  		panic("shouldn't be able to send the result")
    51  	}
    52  	task := process.State.(taskSagaCancelCase1)
    53  	task.workerRes <- "ok"
    54  	return
    55  }
    56  
    57  type testSagaCancel1 struct {
    58  	gen.Saga
    59  }
    60  
    61  func (gs *testSagaCancel1) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) {
    62  	worker := &testSagaCancelWorker1{}
    63  	opts := gen.SagaOptions{
    64  		Worker: worker,
    65  	}
    66  	return opts, nil
    67  }
    68  
    69  func (gs *testSagaCancel1) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus {
    70  	process.State = value
    71  	task := process.State.(taskSagaCancelCase1)
    72  	task.sagaRes <- "startTX"
    73  
    74  	_, err := process.StartJob(id, gen.SagaJobOptions{}, value)
    75  	if err != nil {
    76  		panic(err)
    77  	}
    78  	task.workerRes <- "startWorker"
    79  	if err := process.CancelTransaction(id, "test cancel"); err != nil {
    80  		panic(err)
    81  	}
    82  
    83  	// try to cancel unknown TX
    84  	if err := process.CancelTransaction(gen.SagaTransactionID{}, "bla bla"); err != gen.ErrSagaTxUnknown {
    85  		panic("must be ErrSagaTxUnknown")
    86  	}
    87  	task.sagaRes <- "cancelTX"
    88  	return gen.SagaStatusOK
    89  }
    90  
    91  func (gs *testSagaCancel1) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus {
    92  	task := process.State.(taskSagaCancelCase1)
    93  	if reason == "test cancel" {
    94  		task.sagaRes <- "ok"
    95  	}
    96  	return gen.SagaStatusOK
    97  }
    98  
    99  func (gs *testSagaCancel1) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus {
   100  	return gen.SagaStatusOK
   101  }
   102  
   103  func (gs *testSagaCancel1) HandleSagaDirect(process *gen.SagaProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
   104  
   105  	process.StartTransaction(gen.SagaTransactionOptions{}, message)
   106  	return nil, nil
   107  }
   108  
   109  func TestSagaCancelSimple(t *testing.T) {
   110  
   111  	fmt.Printf("\n=== Test GenSagaCancelSimple\n")
   112  	fmt.Printf("Starting node: nodeGenSagaCancelSimple01@localhost...")
   113  
   114  	node, _ := ergo.StartNode("nodeGenSagaCancelSimple01@localhost", "cookies", node.Options{})
   115  	if node == nil {
   116  		t.Fatal("can't start node")
   117  		return
   118  	}
   119  	fmt.Println("OK")
   120  	defer node.Stop()
   121  
   122  	fmt.Printf("... Starting Saga processes: ")
   123  	saga := &testSagaCancel1{}
   124  	saga_process, err := node.Spawn("saga", gen.ProcessOptions{MailboxSize: 10000}, saga)
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	fmt.Println("OK")
   129  
   130  	task := taskSagaCancelCase1{
   131  		workerRes: make(chan interface{}, 2),
   132  		sagaRes:   make(chan interface{}, 2),
   133  	}
   134  	_, err = saga_process.Direct(task)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  	fmt.Printf("... Start new TX on saga: ")
   139  	waitForResultWithValue(t, task.sagaRes, "startTX")
   140  	fmt.Printf("... Start new worker on saga: ")
   141  	waitForResultWithValue(t, task.workerRes, "startWorker")
   142  	fmt.Printf("... Cancel TX on saga: ")
   143  	waitForResultWithValue(t, task.sagaRes, "cancelTX")
   144  	fmt.Printf("... Saga worker handled TX cancelation: ")
   145  	waitForResultWithValue(t, task.workerRes, "ok")
   146  	fmt.Printf("... Saga handled TX cancelation: ")
   147  	waitForResultWithValue(t, task.sagaRes, "ok")
   148  }
   149  
   150  //
   151  // Case 2.a
   152  //    Node1.Saga1 -> Tx -> Node2.Saga2 -> Tx -> Node3.Saga3
   153  //       Node1.Saga1 cancels Tx
   154  //       Node1.Saga1 -> cancel -> Node2.Saga2 -> cancel -> Node3Saga3
   155  //
   156  
   157  type testSagaCancelWorker2 struct {
   158  	gen.SagaWorker
   159  }
   160  
   161  func (w *testSagaCancelWorker2) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error {
   162  	process.State = job.Value
   163  	return nil
   164  }
   165  func (w *testSagaCancelWorker2) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) {
   166  	if err := process.SendInterim(1); err != gen.ErrSagaTxCanceled {
   167  		panic("shouldn't be able to send interim result")
   168  	}
   169  	if err := process.SendResult(1); err != gen.ErrSagaTxCanceled {
   170  		panic("shouldn't be able to send the result")
   171  	}
   172  	args := process.State.(testSagaCancel2Args)
   173  	args.workerRes <- reason
   174  	return
   175  }
   176  
   177  type testSagaCancel2 struct {
   178  	gen.Saga
   179  }
   180  
   181  type testSagaCancel2Args struct {
   182  	workerRes chan interface{}
   183  	sagaRes   chan interface{}
   184  }
   185  
   186  func (gs *testSagaCancel2) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) {
   187  	worker := &testSagaCancelWorker2{}
   188  	opts := gen.SagaOptions{
   189  		Worker: worker,
   190  	}
   191  	process.State = args[0] // testSagaCancel2Args
   192  	return opts, nil
   193  }
   194  
   195  func (gs *testSagaCancel2) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus {
   196  	args := process.State.(testSagaCancel2Args)
   197  	args.sagaRes <- id
   198  
   199  	_, err := process.StartJob(id, gen.SagaJobOptions{}, process.State)
   200  	if err != nil {
   201  		panic(err)
   202  	}
   203  	args.workerRes <- id
   204  
   205  	next := gen.SagaNext{}
   206  	switch process.Name() {
   207  	case "saga1":
   208  		trapCancel, _ := value.(bool)
   209  		if trapCancel {
   210  			// case 2.D
   211  			next.TrapCancel = true
   212  		}
   213  		next.Saga = gen.ProcessID{Name: "saga2", Node: "node2GenSagaCancelCases@localhost"}
   214  	case "saga2":
   215  		next.Saga = gen.ProcessID{Name: "saga3", Node: "node3GenSagaCancelCases@localhost"}
   216  	default:
   217  		return gen.SagaStatusOK
   218  	}
   219  	process.Next(id, next)
   220  
   221  	return gen.SagaStatusOK
   222  }
   223  
   224  func (gs *testSagaCancel2) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus {
   225  	args := process.State.(testSagaCancel2Args)
   226  	args.sagaRes <- reason
   227  	return gen.SagaStatusOK
   228  }
   229  
   230  func (gs *testSagaCancel2) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus {
   231  	return gen.SagaStatusOK
   232  }
   233  
   234  type testSagaStartTX struct {
   235  	TrapCancel bool
   236  }
   237  type testSagaCancelTX struct {
   238  	ID     gen.SagaTransactionID
   239  	Reason string
   240  }
   241  
   242  func (gs *testSagaCancel2) HandleSagaInfo(process *gen.SagaProcess, message etf.Term) gen.ServerStatus {
   243  	args := process.State.(testSagaCancel2Args)
   244  	switch m := message.(type) {
   245  	case gen.MessageSagaCancel:
   246  		args.sagaRes <- m.Reason
   247  		next := gen.SagaNext{}
   248  		next.Saga = gen.ProcessID{Name: "saga4", Node: "node4GenSagaCancelCases@localhost"}
   249  		process.Next(m.TransactionID, next)
   250  		args.sagaRes <- m.TransactionID
   251  	}
   252  	return gen.ServerStatusOK
   253  }
   254  
   255  func (gs *testSagaCancel2) HandleSagaDirect(process *gen.SagaProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
   256  
   257  	switch m := message.(type) {
   258  	case testSagaStartTX:
   259  		return process.StartTransaction(gen.SagaTransactionOptions{}, m.TrapCancel), nil
   260  	case testSagaCancelTX:
   261  		return nil, process.CancelTransaction(m.ID, m.Reason)
   262  	}
   263  	return nil, nil
   264  }
   265  
   266  func TestSagaCancelCases(t *testing.T) {
   267  	fmt.Printf("\n=== Test GenSagaCancelCases\n")
   268  
   269  	fmt.Printf("Starting node: node1GenSagaCancelCases@localhost...")
   270  	node1, _ := ergo.StartNode("node1GenSagaCancelCases@localhost", "cookies", node.Options{})
   271  
   272  	if node1 == nil {
   273  		t.Fatal("can't start node")
   274  		return
   275  	}
   276  	fmt.Println("OK")
   277  	defer node1.Stop()
   278  
   279  	fmt.Printf("Starting node: node2GenSagaCancelCases@localhost...")
   280  	node2, _ := ergo.StartNode("node2GenSagaCancelCases@localhost", "cookies", node.Options{})
   281  
   282  	if node2 == nil {
   283  		t.Fatal("can't start node")
   284  		return
   285  	}
   286  	fmt.Println("OK")
   287  	defer node2.Stop()
   288  
   289  	fmt.Printf("Starting node: node3GenSagaCancelCases@localhost...")
   290  	node3, _ := ergo.StartNode("node3GenSagaCancelCases@localhost", "cookies", node.Options{})
   291  
   292  	if node3 == nil {
   293  		t.Fatal("can't start node")
   294  		return
   295  	}
   296  	fmt.Println("OK")
   297  	defer node3.Stop()
   298  
   299  	args1 := testSagaCancel2Args{
   300  		workerRes: make(chan interface{}, 2),
   301  		sagaRes:   make(chan interface{}, 2),
   302  	}
   303  
   304  	fmt.Printf("Starting saga1 on node1GenSagaCancelCases@localhost...")
   305  	saga1 := &testSagaCancel2{}
   306  	saga1_process, err := node1.Spawn("saga1", gen.ProcessOptions{MailboxSize: 10000}, saga1, args1)
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  	fmt.Println("OK")
   311  
   312  	args2 := testSagaCancel2Args{
   313  		workerRes: make(chan interface{}, 2),
   314  		sagaRes:   make(chan interface{}, 2),
   315  	}
   316  	fmt.Printf("Starting saga2 on node2GenSagaCancelCases@localhost...")
   317  	saga2 := &testSagaCancel2{}
   318  	saga2_process, err := node2.Spawn("saga2", gen.ProcessOptions{MailboxSize: 10000}, saga2, args2)
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  	fmt.Println("OK")
   323  
   324  	args3 := testSagaCancel2Args{
   325  		workerRes: make(chan interface{}, 2),
   326  		sagaRes:   make(chan interface{}, 2),
   327  	}
   328  	fmt.Printf("Starting saga3 on node3GenSagaCancelCases@localhost...")
   329  	saga3 := &testSagaCancel2{}
   330  	saga3_process, err := node3.Spawn("saga3", gen.ProcessOptions{MailboxSize: 10000}, saga3, args3)
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	fmt.Println("OK")
   335  
   336  	//
   337  	// case 2.A
   338  	//
   339  	fmt.Println("  Case A (cancel TX on Node1.Saga1): Node1.Saga1 -> cancel -> Node2.Saga2 -> cancel -> Node3.Saga3")
   340  
   341  	ValueTXID, err := saga1_process.Direct(testSagaStartTX{})
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  	TXID, ok := ValueTXID.(gen.SagaTransactionID)
   346  	if !ok {
   347  		t.Fatal("not a gen.SagaTransactionID")
   348  	}
   349  
   350  	fmt.Printf("... Start new TX %v on saga1: ", TXID)
   351  	waitForResultWithValue(t, args1.sagaRes, TXID)
   352  	fmt.Printf("... Start new worker on saga1 with TX %v: ", TXID)
   353  	waitForResultWithValue(t, args1.workerRes, TXID)
   354  
   355  	fmt.Printf("... Start new TX %v on saga2: ", TXID)
   356  	waitForResultWithValue(t, args2.sagaRes, TXID)
   357  	fmt.Printf("... Start new worker on saga2 with TX %v: ", TXID)
   358  	waitForResultWithValue(t, args2.workerRes, TXID)
   359  
   360  	fmt.Printf("... Start new TX %v on saga3: ", TXID)
   361  	waitForResultWithValue(t, args3.sagaRes, TXID)
   362  	fmt.Printf("... Start new worker on saga3 with TX %v: ", TXID)
   363  	waitForResultWithValue(t, args3.workerRes, TXID)
   364  
   365  	fmt.Printf("... Cancel TX %v on saga1: ", TXID)
   366  	cancelReason := "cancel case1"
   367  	_, err = saga1_process.Direct(testSagaCancelTX{ID: TXID, Reason: cancelReason})
   368  	if err != nil {
   369  		t.Fatal(err)
   370  	}
   371  	waitForResultWithValue(t, args1.sagaRes, cancelReason)
   372  	fmt.Printf("...       saga1 cancels TX %v on its worker: ", TXID)
   373  	waitForResultWithValue(t, args1.workerRes, cancelReason)
   374  	fmt.Printf("...       cancels TX %v on saga2: ", TXID)
   375  	waitForResultWithValue(t, args2.sagaRes, cancelReason)
   376  	fmt.Printf("...       saga2 cancels TX %v on its worker: ", TXID)
   377  	waitForResultWithValue(t, args2.workerRes, cancelReason)
   378  	fmt.Printf("...       cancels TX %v on saga3: ", TXID)
   379  	waitForResultWithValue(t, args3.sagaRes, cancelReason)
   380  	fmt.Printf("...       saga3 cancels TX %v on its worker: ", TXID)
   381  	waitForResultWithValue(t, args3.workerRes, cancelReason)
   382  	//
   383  	// case 2.B
   384  	//
   385  
   386  	fmt.Println("  Case B (cancel TX on Node.Saga2): Node1.Saga1 <- cancel <- Node2.Saga2 -> cancel -> Node3.Saga3")
   387  
   388  	ValueTXID, err = saga1_process.Direct(testSagaStartTX{})
   389  	if err != nil {
   390  		t.Fatal(err)
   391  	}
   392  	TXID, ok = ValueTXID.(gen.SagaTransactionID)
   393  	if !ok {
   394  		t.Fatal("not a gen.SagaTransactionID")
   395  	}
   396  
   397  	fmt.Printf("... Start new TX %v on saga1: ", TXID)
   398  	waitForResultWithValue(t, args1.sagaRes, TXID)
   399  	fmt.Printf("... Start new worker on saga1 with TX %v: ", TXID)
   400  	waitForResultWithValue(t, args1.workerRes, TXID)
   401  
   402  	fmt.Printf("... Start new TX %v on saga2: ", TXID)
   403  	waitForResultWithValue(t, args2.sagaRes, TXID)
   404  	fmt.Printf("... Start new worker on saga2 with TX %v: ", TXID)
   405  	waitForResultWithValue(t, args2.workerRes, TXID)
   406  
   407  	fmt.Printf("... Start new TX %v on saga3: ", TXID)
   408  	waitForResultWithValue(t, args3.sagaRes, TXID)
   409  	fmt.Printf("... Start new worker on saga3 with TX %v: ", TXID)
   410  	waitForResultWithValue(t, args3.workerRes, TXID)
   411  
   412  	fmt.Printf("... Cancel TX %v on saga2: ", TXID)
   413  	cancelReason = "cancel case2"
   414  	_, err = saga2_process.Direct(testSagaCancelTX{ID: TXID, Reason: cancelReason})
   415  	if err != nil {
   416  		t.Fatal(err)
   417  	}
   418  	waitForResultWithValue(t, args2.sagaRes, cancelReason)
   419  	fmt.Printf("...       saga2 cancels TX %v on its worker: ", TXID)
   420  	waitForResultWithValue(t, args2.workerRes, cancelReason)
   421  	fmt.Printf("...       cancels TX %v on saga1: ", TXID)
   422  	waitForResultWithValue(t, args1.sagaRes, cancelReason)
   423  	fmt.Printf("...       saga1 cancels TX %v on its worker: ", TXID)
   424  	waitForResultWithValue(t, args1.workerRes, cancelReason)
   425  	fmt.Printf("...       cancels TX %v on saga3: ", TXID)
   426  	waitForResultWithValue(t, args3.sagaRes, cancelReason)
   427  	fmt.Printf("...       saga3 cancels TX %v on its worker: ", TXID)
   428  	waitForResultWithValue(t, args3.workerRes, cancelReason)
   429  	//
   430  	// case 2.C
   431  	//
   432  	fmt.Println("  Case C (cancel TX on Node.Saga3): Node1.Saga1 <- cancel <- Node2.Saga2 <- cancel <- Node3.Saga3")
   433  
   434  	ValueTXID, err = saga1_process.Direct(testSagaStartTX{})
   435  	if err != nil {
   436  		t.Fatal(err)
   437  	}
   438  	TXID, ok = ValueTXID.(gen.SagaTransactionID)
   439  	if !ok {
   440  		t.Fatal("not a gen.SagaTransactionID")
   441  	}
   442  
   443  	fmt.Printf("... Start new TX %v on saga1: ", TXID)
   444  	waitForResultWithValue(t, args1.sagaRes, TXID)
   445  	fmt.Printf("... Start new worker on saga1 with TX %v: ", TXID)
   446  	waitForResultWithValue(t, args1.workerRes, TXID)
   447  
   448  	fmt.Printf("... Start new TX %v on saga2: ", TXID)
   449  	waitForResultWithValue(t, args2.sagaRes, TXID)
   450  	fmt.Printf("... Start new worker on saga2 with TX %v: ", TXID)
   451  	waitForResultWithValue(t, args2.workerRes, TXID)
   452  
   453  	fmt.Printf("... Start new TX %v on saga3: ", TXID)
   454  	waitForResultWithValue(t, args3.sagaRes, TXID)
   455  	fmt.Printf("... Start new worker on saga3 with TX %v: ", TXID)
   456  	waitForResultWithValue(t, args3.workerRes, TXID)
   457  
   458  	fmt.Printf("... Cancel TX %v on saga3: ", TXID)
   459  	cancelReason = "cancel case3"
   460  	_, err = saga3_process.Direct(testSagaCancelTX{ID: TXID, Reason: cancelReason})
   461  	if err != nil {
   462  		t.Fatal(err)
   463  	}
   464  	waitForResultWithValue(t, args3.sagaRes, cancelReason)
   465  	fmt.Printf("...       saga3 cancels TX %v on its worker: ", TXID)
   466  	waitForResultWithValue(t, args3.workerRes, cancelReason)
   467  	fmt.Printf("...       cancels TX %v on saga2: ", TXID)
   468  	waitForResultWithValue(t, args2.sagaRes, cancelReason)
   469  	fmt.Printf("...       saga2 cancels TX %v on its worker: ", TXID)
   470  	waitForResultWithValue(t, args2.workerRes, cancelReason)
   471  	fmt.Printf("...       cancels TX %v on saga1: ", TXID)
   472  	waitForResultWithValue(t, args1.sagaRes, cancelReason)
   473  	fmt.Printf("...       saga1 cancels TX %v on its worker: ", TXID)
   474  	waitForResultWithValue(t, args1.workerRes, cancelReason)
   475  	//
   476  	// Case 2.D
   477  	//
   478  	fmt.Println("  Case D: Saga1 sets TrapCancel, Saga2 process/node is going down, Saga1 sends Tx to the Saga4:")
   479  
   480  	fmt.Printf("Starting node: node4GenSagaCancelCases@localhost...")
   481  	node4, _ := ergo.StartNode("node4GenSagaCancelCases@localhost", "cookies", node.Options{})
   482  
   483  	if node4 == nil {
   484  		t.Fatal("can't start node")
   485  		return
   486  	}
   487  	fmt.Println("OK")
   488  	defer node4.Stop()
   489  
   490  	args4 := testSagaCancel2Args{
   491  		workerRes: make(chan interface{}, 2),
   492  		sagaRes:   make(chan interface{}, 2),
   493  	}
   494  	fmt.Printf("Starting saga4 on node4GenSagaCancelCases@localhost...")
   495  	saga4 := &testSagaCancel2{}
   496  	saga4_process, err := node4.Spawn("saga4", gen.ProcessOptions{MailboxSize: 10000}, saga4, args4)
   497  	if err != nil {
   498  		t.Fatal(err)
   499  	}
   500  	fmt.Println("OK", saga4_process.Self())
   501  
   502  	// TrapCancel will be enabled on the Saga1 only
   503  	ValueTXID, err = saga1_process.Direct(testSagaStartTX{TrapCancel: true})
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  	TXID, ok = ValueTXID.(gen.SagaTransactionID)
   508  	if !ok {
   509  		t.Fatal("not a gen.SagaTransactionID")
   510  	}
   511  
   512  	fmt.Printf("... Start new TX %v on saga1: ", TXID)
   513  	waitForResultWithValue(t, args1.sagaRes, TXID)
   514  	fmt.Printf("... Start new worker on saga1 with TX %v: ", TXID)
   515  	waitForResultWithValue(t, args1.workerRes, TXID)
   516  
   517  	fmt.Printf("... Start new TX %v on saga2: ", TXID)
   518  	waitForResultWithValue(t, args2.sagaRes, TXID)
   519  	fmt.Printf("... Start new worker on saga2 with TX %v: ", TXID)
   520  	waitForResultWithValue(t, args2.workerRes, TXID)
   521  
   522  	fmt.Printf("... Start new TX %v on saga3: ", TXID)
   523  	waitForResultWithValue(t, args3.sagaRes, TXID)
   524  	fmt.Printf("... Start new worker on saga3 with TX %v: ", TXID)
   525  	waitForResultWithValue(t, args3.workerRes, TXID)
   526  
   527  	fmt.Printf("... Terminate saga2 process: ")
   528  	time.Sleep(200 * time.Millisecond)
   529  	saga2_process.Kill()
   530  	if err := saga2_process.WaitWithTimeout(2 * time.Second); err != nil {
   531  		t.Fatal(err)
   532  	}
   533  	fmt.Println("OK")
   534  
   535  	fmt.Printf("...       handle trapped cancelation TX %v on saga1: ", TXID)
   536  	cancelReason = fmt.Sprintf("next saga %s is down", gen.ProcessID{Name: saga2_process.Name(), Node: node2.Name()})
   537  	waitForResultWithValue(t, args1.sagaRes, cancelReason)
   538  	fmt.Printf("...       cancels TX %v on saga3: ", TXID)
   539  	cancelReason = fmt.Sprintf("parent saga %s is down", saga2_process.Self())
   540  	waitForResultWithValue(t, args3.sagaRes, cancelReason)
   541  	fmt.Printf("...       saga3 cancels TX %v on its worker: ", TXID)
   542  	waitForResultWithValue(t, args3.workerRes, cancelReason)
   543  
   544  	fmt.Printf("...       forward (trapped) canceled TX %v on saga1 to Saga4: ", TXID)
   545  	waitForResultWithValue(t, args1.sagaRes, TXID)
   546  	fmt.Printf("... Start new TX %v on saga4: ", TXID)
   547  	waitForResultWithValue(t, args4.sagaRes, TXID)
   548  	fmt.Printf("... Start new worker on saga4 with TX %v: ", TXID)
   549  	waitForResultWithValue(t, args4.workerRes, TXID)
   550  }