diff --git a/test/inst/sync.go b/test/inst/sync.go new file mode 100644 index 0000000000000000000000000000000000000000..bd35d147ea73efcf80bb56255bcf497d69eede67 --- /dev/null +++ b/test/inst/sync.go @@ -0,0 +1,7 @@ +package inst + +type Sync struct{} + +func (Sync) instruction() {} + +var _ Instruction = Sync{} diff --git a/test/runner.go b/test/runner.go index 70e1aee7ae296d6263aab34bd251865d94312b58..3ec8071ae23b7bb43855c28c9c7934f77fba6f5f 100644 --- a/test/runner.go +++ b/test/runner.go @@ -2,6 +2,7 @@ package test import ( "errors" + "fmt" "io" "pggat/lib/bouncer/bouncers/v2" @@ -16,9 +17,6 @@ import ( type Runner struct { config Config test Test - - pools map[string]*pool.Pool - control fed.Conn } func MakeRunner(config Config, test Test) Runner { @@ -28,107 +26,111 @@ func MakeRunner(config Config, test Test) Runner { } } -func (T *Runner) setup() error { - // get pools ready - if T.control != nil { - _ = T.control.Close() - } - var err error - T.control, _, err = T.config.Peer.Dial() - if err != nil { - return err - } - - for name, p := range T.pools { - delete(T.pools, name) - p.Close() - } - if T.pools == nil { - T.pools = make(map[string]*pool.Pool) - } +func (T *Runner) prepare(client *gsql.Client) []Capturer { + results := make([]Capturer, len(T.test.Instructions)) - for name, options := range T.config.Modes { - opts := options - // allowing ps sync would mess up testing - opts.ParameterStatusSync = pool.ParameterStatusSyncNone - p := pool.NewPool(opts) - p.AddRecipe("server", recipe.NewRecipe( - recipe.Options{ - Dialer: T.config.Peer, - }, - )) - T.pools[name] = p + for i, x := range T.test.Instructions { + switch v := x.(type) { + case inst.SimpleQuery: + q := packets.Query(v) + client.Do(&results[i], q.IntoPacket()) + case inst.Sync: + client.Do(&results[i], fed.NewPacket(packets.TypeSync)) + } } - return nil + return results } -func (T *Runner) run(pkts ...fed.Packet) error { - // expected - var expected Capturer - - { - var client gsql.Client - client.Do(&expected, pkts...) - if err := client.Close(); err != nil { - return err - } +func (T *Runner) runControl() ([]Capturer, error) { + control, _, err := T.config.Peer.Dial() + if err != nil { + return nil, err + } + defer func() { + _ = control.Close() + }() + + var client gsql.Client + results := T.prepare(&client) + if err = client.Close(); err != nil { + return nil, err + } - for { - p, err := client.ReadPacket(true) - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return err + for { + var p fed.Packet + p, err = client.ReadPacket(true) + if err != nil { + if errors.Is(err, io.EOF) { + break } + return nil, err + } - clientErr, serverErr := bouncers.Bounce(&client, T.control, p) - if clientErr != nil { - return clientErr - } - if serverErr != nil { - return serverErr - } + clientErr, serverErr := bouncers.Bounce(&client, control, p) + if clientErr != nil { + return nil, clientErr + } + if serverErr != nil { + return nil, serverErr } } - // actual - for name, p := range T.pools { - var result Capturer - - var client gsql.Client - client.Do(&result, pkts...) - if err := client.Close(); err != nil { - return err - } + return results, nil +} - if err := p.Serve(&client, nil, [8]byte{}); err != nil && !errors.Is(err, io.EOF) { - return err - } +func (T *Runner) runMode(options pool.Options) ([]Capturer, error) { + opts := options + // allowing ps sync would mess up testing + opts.ParameterStatusSync = pool.ParameterStatusSyncNone + p := pool.NewPool(opts) + defer p.Close() + p.AddRecipe("server", recipe.NewRecipe( + recipe.Options{ + Dialer: T.config.Peer, + }, + )) + + var client gsql.Client + results := T.prepare(&client) + if err := client.Close(); err != nil { + return nil, err + } - if err := expected.Check(&result); err != nil { - return err - } - _ = name + if err := p.Serve(&client, nil, [8]byte{}); err != nil && !errors.Is(err, io.EOF) { + return nil, err } - return nil + return results, nil } func (T *Runner) Run() error { - if err := T.setup(); err != nil { + // control + expected, err := T.runControl() + if err != nil { return err } - for _, i := range T.test.Instructions { - switch v := i.(type) { - case inst.SimpleQuery: - q := packets.Query(v) - if err := T.run(q.IntoPacket()); err != nil { + // modes + for name, mode := range T.config.Modes { + actual, err := T.runMode(mode) + if err != nil { + return err + } + + if len(expected) != len(actual) { + return fmt.Errorf("wrong number of results! expected %d but got %d", len(expected), len(actual)) + } + + for i, exp := range expected { + act := actual[i] + + if err = exp.Check(&act); err != nil { return err } } + + _ = name } return nil diff --git a/test/tester_test.go b/test/tester_test.go index 809190fcf2d16b4250dfbd89c75815b858bcaefc..a558e62a5049d3e828c27e81cc0646ac22835d0a 100644 --- a/test/tester_test.go +++ b/test/tester_test.go @@ -35,6 +35,8 @@ func TestTester(t *testing.T) { }) if err := tester.Run( tests.SimpleQuery, + tests.Transaction, + tests.Sync, ); err != nil { t.Error(err) } diff --git a/test/tests/sync.go b/test/tests/sync.go new file mode 100644 index 0000000000000000000000000000000000000000..976ee8e194186581a6a55dbecd85acf6b02922a5 --- /dev/null +++ b/test/tests/sync.go @@ -0,0 +1,15 @@ +package tests + +import ( + "pggat/test" + "pggat/test/inst" +) + +var Sync = test.Test{ + Instructions: []inst.Instruction{ + inst.Sync{}, + inst.SimpleQuery("BEGIN;"), + inst.Sync{}, + inst.SimpleQuery("END;"), + }, +} diff --git a/test/tests/transaction.go b/test/tests/transaction.go new file mode 100644 index 0000000000000000000000000000000000000000..ad7ca675af41d685ef689f92a6b107dbfb0fe089 --- /dev/null +++ b/test/tests/transaction.go @@ -0,0 +1,16 @@ +package tests + +import ( + "pggat/test" + "pggat/test/inst" +) + +var Transaction = test.Test{ + Instructions: []inst.Instruction{ + inst.SimpleQuery("BEGIN;"), + inst.SimpleQuery("select 1;"), + inst.SimpleQuery("this will fail;"), + inst.SimpleQuery("select 2;"), + inst.SimpleQuery("END;"), + }, +}