tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/duplex/duplex_test.go (about)

     1  package duplex
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"tractor.dev/toolkit-go/duplex/codec"
     8  	"tractor.dev/toolkit-go/duplex/fn"
     9  	"tractor.dev/toolkit-go/duplex/mux"
    10  	"tractor.dev/toolkit-go/duplex/talk"
    11  )
    12  
    13  func fatal(t *testing.T, err error) {
    14  	if err != nil {
    15  		t.Fatal(err)
    16  	}
    17  }
    18  
    19  func equal(t *testing.T, a, b any, message string) {
    20  	if a != b {
    21  		t.Log(message)
    22  		t.Fail()
    23  	}
    24  }
    25  
    26  func makeClientServer(service any) (client, server *talk.Peer, closer func()) {
    27  	c, s := mux.Pair()
    28  	client = talk.NewPeer(c, codec.CBORCodec{})
    29  	server = talk.NewPeer(s, codec.CBORCodec{})
    30  	server.Server.Handler = fn.HandlerFrom(service)
    31  	go client.Respond()
    32  	go server.Respond()
    33  	closer = func() {
    34  		client.Close()
    35  		server.Close()
    36  	}
    37  	return
    38  }
    39  
    40  type TestService struct {
    41  	T *testing.T
    42  }
    43  
    44  func (s *TestService) Hello() string {
    45  	return "Hello"
    46  }
    47  
    48  func (s *TestService) NilAnyArg(a string, v any) {
    49  	equal(s.T, v, nil, "nil any arg not nil")
    50  }
    51  
    52  func TestBasic(t *testing.T) {
    53  	c, _, closer := makeClientServer(&TestService{T: t})
    54  	defer closer()
    55  
    56  	var ret string
    57  	_, err := c.Call(context.Background(), "Hello", nil, &ret)
    58  	fatal(t, err)
    59  
    60  	equal(t, ret, "Hello", "return not Hello")
    61  }
    62  
    63  func TestNilAnyArg(t *testing.T) {
    64  	c, _, closer := makeClientServer(&TestService{T: t})
    65  	defer closer()
    66  
    67  	_, err := c.Call(context.Background(), "NilAnyArg", fn.Args{"abc", nil}, nil)
    68  	fatal(t, err)
    69  
    70  }
    71  
    72  // this test ensures the io.Pipe based mux.Pair buffers enough writes
    73  // to allow a simultaneous call or channel open, since a lockstep pipe
    74  // will deadlock unable to write the open confirm since the other end
    75  // will also be trying to write an open confirm before reading next packet
    76  func TestSimultaneousOpen(t *testing.T) {
    77  	c, s := mux.Pair()
    78  	client := talk.NewPeer(c, codec.CBORCodec{})
    79  	client.Server.Handler = fn.HandlerFrom(&TestService{T: t})
    80  	server := talk.NewPeer(s, codec.CBORCodec{})
    81  	server.Server.Handler = fn.HandlerFrom(&TestService{T: t})
    82  	go client.Respond()
    83  	go server.Respond()
    84  
    85  	cdone := make(chan bool)
    86  	sdone := make(chan bool)
    87  
    88  	go func() {
    89  		var ret any
    90  		_, err := client.Call(context.Background(), "Hello", nil, &ret)
    91  		fatal(t, err)
    92  		close(cdone)
    93  	}()
    94  
    95  	go func() {
    96  		var ret any
    97  		_, err := server.Call(context.Background(), "Hello", nil, &ret)
    98  		fatal(t, err)
    99  		close(sdone)
   100  	}()
   101  
   102  	<-sdone
   103  	<-cdone
   104  
   105  	client.Close()
   106  	server.Close()
   107  }