github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/streamtest/streamtest_test.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package streamtest_test
     6  
     7  import (
     8  	"bufio"
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/ethersphere/bee/v2/pkg/p2p"
    18  	"github.com/ethersphere/bee/v2/pkg/p2p/streamtest"
    19  	"github.com/ethersphere/bee/v2/pkg/swarm"
    20  	ma "github.com/multiformats/go-multiaddr"
    21  )
    22  
    23  func TestRecorder(t *testing.T) {
    24  	t.Parallel()
    25  
    26  	var answers = map[string]string{
    27  		"What is your name?":                                    "Sir Lancelot of Camelot",
    28  		"What is your quest?":                                   "To seek the Holy Grail.",
    29  		"What is your favorite color?":                          "Blue.",
    30  		"What is the air-speed velocity of an unladen swallow?": "What do you mean? An African or European swallow?",
    31  	}
    32  
    33  	recorder := streamtest.New(
    34  		streamtest.WithProtocols(
    35  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
    36  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
    37  				for {
    38  					q, err := rw.ReadString('\n')
    39  					if err != nil {
    40  						if errors.Is(err, io.EOF) {
    41  							break
    42  						}
    43  						return fmt.Errorf("read: %w", err)
    44  					}
    45  					q = strings.TrimRight(q, "\n")
    46  					if _, err = rw.WriteString(answers[q] + "\n"); err != nil {
    47  						return fmt.Errorf("write: %w", err)
    48  					}
    49  					if err := rw.Flush(); err != nil {
    50  						return fmt.Errorf("flush: %w", err)
    51  					}
    52  				}
    53  				return nil
    54  			}),
    55  		),
    56  	)
    57  
    58  	ask := func(ctx context.Context, s p2p.Streamer, address swarm.Address, questions ...string) (answers []string, err error) {
    59  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
    60  		if err != nil {
    61  			return nil, fmt.Errorf("new stream: %w", err)
    62  		}
    63  		defer stream.Close()
    64  
    65  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
    66  
    67  		for _, q := range questions {
    68  			if _, err := rw.WriteString(q + "\n"); err != nil {
    69  				return nil, fmt.Errorf("write: %w", err)
    70  			}
    71  			if err := rw.Flush(); err != nil {
    72  				return nil, fmt.Errorf("flush: %w", err)
    73  			}
    74  
    75  			a, err := rw.ReadString('\n')
    76  			if err != nil {
    77  				return nil, fmt.Errorf("read: %w", err)
    78  			}
    79  			a = strings.TrimRight(a, "\n")
    80  			answers = append(answers, a)
    81  		}
    82  		return answers, nil
    83  	}
    84  
    85  	questions := []string{"What is your name?", "What is your quest?", "What is your favorite color?"}
    86  
    87  	aa, err := ask(context.Background(), recorder, swarm.ZeroAddress, questions...)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	for i, q := range questions {
    93  		if aa[i] != answers[q] {
    94  			t.Errorf("got answer %q for question %q, want %q", aa[i], q, answers[q])
    95  		}
    96  	}
    97  
    98  	_, err = recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, "invalid stream name")
    99  	if !errors.Is(err, streamtest.ErrRecordsNotFound) {
   100  		t.Errorf("got error %v, want %v", err, streamtest.ErrRecordsNotFound)
   101  	}
   102  
   103  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	testRecords(t, records, [][2]string{
   109  		{
   110  			"What is your name?\nWhat is your quest?\nWhat is your favorite color?\n",
   111  			"Sir Lancelot of Camelot\nTo seek the Holy Grail.\nBlue.\n",
   112  		},
   113  	}, nil)
   114  }
   115  
   116  func TestRecorder_errStreamNotSupported(t *testing.T) {
   117  	t.Parallel()
   118  
   119  	r := streamtest.New()
   120  
   121  	_, err := r.NewStream(context.Background(), swarm.ZeroAddress, nil, "testing", "messages", "1.0.1")
   122  	if !errors.Is(err, streamtest.ErrStreamNotSupported) {
   123  		t.Fatalf("got error %v, want %v", err, streamtest.ErrStreamNotSupported)
   124  	}
   125  }
   126  
   127  func TestRecorder_fullcloseWithRemoteClose(t *testing.T) {
   128  	t.Parallel()
   129  
   130  	recorder := streamtest.New(
   131  		streamtest.WithProtocols(
   132  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   133  				defer stream.Close()
   134  				_, err := bufio.NewReader(stream).ReadString('\n')
   135  				return err
   136  			}),
   137  		),
   138  	)
   139  
   140  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) (err error) {
   141  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   142  		if err != nil {
   143  			return fmt.Errorf("new stream: %w", err)
   144  		}
   145  
   146  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   147  		if _, err := rw.WriteString("message\n"); err != nil {
   148  			return fmt.Errorf("write: %w", err)
   149  		}
   150  		if err := rw.Flush(); err != nil {
   151  			return fmt.Errorf("flush: %w", err)
   152  		}
   153  
   154  		return stream.FullClose()
   155  	}
   156  
   157  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   158  	if err != nil {
   159  		t.Fatal(err)
   160  	}
   161  
   162  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  
   167  	testRecords(t, records, [][2]string{
   168  		{
   169  			"message\n",
   170  		},
   171  	}, nil)
   172  }
   173  
   174  func TestRecorder_fullcloseWithoutRemoteClose(t *testing.T) {
   175  	t.Parallel()
   176  
   177  	recorder := streamtest.New(
   178  		streamtest.WithProtocols(
   179  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   180  				// don't close the stream here
   181  				// just try to read the message that it terminated with
   182  				// a new line character
   183  				_, err := bufio.NewReader(stream).ReadString('\n')
   184  				return err
   185  			}),
   186  		),
   187  	)
   188  
   189  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) (err error) {
   190  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   191  		if err != nil {
   192  			return fmt.Errorf("new stream: %w", err)
   193  		}
   194  
   195  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   196  		if _, err := rw.WriteString("message\n"); err != nil {
   197  			return fmt.Errorf("write: %w", err)
   198  		}
   199  		if err := rw.Flush(); err != nil {
   200  			return fmt.Errorf("flush: %w", err)
   201  		}
   202  
   203  		return stream.FullClose()
   204  	}
   205  
   206  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  
   211  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  
   216  	testRecords(t, records, [][2]string{
   217  		{
   218  			"message\n",
   219  		},
   220  	}, nil)
   221  }
   222  
   223  func TestRecorder_multipleParallelFullCloseAndClose(t *testing.T) {
   224  	t.Parallel()
   225  
   226  	recorder := streamtest.New(
   227  		streamtest.WithProtocols(
   228  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   229  				if _, err := bufio.NewReader(stream).ReadString('\n'); err != nil {
   230  					return err
   231  				}
   232  
   233  				return stream.FullClose()
   234  			}),
   235  		),
   236  	)
   237  
   238  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) (err error) {
   239  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   240  		if err != nil {
   241  			return fmt.Errorf("new stream: %w", err)
   242  		}
   243  
   244  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   245  		if _, err := rw.WriteString("message\n"); err != nil {
   246  			return fmt.Errorf("write: %w", err)
   247  		}
   248  		if err := rw.Flush(); err != nil {
   249  			return fmt.Errorf("flush: %w", err)
   250  		}
   251  
   252  		return stream.FullClose()
   253  	}
   254  
   255  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  
   260  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   261  	if err != nil {
   262  		t.Fatal(err)
   263  	}
   264  
   265  	testRecords(t, records, [][2]string{
   266  		{
   267  			"message\n",
   268  		},
   269  	}, nil)
   270  }
   271  
   272  func TestRecorder_closeAfterPartialWrite(t *testing.T) {
   273  	t.Parallel()
   274  
   275  	recorder := streamtest.New(
   276  		streamtest.WithProtocols(
   277  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   278  				// just try to read the message that it terminated with
   279  				// a new line character
   280  				_, err := bufio.NewReader(stream).ReadString('\n')
   281  				return err
   282  			}),
   283  		),
   284  	)
   285  
   286  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) (err error) {
   287  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   288  		if err != nil {
   289  			return fmt.Errorf("new stream: %w", err)
   290  		}
   291  		defer stream.Close()
   292  
   293  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   294  
   295  		// write a message, but do not write a new line character for handler to
   296  		// know that it is complete
   297  		if _, err := rw.WriteString("unterminated message"); err != nil {
   298  			return fmt.Errorf("write: %w", err)
   299  		}
   300  		if err := rw.Flush(); err != nil {
   301  			return fmt.Errorf("flush: %w", err)
   302  		}
   303  
   304  		// deliberately close the stream before the new line character is
   305  		// written to the stream
   306  		if err := stream.Close(); err != nil {
   307  			return err
   308  		}
   309  
   310  		// stream should be closed and write should return err
   311  		if _, err := rw.WriteString("expect err message"); err != nil {
   312  			return fmt.Errorf("write: %w", err)
   313  		}
   314  
   315  		if err := rw.Flush(); err == nil {
   316  			return fmt.Errorf("expected err")
   317  		}
   318  
   319  		return nil
   320  	}
   321  
   322  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  
   327  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  
   332  	testRecords(t, records, [][2]string{
   333  		{
   334  			"unterminated message",
   335  			"",
   336  		},
   337  	}, nil)
   338  }
   339  
   340  func TestRecorder_resetAfterPartialWrite(t *testing.T) {
   341  	t.Parallel()
   342  
   343  	recorder := streamtest.New(
   344  		streamtest.WithProtocols(
   345  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   346  				// just try to read the message that it terminated with
   347  				// a new line character
   348  				_, err := bufio.NewReader(stream).ReadString('\n')
   349  				return err
   350  			}),
   351  		),
   352  	)
   353  
   354  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) (err error) {
   355  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   356  		if err != nil {
   357  			return fmt.Errorf("new stream: %w", err)
   358  		}
   359  		defer stream.Close()
   360  
   361  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   362  
   363  		// write a message, but do not write a new line character for handler to
   364  		// know that it is complete
   365  		if _, err := rw.WriteString("unterminated message"); err != nil {
   366  			return fmt.Errorf("write: %w", err)
   367  		}
   368  		if err := rw.Flush(); err != nil {
   369  			return fmt.Errorf("flush: %w", err)
   370  		}
   371  
   372  		// deliberately reset the stream before the new line character is
   373  		// written to the stream
   374  		if err := stream.Reset(); err != nil {
   375  			return err
   376  		}
   377  
   378  		// stream should be closed and read should return streamtest.ErrStreamClosed
   379  		if _, err := rw.ReadString('\n'); !errors.Is(err, streamtest.ErrStreamClosed) {
   380  			return fmt.Errorf("got error %w, want %w", err, streamtest.ErrStreamClosed)
   381  		}
   382  
   383  		return nil
   384  	}
   385  
   386  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   387  	if err != nil {
   388  		t.Fatal(err)
   389  	}
   390  
   391  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  
   396  	testRecords(t, records, [][2]string{
   397  		{
   398  			"unterminated message",
   399  			"",
   400  		},
   401  	}, nil)
   402  }
   403  
   404  func TestRecorder_withMiddlewares(t *testing.T) {
   405  	t.Parallel()
   406  
   407  	recorder := streamtest.New(
   408  		streamtest.WithProtocols(
   409  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   410  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   411  
   412  				if _, err := rw.ReadString('\n'); err != nil {
   413  					return err
   414  				}
   415  
   416  				if _, err := rw.WriteString("handler, "); err != nil {
   417  					return err
   418  				}
   419  				if err := rw.Flush(); err != nil {
   420  					return err
   421  				}
   422  
   423  				return nil
   424  			}),
   425  		),
   426  		streamtest.WithMiddlewares(
   427  			func(h p2p.HandlerFunc) p2p.HandlerFunc {
   428  				return func(ctx context.Context, peer p2p.Peer, stream p2p.Stream) error {
   429  					if err := h(ctx, peer, stream); err != nil {
   430  						return err
   431  					}
   432  					// close stream after all previous middlewares wrote to it
   433  					// so that the receiving peer can get all the post messages
   434  					return stream.Close()
   435  				}
   436  			},
   437  			func(h p2p.HandlerFunc) p2p.HandlerFunc {
   438  				return func(ctx context.Context, peer p2p.Peer, stream p2p.Stream) error {
   439  					if _, err := stream.Write([]byte("pre 1, ")); err != nil {
   440  						return err
   441  					}
   442  					if err := h(ctx, peer, stream); err != nil {
   443  						return err
   444  					}
   445  					if _, err := stream.Write([]byte("post 1, ")); err != nil {
   446  						return err
   447  					}
   448  					return nil
   449  				}
   450  			},
   451  			func(h p2p.HandlerFunc) p2p.HandlerFunc {
   452  				return func(ctx context.Context, peer p2p.Peer, stream p2p.Stream) error {
   453  					if _, err := stream.Write([]byte("pre 2, ")); err != nil {
   454  						return err
   455  					}
   456  					if err := h(ctx, peer, stream); err != nil {
   457  						return err
   458  					}
   459  					if _, err := stream.Write([]byte("post 2, ")); err != nil {
   460  						return err
   461  					}
   462  					return nil
   463  				}
   464  			},
   465  		),
   466  		streamtest.WithMiddlewares(
   467  			func(h p2p.HandlerFunc) p2p.HandlerFunc {
   468  				return func(ctx context.Context, peer p2p.Peer, stream p2p.Stream) error {
   469  					if _, err := stream.Write([]byte("pre 3, ")); err != nil {
   470  						return err
   471  					}
   472  					if err := h(ctx, peer, stream); err != nil {
   473  						return err
   474  					}
   475  					if _, err := stream.Write([]byte("post 3, ")); err != nil {
   476  						return err
   477  					}
   478  					return nil
   479  				}
   480  			},
   481  		),
   482  	)
   483  
   484  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) error {
   485  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   486  		if err != nil {
   487  			return fmt.Errorf("new stream: %w", err)
   488  		}
   489  		defer stream.Close()
   490  
   491  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   492  
   493  		if _, err := rw.WriteString("test\n"); err != nil {
   494  			return err
   495  		}
   496  		if err := rw.Flush(); err != nil {
   497  			return err
   498  		}
   499  		_, err = io.ReadAll(rw)
   500  		return err
   501  	}
   502  
   503  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  
   508  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   509  	if err != nil {
   510  		t.Fatal(err)
   511  	}
   512  
   513  	testRecords(t, records, [][2]string{
   514  		{
   515  			"test\n",
   516  			"pre 1, pre 2, pre 3, handler, post 3, post 2, post 1, ",
   517  		},
   518  	}, nil)
   519  }
   520  
   521  func TestRecorder_recordErr(t *testing.T) {
   522  	t.Parallel()
   523  
   524  	testErr := errors.New("test error")
   525  
   526  	recorder := streamtest.New(
   527  		streamtest.WithProtocols(
   528  			newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   529  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   530  				defer stream.Close()
   531  
   532  				if _, err := rw.ReadString('\n'); err != nil {
   533  					return err
   534  				}
   535  
   536  				if _, err := rw.WriteString("resp\n"); err != nil {
   537  					return err
   538  				}
   539  				if err := rw.Flush(); err != nil {
   540  					return err
   541  				}
   542  
   543  				return testErr
   544  			}),
   545  		),
   546  	)
   547  
   548  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) (err error) {
   549  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   550  		if err != nil {
   551  			return fmt.Errorf("new stream: %w", err)
   552  		}
   553  		defer stream.Close()
   554  
   555  		if _, err = stream.Write([]byte("req\n")); err != nil {
   556  			return err
   557  		}
   558  
   559  		_, err = io.ReadAll(stream)
   560  		return err
   561  	}
   562  
   563  	err := request(context.Background(), recorder, swarm.ZeroAddress)
   564  	if err != nil {
   565  		t.Fatal(err)
   566  	}
   567  
   568  	records, err := recorder.Records(swarm.ZeroAddress, testProtocolName, testProtocolVersion, testStreamName)
   569  	if err != nil {
   570  		t.Fatal(err)
   571  	}
   572  
   573  	testRecords(t, records, [][2]string{
   574  		{
   575  			"req\n",
   576  			"resp\n",
   577  		},
   578  	}, testErr)
   579  }
   580  
   581  func TestRecorder_withPeerProtocols(t *testing.T) {
   582  	t.Parallel()
   583  
   584  	peer1 := swarm.MustParseHexAddress("1000000000000000000000000000000000000000000000000000000000000000")
   585  	peer2 := swarm.MustParseHexAddress("2000000000000000000000000000000000000000000000000000000000000000")
   586  	recorder := streamtest.New(
   587  		streamtest.WithPeerProtocols(map[string]p2p.ProtocolSpec{
   588  			peer1.String(): newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   589  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   590  
   591  				if _, err := rw.ReadString('\n'); err != nil {
   592  					return err
   593  				}
   594  				if _, err := rw.WriteString("handler 1\n"); err != nil {
   595  					return err
   596  				}
   597  				if err := rw.Flush(); err != nil {
   598  					return err
   599  				}
   600  
   601  				return nil
   602  			}),
   603  			peer2.String(): newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   604  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   605  
   606  				if _, err := rw.ReadString('\n'); err != nil {
   607  					return err
   608  				}
   609  				if _, err := rw.WriteString("handler 2\n"); err != nil {
   610  					return err
   611  				}
   612  				if err := rw.Flush(); err != nil {
   613  					return err
   614  				}
   615  
   616  				return nil
   617  			}),
   618  		}),
   619  	)
   620  
   621  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) error {
   622  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   623  		if err != nil {
   624  			return fmt.Errorf("new stream: %w", err)
   625  		}
   626  		defer stream.Close()
   627  
   628  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   629  
   630  		if _, err := rw.WriteString("req\n"); err != nil {
   631  			return err
   632  		}
   633  		if err := rw.Flush(); err != nil {
   634  			return err
   635  		}
   636  		_, err = rw.ReadString('\n')
   637  		return err
   638  	}
   639  
   640  	err := request(context.Background(), recorder, peer1)
   641  	if err != nil {
   642  		t.Fatal(err)
   643  	}
   644  
   645  	records, err := recorder.Records(peer1, testProtocolName, testProtocolVersion, testStreamName)
   646  	if err != nil {
   647  		t.Fatal(err)
   648  	}
   649  
   650  	testRecords(t, records, [][2]string{
   651  		{
   652  			"req\n",
   653  			"handler 1\n",
   654  		},
   655  	}, nil)
   656  
   657  	err = request(context.Background(), recorder, peer2)
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  
   662  	records, err = recorder.Records(peer2, testProtocolName, testProtocolVersion, testStreamName)
   663  	if err != nil {
   664  		t.Fatal(err)
   665  	}
   666  
   667  	testRecords(t, records, [][2]string{
   668  		{
   669  			"req\n",
   670  			"handler 2\n",
   671  		},
   672  	}, nil)
   673  }
   674  
   675  func TestRecorder_withStreamError(t *testing.T) {
   676  	t.Parallel()
   677  
   678  	peer1 := swarm.MustParseHexAddress("1000000000000000000000000000000000000000000000000000000000000000")
   679  	peer2 := swarm.MustParseHexAddress("2000000000000000000000000000000000000000000000000000000000000000")
   680  	testErr := errors.New("dummy stream error")
   681  	recorder := streamtest.New(
   682  		streamtest.WithPeerProtocols(map[string]p2p.ProtocolSpec{
   683  			peer1.String(): newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   684  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   685  
   686  				if _, err := rw.ReadString('\n'); err != nil {
   687  					return err
   688  				}
   689  				if _, err := rw.WriteString("handler 1\n"); err != nil {
   690  					return err
   691  				}
   692  				if err := rw.Flush(); err != nil {
   693  					return err
   694  				}
   695  
   696  				return nil
   697  			}),
   698  			peer2.String(): newTestProtocol(func(_ context.Context, peer p2p.Peer, stream p2p.Stream) error {
   699  				rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   700  
   701  				if _, err := rw.ReadString('\n'); err != nil {
   702  					return err
   703  				}
   704  				if _, err := rw.WriteString("handler 2\n"); err != nil {
   705  					return err
   706  				}
   707  				if err := rw.Flush(); err != nil {
   708  					return err
   709  				}
   710  
   711  				return nil
   712  			}),
   713  		}),
   714  		streamtest.WithStreamError(func(addr swarm.Address, _, _, _ string) error {
   715  			if addr.String() == peer1.String() {
   716  				return testErr
   717  			}
   718  			return nil
   719  		}),
   720  	)
   721  
   722  	request := func(ctx context.Context, s p2p.Streamer, address swarm.Address) error {
   723  		stream, err := s.NewStream(ctx, address, nil, testProtocolName, testProtocolVersion, testStreamName)
   724  		if err != nil {
   725  			return fmt.Errorf("new stream: %w", err)
   726  		}
   727  		defer stream.Close()
   728  
   729  		rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
   730  
   731  		if _, err := rw.WriteString("req\n"); err != nil {
   732  			return err
   733  		}
   734  		if err := rw.Flush(); err != nil {
   735  			return err
   736  		}
   737  		_, err = rw.ReadString('\n')
   738  		return err
   739  	}
   740  
   741  	err := request(context.Background(), recorder, peer1)
   742  	if err == nil {
   743  		t.Fatal("expected error on NewStream for peer")
   744  	}
   745  
   746  	err = request(context.Background(), recorder, peer2)
   747  	if err != nil {
   748  		t.Fatal(err)
   749  	}
   750  
   751  	records, err := recorder.Records(peer2, testProtocolName, testProtocolVersion, testStreamName)
   752  	if err != nil {
   753  		t.Fatal(err)
   754  	}
   755  
   756  	testRecords(t, records, [][2]string{
   757  		{
   758  			"req\n",
   759  			"handler 2\n",
   760  		},
   761  	}, nil)
   762  }
   763  
   764  func TestRecorder_ping(t *testing.T) {
   765  	t.Parallel()
   766  
   767  	testAddr, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
   768  
   769  	rec := streamtest.New()
   770  
   771  	_, err := rec.Ping(context.Background(), testAddr)
   772  	if err != nil {
   773  		t.Fatalf("unable to ping err: %s", err.Error())
   774  	}
   775  
   776  	rec2 := streamtest.New(
   777  		streamtest.WithPingErr(func(_ ma.Multiaddr) (rtt time.Duration, err error) {
   778  			return rtt, errors.New("fail")
   779  		}),
   780  	)
   781  
   782  	_, err = rec2.Ping(context.Background(), testAddr)
   783  	if err == nil {
   784  		t.Fatal("expected ping err")
   785  	}
   786  }
   787  
   788  const (
   789  	testProtocolName    = "testing"
   790  	testProtocolVersion = "1.0.1"
   791  	testStreamName      = "messages"
   792  )
   793  
   794  func newTestProtocol(h p2p.HandlerFunc) p2p.ProtocolSpec {
   795  	return p2p.ProtocolSpec{
   796  		Name:    testProtocolName,
   797  		Version: testProtocolVersion,
   798  		StreamSpecs: []p2p.StreamSpec{
   799  			{
   800  				Name:    testStreamName,
   801  				Handler: h,
   802  			},
   803  		},
   804  	}
   805  }
   806  
   807  func testRecords(t *testing.T, records []*streamtest.Record, want [][2]string, wantErr error) {
   808  	t.Helper()
   809  
   810  	lr := len(records)
   811  	lw := len(want)
   812  	if lr != lw {
   813  		t.Fatalf("got %v records, want %v", lr, lw)
   814  	}
   815  
   816  	for i := 0; i < lr; i++ {
   817  		record := records[i]
   818  
   819  		if err := record.Err(); !errors.Is(err, wantErr) {
   820  			t.Fatalf("got error from record %v, want %v", err, wantErr)
   821  		}
   822  
   823  		w := want[i]
   824  
   825  		gotIn := string(record.In())
   826  		if gotIn != w[0] {
   827  			t.Errorf("got stream in %q, want %q", gotIn, w[0])
   828  		}
   829  
   830  		gotOut := string(record.Out())
   831  		if gotOut != w[1] {
   832  			t.Errorf("got stream out %q, want %q", gotOut, w[1])
   833  		}
   834  	}
   835  }