github.com/jackc/pgx/v5@v5.5.5/pgproto3/frontend_test.go (about) 1 package pgproto3_test 2 3 import ( 4 "io" 5 "testing" 6 7 "github.com/jackc/pgx/v5/pgproto3" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 ) 11 12 type interruptReader struct { 13 chunks [][]byte 14 } 15 16 func (ir *interruptReader) Read(p []byte) (n int, err error) { 17 if len(ir.chunks) == 0 { 18 return 0, io.EOF 19 } 20 21 n = copy(p, ir.chunks[0]) 22 if n != len(ir.chunks[0]) { 23 panic("this test reader doesn't support partial reads of chunks") 24 } 25 26 ir.chunks = ir.chunks[1:] 27 28 return n, nil 29 } 30 31 func (ir *interruptReader) push(p []byte) { 32 ir.chunks = append(ir.chunks, p) 33 } 34 35 func TestFrontendReceiveInterrupted(t *testing.T) { 36 t.Parallel() 37 38 server := &interruptReader{} 39 server.push([]byte{'Z', 0, 0, 0, 5}) 40 41 frontend := pgproto3.NewFrontend(server, nil) 42 43 msg, err := frontend.Receive() 44 if err == nil { 45 t.Fatal("expected err") 46 } 47 if msg != nil { 48 t.Fatalf("did not expect msg, but %v", msg) 49 } 50 51 server.push([]byte{'I'}) 52 53 msg, err = frontend.Receive() 54 if err != nil { 55 t.Fatal(err) 56 } 57 if msg, ok := msg.(*pgproto3.ReadyForQuery); !ok || msg.TxStatus != 'I' { 58 t.Fatalf("unexpected msg: %v", msg) 59 } 60 } 61 62 func TestFrontendReceiveUnexpectedEOF(t *testing.T) { 63 t.Parallel() 64 65 server := &interruptReader{} 66 server.push([]byte{'Z', 0, 0, 0, 5}) 67 68 frontend := pgproto3.NewFrontend(server, nil) 69 70 msg, err := frontend.Receive() 71 if err == nil { 72 t.Fatal("expected err") 73 } 74 if msg != nil { 75 t.Fatalf("did not expect msg, but %v", msg) 76 } 77 78 msg, err = frontend.Receive() 79 assert.Nil(t, msg) 80 assert.Equal(t, io.ErrUnexpectedEOF, err) 81 } 82 83 func TestErrorResponse(t *testing.T) { 84 t.Parallel() 85 86 want := &pgproto3.ErrorResponse{ 87 Severity: "ERROR", 88 SeverityUnlocalized: "ERROR", 89 Message: `column "foo" does not exist`, 90 File: "parse_relation.c", 91 Code: "42703", 92 Position: 8, 93 Line: 3513, 94 Routine: "errorMissingColumn", 95 } 96 97 raw := []byte{ 98 'E', 0, 0, 0, 'f', 99 'S', 'E', 'R', 'R', 'O', 'R', 0, 100 'V', 'E', 'R', 'R', 'O', 'R', 0, 101 'C', '4', '2', '7', '0', '3', 0, 102 'M', 'c', 'o', 'l', 'u', 'm', 'n', 32, '"', 'f', 'o', 'o', '"', 32, 'd', 'o', 'e', 's', 32, 'n', 'o', 't', 32, 'e', 'x', 'i', 's', 't', 0, 103 'P', '8', 0, 104 'F', 'p', 'a', 'r', 's', 'e', '_', 'r', 'e', 'l', 'a', 't', 'i', 'o', 'n', '.', 'c', 0, 105 'L', '3', '5', '1', '3', 0, 106 'R', 'e', 'r', 'r', 'o', 'r', 'M', 'i', 's', 's', 'i', 'n', 'g', 'C', 'o', 'l', 'u', 'm', 'n', 0, 0, 107 } 108 109 server := &interruptReader{} 110 server.push(raw) 111 112 frontend := pgproto3.NewFrontend(server, nil) 113 114 got, err := frontend.Receive() 115 require.NoError(t, err) 116 assert.Equal(t, want, got) 117 }