4
0

server_test.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package toc
  2. import (
  3. "context"
  4. "errors"
  5. "io"
  6. "log/slog"
  7. "sync"
  8. "testing"
  9. "github.com/mk6i/open-oscar-server/wire"
  10. "github.com/stretchr/testify/assert"
  11. )
  12. // ensure correct behavior during global context cancellation (server shutdown)
  13. func TestServer_handleTOCRequest_serverShutdown(t *testing.T) {
  14. wg := sync.WaitGroup{}
  15. wg.Add(1)
  16. ctx, cancel := context.WithCancel(context.Background())
  17. go func() {
  18. defer wg.Done()
  19. sv := Server{
  20. bosProxy: testOSCARProxy(t),
  21. logger: slog.Default(),
  22. }
  23. serverReader, _ := io.Pipe()
  24. fc := wire.NewFlapClient(0, serverReader, nil)
  25. closeConn := func() {
  26. _ = serverReader.Close()
  27. }
  28. me, _ := newTestTOCConn(newTestSession("me"))
  29. err := sv.handleTOCRequest(ctx, closeConn, me, fc)
  30. assert.True(t, errors.Is(err, errTOCProcessing) || errors.Is(err, errServerWrite))
  31. }()
  32. // cancel context, simulating server shutdown
  33. cancel()
  34. // wait for handleTOCRequest to return
  35. wg.Wait()
  36. }
  37. // ensure correct behavior when client TCP connection disconnects
  38. func TestServer_handleTOCRequest_clientReadDisconnect(t *testing.T) {
  39. wg := sync.WaitGroup{}
  40. wg.Add(1)
  41. serverReader, _ := io.Pipe()
  42. go func() {
  43. defer wg.Done()
  44. closeConn := func() {
  45. _ = serverReader.Close()
  46. }
  47. me, _ := newTestTOCConn(newTestSession("me"))
  48. fc := wire.NewFlapClient(0, serverReader, nil)
  49. sv := Server{
  50. bosProxy: testOSCARProxy(t),
  51. logger: slog.Default(),
  52. }
  53. err := sv.handleTOCRequest(context.Background(), closeConn, me, fc)
  54. assert.ErrorIs(t, err, errClientReq)
  55. assert.ErrorIs(t, err, io.ErrClosedPipe)
  56. }()
  57. // simulate a client TCP disconnect
  58. _ = serverReader.Close()
  59. // wait for handleTOCRequest to return
  60. wg.Wait()
  61. }
  62. // ensure correct behavior when session gets closed by another login
  63. func TestServer_handleTOCRequest_sessClose(t *testing.T) {
  64. wg := sync.WaitGroup{}
  65. wg.Add(1)
  66. me, bos := newTestTOCConn(newTestSession("me"))
  67. go func() {
  68. defer wg.Done()
  69. serverReader, _ := io.Pipe()
  70. fc := wire.NewFlapClient(0, serverReader, nil)
  71. closeConn := func() {
  72. _ = serverReader.Close()
  73. }
  74. sv := Server{
  75. bosProxy: testOSCARProxy(t),
  76. logger: slog.Default(),
  77. }
  78. err := sv.handleTOCRequest(context.Background(), closeConn, me, fc)
  79. assert.ErrorIs(t, err, errTOCProcessing)
  80. assert.ErrorIs(t, err, errDisconnect)
  81. }()
  82. // close the BOS connection, simulating another client login kicking this
  83. // session
  84. bos.Close()
  85. // wait for handleTOCRequest to return
  86. wg.Wait()
  87. }
  88. // ensure correct behavior when writing server response fails
  89. func TestServer_handleTOCRequest_replyFailure(t *testing.T) {
  90. wg := sync.WaitGroup{}
  91. wg.Add(1)
  92. serverReader, clientWriter := io.Pipe()
  93. clientReader, serverWriter := io.Pipe()
  94. go func() {
  95. defer wg.Done()
  96. closeConn := func() {
  97. _ = serverReader.Close()
  98. }
  99. me, _ := newTestTOCConn(newTestSession("me"))
  100. fc := wire.NewFlapClient(0, serverReader, serverWriter)
  101. sv := Server{
  102. bosProxy: testOSCARProxy(t),
  103. logger: slog.Default(),
  104. }
  105. err := sv.handleTOCRequest(context.Background(), closeConn, me, fc)
  106. assert.ErrorIs(t, err, errServerWrite)
  107. assert.ErrorIs(t, err, io.ErrClosedPipe)
  108. }()
  109. // simulate a failed TCP write
  110. _ = serverWriter.Close()
  111. // set up a TOC client
  112. fc := wire.NewFlapClient(0, clientReader, clientWriter)
  113. // send a TOC command
  114. err := fc.SendDataFrame([]byte(`toc_get_status`))
  115. assert.NoError(t, err)
  116. // wait for handleTOCRequest to return
  117. wg.Wait()
  118. }
  119. // ensure correct behavior when writing server response fails
  120. func TestServer_handleTOCRequest_happyPath(t *testing.T) {
  121. wg := sync.WaitGroup{}
  122. wg.Add(1)
  123. serverReader, clientWriter := io.Pipe()
  124. clientReader, serverWriter := io.Pipe()
  125. go func() {
  126. defer wg.Done()
  127. closeConn := func() {
  128. _ = serverReader.Close()
  129. }
  130. fc := wire.NewFlapClient(0, serverReader, serverWriter)
  131. sv := Server{
  132. bosProxy: testOSCARProxy(t),
  133. logger: slog.Default(),
  134. }
  135. me, _ := newTestTOCConn(newTestSession("me"))
  136. err := sv.handleTOCRequest(context.Background(), closeConn, me, fc)
  137. assert.ErrorIs(t, err, errClientReq)
  138. assert.ErrorIs(t, err, io.ErrClosedPipe)
  139. }()
  140. // set up a TOC client
  141. fc := wire.NewFlapClient(0, clientReader, clientWriter)
  142. // send a malformed TOC command to the server
  143. err := fc.SendDataFrame([]byte(`toc_get_status`))
  144. assert.NoError(t, err)
  145. // wait for the TOC response from the server
  146. frame, err := fc.ReceiveFLAP()
  147. assert.NoError(t, err)
  148. // expecting an error from TOC because the command is malformed. this
  149. // demonstrates that a command was processed by the TOC handler.
  150. assert.Contains(t, string(frame.Payload), "internal server error")
  151. // cleanly disconnect
  152. _ = serverReader.Close()
  153. // wait for handleTOCRequest to return
  154. wg.Wait()
  155. }