diff --git a/cmd/test/main.go b/cmd/test/main.go
deleted file mode 100644
index 4a16d27c33b6198654a27f2f3af2f26f7967800d..0000000000000000000000000000000000000000
--- a/cmd/test/main.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package main
-
-import (
-	"flag"
-
-	"pggat/test"
-)
-
-func main() {
-	var config test.Config
-
-	flag.StringVar(&config.TestsPath, "path", "test/tests", "path to the tests to run")
-
-	flag.BoolVar(&config.Offline, "offline", false, "if true, existing test results will be used")
-
-	flag.StringVar(&config.Host, "host", "localhost", "postgres host")
-	flag.IntVar(&config.Port, "port", 5432, "postgres port")
-	flag.StringVar(&config.Database, "database", "pggat", "postgres database")
-	flag.StringVar(&config.User, "user", "postgres", "postgres user")
-	flag.StringVar(&config.Password, "password", "password", "postgres password")
-
-	flag.Parse()
-
-	tester := test.NewTester(config)
-	if err := tester.Run(); err != nil {
-		panic(err)
-	}
-}
diff --git a/go.mod b/go.mod
index e1e15f63976f41248f5321b0adfb60ce251a5679..77a862c54397cfe4a8ef925eefa726992553b626 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,6 @@ require (
 	github.com/google/uuid v1.3.0
 	github.com/xdg-go/scram v1.1.2
 	github.com/zalando/postgres-operator v1.10.1
-	k8s.io/api v0.27.4
 	k8s.io/apimachinery v0.27.4
 	k8s.io/client-go v0.27.4
 	tuxpa.in/a/zlog v1.61.0
@@ -27,18 +26,24 @@ require (
 	github.com/google/gnostic v0.5.7-v3refs // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/gofuzz v1.1.0 // indirect
+	github.com/imdario/mergo v0.3.6 // indirect
 	github.com/joho/godotenv v1.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/kr/text v0.2.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/rs/zerolog v1.28.0 // indirect
+	github.com/sirupsen/logrus v1.9.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
+	golang.org/x/crypto v0.8.0 // indirect
 	golang.org/x/net v0.9.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
 	golang.org/x/sys v0.7.0 // indirect
@@ -50,6 +55,8 @@ require (
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	k8s.io/api v0.27.4 // indirect
+	k8s.io/apiextensions-apiserver v0.25.9 // indirect
 	k8s.io/klog/v2 v2.90.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
 	k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
diff --git a/go.sum b/go.sum
index 128e84c04f4bc5b528f122355f3d70c144c8a4ff..ed2761da4585ae19431608ce88aa6ae96794ec76 100644
--- a/go.sum
+++ b/go.sum
@@ -138,6 +138,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
+github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
 github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -167,6 +169,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI=
+github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk=
@@ -180,7 +184,10 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
 github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
 github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -188,6 +195,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
@@ -216,6 +224,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -328,6 +338,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
@@ -503,6 +514,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs=
 k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y=
+k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8=
+k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M=
 k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs=
 k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
 k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk=
diff --git a/lib/bouncer/backends/v0/accept.go b/lib/bouncer/backends/v0/accept.go
index 3cc415b592c595fe7f46b5a95f9cc1f6a334e2f5..cc9fb2a7449e7af8702efe948bbb5ae84aa54756 100644
--- a/lib/bouncer/backends/v0/accept.go
+++ b/lib/bouncer/backends/v0/accept.go
@@ -3,6 +3,7 @@ package backends
 import (
 	"crypto/tls"
 	"errors"
+	"io"
 
 	"pggat/lib/auth"
 	"pggat/lib/fed"
@@ -242,8 +243,13 @@ func enableSSL(server fed.Conn, config *tls.Config) (bool, error) {
 		return false, err
 	}
 
+	byteReader, ok := server.(io.ByteReader)
+	if !ok {
+		return false, errors.New("server must be io.ByteReader to enable ssl")
+	}
+
 	// read byte to see if ssl is allowed
-	yn, err := server.ReadByte()
+	yn, err := byteReader.ReadByte()
 	if err != nil {
 		return false, err
 	}
@@ -253,7 +259,12 @@ func enableSSL(server fed.Conn, config *tls.Config) (bool, error) {
 		return false, nil
 	}
 
-	if err = server.EnableSSLClient(config); err != nil {
+	sslClient, ok := server.(fed.SSLClient)
+	if !ok {
+		return false, errors.New("server must be fed.SSLClient to enable ssl")
+	}
+
+	if err = sslClient.EnableSSLClient(config); err != nil {
 		return false, err
 	}
 
diff --git a/lib/bouncer/frontends/v0/accept.go b/lib/bouncer/frontends/v0/accept.go
index 2e1d2cb838ff995a76ff5d25824db01e912d3cd0..ef47bd9d0667a94e8d0b3c65c55af9922ff2a3ca 100644
--- a/lib/bouncer/frontends/v0/accept.go
+++ b/lib/bouncer/frontends/v0/accept.go
@@ -2,6 +2,7 @@ package frontends
 
 import (
 	"fmt"
+	"io"
 	"strings"
 
 	"pggat/lib/fed"
@@ -48,24 +49,54 @@ func startup0(
 			done = true
 			return
 		case 5679:
+			byteWriter, ok := conn.(io.ByteWriter)
+			if !ok {
+				err = perror.New(
+					perror.FATAL,
+					perror.FeatureNotSupported,
+					"SSL is not supported",
+				)
+				return
+			}
+
 			// ssl is not enabled
 			if options.SSLConfig == nil {
-				err = perror.Wrap(conn.WriteByte('N'))
+				err = perror.Wrap(byteWriter.WriteByte('N'))
+				return
+			}
+
+			sslServer, ok := conn.(fed.SSLServer)
+			if !ok {
+				err = perror.New(
+					perror.FATAL,
+					perror.FeatureNotSupported,
+					"SSL is not supported",
+				)
 				return
 			}
 
 			// do ssl
-			if err = perror.Wrap(conn.WriteByte('S')); err != nil {
+			if err = perror.Wrap(byteWriter.WriteByte('S')); err != nil {
 				return
 			}
-			if err = perror.Wrap(conn.EnableSSLServer(options.SSLConfig)); err != nil {
+			if err = perror.Wrap(sslServer.EnableSSLServer(options.SSLConfig)); err != nil {
 				return
 			}
 			params.SSLEnabled = true
 			return
 		case 5680:
+			byteWriter, ok := conn.(io.ByteWriter)
+			if !ok {
+				err = perror.New(
+					perror.FATAL,
+					perror.FeatureNotSupported,
+					"GSSAPI is not supported",
+				)
+				return
+			}
+
 			// GSSAPI is not supported yet
-			err = perror.Wrap(conn.WriteByte('N'))
+			err = perror.Wrap(byteWriter.WriteByte('N'))
 			return
 		default:
 			err = perror.New(
diff --git a/lib/fed/conn.go b/lib/fed/conn.go
index a3270429248775b410d68e183899e552f493f666..e9b640bc63f31b0f620f0dd905d98f7b7b5893ce 100644
--- a/lib/fed/conn.go
+++ b/lib/fed/conn.go
@@ -10,9 +10,6 @@ import (
 type Conn interface {
 	ReadWriter
 
-	EnableSSLClient(config *tls.Config) error
-	EnableSSLServer(config *tls.Config) error
-
 	Close() error
 }
 
@@ -158,3 +155,7 @@ func (T *netConn) Close() error {
 }
 
 var _ Conn = (*netConn)(nil)
+var _ SSLServer = (*netConn)(nil)
+var _ SSLClient = (*netConn)(nil)
+var _ io.ByteReader = (*netConn)(nil)
+var _ io.ByteWriter = (*netConn)(nil)
diff --git a/lib/fed/readwriter.go b/lib/fed/readwriter.go
index b606c0712ffcae90738213cdd602f121eb46ae0c..1f8b23c2d02fdace07310098f7a1c465fae8058c 100644
--- a/lib/fed/readwriter.go
+++ b/lib/fed/readwriter.go
@@ -1,12 +1,10 @@
 package fed
 
 type Reader interface {
-	ReadByte() (byte, error)
 	ReadPacket(typed bool) (Packet, error)
 }
 
 type Writer interface {
-	WriteByte(byte) error
 	WritePacket(Packet) error
 }
 
diff --git a/lib/fed/ssl.go b/lib/fed/ssl.go
new file mode 100644
index 0000000000000000000000000000000000000000..580d83f983f4a5696610a2b643a5f8c9dec0a3e2
--- /dev/null
+++ b/lib/fed/ssl.go
@@ -0,0 +1,11 @@
+package fed
+
+import "crypto/tls"
+
+type SSLClient interface {
+	EnableSSLClient(config *tls.Config) error
+}
+
+type SSLServer interface {
+	EnableSSLServer(config *tls.Config) error
+}
diff --git a/lib/middleware/interceptor/interceptor.go b/lib/middleware/interceptor/interceptor.go
index 89d29e97e9cb8085543e6dcfbca01dae9a0eed89..b87616f32c5ac94139bdd8a2dbd379aa32ee2c70 100644
--- a/lib/middleware/interceptor/interceptor.go
+++ b/lib/middleware/interceptor/interceptor.go
@@ -1,8 +1,6 @@
 package interceptor
 
 import (
-	"crypto/tls"
-
 	"pggat/lib/fed"
 	"pggat/lib/middleware"
 )
@@ -25,18 +23,6 @@ func NewInterceptor(rw fed.Conn, middlewares ...middleware.Middleware) *Intercep
 	}
 }
 
-func (T *Interceptor) EnableSSLClient(config *tls.Config) error {
-	return T.rw.EnableSSLClient(config)
-}
-
-func (T *Interceptor) EnableSSLServer(config *tls.Config) error {
-	return T.rw.EnableSSLServer(config)
-}
-
-func (T *Interceptor) ReadByte() (byte, error) {
-	return T.rw.ReadByte()
-}
-
 func (T *Interceptor) ReadPacket(typed bool) (fed.Packet, error) {
 outer:
 	for {
@@ -60,10 +46,6 @@ outer:
 	}
 }
 
-func (T *Interceptor) WriteByte(b byte) error {
-	return T.rw.WriteByte(b)
-}
-
 func (T *Interceptor) WritePacket(packet fed.Packet) error {
 	for _, mw := range T.middlewares {
 		T.context.reset()
diff --git a/test/config.go b/test/config.go
index 957d96c68ed24c67ad4385e22ff1558d521669f1..fe864abfdfd9d818e6afbb2391da56aa230ac5fe 100644
--- a/test/config.go
+++ b/test/config.go
@@ -1,13 +1,9 @@
 package test
 
-type Config struct {
-	TestsPath string
-
-	Offline bool
+import (
+	"pggat/lib/gat/pool/dialer"
+)
 
-	Host     string
-	Port     int
-	Database string
-	User     string
-	Password string
+type Config struct {
+	Peer dialer.Dialer
 }
diff --git a/test/inst/instruction.go b/test/inst/instruction.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ee9fb858efe4b8d352c296d9e73c2e8b50f59a0
--- /dev/null
+++ b/test/inst/instruction.go
@@ -0,0 +1,5 @@
+package inst
+
+type Instruction interface {
+	instruction()
+}
diff --git a/test/inst/simplequery.go b/test/inst/simplequery.go
new file mode 100644
index 0000000000000000000000000000000000000000..25ad59685b54b1f86ddb5637945fec12a30159ff
--- /dev/null
+++ b/test/inst/simplequery.go
@@ -0,0 +1,7 @@
+package inst
+
+type SimpleQuery string
+
+func (SimpleQuery) instruction() {}
+
+var _ Instruction = SimpleQuery("")
diff --git a/test/test.go b/test/test.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ef8748796644fe518da51c6de763fb87ce20961
--- /dev/null
+++ b/test/test.go
@@ -0,0 +1,8 @@
+package test
+
+import "pggat/test/inst"
+
+type Test struct {
+	Parallel     bool
+	Instructions []inst.Instruction
+}
diff --git a/test/tester.go b/test/tester.go
index 7b6f1ecb0e7d57423007a14818335e6c69ebd0c4..11c7b877be64a61eeb293b664914cf4e359f5af5 100644
--- a/test/tester.go
+++ b/test/tester.go
@@ -1,14 +1,9 @@
 package test
 
 import (
-	"bufio"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-
 	"tuxpa.in/a/zlog/log"
+
+	"pggat/test/inst"
 )
 
 type Tester struct {
@@ -21,65 +16,21 @@ func NewTester(config Config) *Tester {
 	}
 }
 
-func (T *Tester) Run() error {
-	dirEntries, err := os.ReadDir(T.config.TestsPath)
-	if err != nil {
-		return err
-	}
-
-	for _, dirEntry := range dirEntries {
-		if dirEntry.IsDir() {
-			continue
-		}
-		path := filepath.Join(T.config.TestsPath, dirEntry.Name())
-		log.Printf(`Running test "%s"`, path)
-
-		file, err := os.Open(path)
-		if err != nil {
-			return err
-		}
-
-		scanner := bufio.NewScanner(file)
-		for scanner.Scan() {
-			line := scanner.Text()
-
-			fields := strings.Fields(line)
-			if len(fields) == 0 {
-				continue
-			}
-
-			instruction := fields[0]
-			arguments := make([]any, 0, len(fields)-1)
-			for _, argString := range fields[1:] {
-				var arg any
-				switch {
-				case argString == "true":
-					arg = true
-				case argString == "false":
-					arg = false
-				case strings.HasPrefix(argString, `"`), strings.HasPrefix(argString, "`"):
-					log.Printf("unquote %s", argString)
-					arg, err = strconv.Unquote(argString)
-					if err != nil {
-						return err
-					}
-				default:
-					return fmt.Errorf(`unknown argument "%s"`, argString)
-				}
-				arguments = append(arguments, arg)
-			}
-			log.Print(instruction, " ", arguments)
-		}
-		if err = scanner.Err(); err != nil {
-			return err
+func (T *Tester) run(test Test) error {
+	for _, v := range test.Instructions {
+		switch i := v.(type) {
+		case inst.SimpleQuery:
+			log.Println("run", i)
 		}
+	}
+	return nil // TODO(garet)
+}
 
-		if err = file.Close(); err != nil {
+func (T *Tester) Run(tests ...Test) error {
+	for _, test := range tests {
+		if err := T.run(test); err != nil {
 			return err
 		}
-
-		log.Print("OK")
 	}
-
 	return nil
 }
diff --git a/test/tester_test.go b/test/tester_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c351bbaf5a45b2cb1c87ff7f3b70d26898f0dce
--- /dev/null
+++ b/test/tester_test.go
@@ -0,0 +1,30 @@
+package test_test
+
+import (
+	"testing"
+
+	"pggat/lib/auth/credentials"
+	"pggat/lib/bouncer/backends/v0"
+	"pggat/lib/gat/pool/dialer"
+	"pggat/test"
+	"pggat/test/tests"
+)
+
+func TestTester(t *testing.T) {
+	tester := test.NewTester(test.Config{
+		Peer: dialer.Net{
+			Network: "tcp",
+			Address: "localhost:5432",
+			AcceptOptions: backends.AcceptOptions{
+				Credentials: credentials.Cleartext{
+					Username: "postgres",
+					Password: "password",
+				},
+				Database: "pggat",
+			},
+		},
+	})
+	if err := tester.Run(tests.SimpleQuery); err != nil {
+		t.Error(err)
+	}
+}
diff --git a/test/testfile/instruction.go b/test/testfile/instruction.go
deleted file mode 100644
index 119fe6e7c52c8d1447d3d30ae1849a0c3d2ed8f7..0000000000000000000000000000000000000000
--- a/test/testfile/instruction.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package testfile
-
-type Instruction struct {
-	Op   Opcode
-	Args []any
-}
diff --git a/test/testfile/opcode.go b/test/testfile/opcode.go
deleted file mode 100644
index 2684e6182ac40ef543e2435d1892498335b3cccb..0000000000000000000000000000000000000000
--- a/test/testfile/opcode.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package testfile
-
-type Opcode int
-
-func OpcodeFromString(str string) (Opcode, bool) {
-
-}
diff --git a/test/testfile/parser/any.go b/test/testfile/parser/any.go
deleted file mode 100644
index 86756b717c608634c4d80d4875c3751e2550d766..0000000000000000000000000000000000000000
--- a/test/testfile/parser/any.go
+++ /dev/null
@@ -1,4 +0,0 @@
-package parser
-
-func Any(ctx *Context) (rune, bool) {
-}
diff --git a/test/testfile/parser/context.go b/test/testfile/parser/context.go
deleted file mode 100644
index 3e476cff829bd6220a6e369439a7b36e0c5b3bae..0000000000000000000000000000000000000000
--- a/test/testfile/parser/context.go
+++ /dev/null
@@ -1,4 +0,0 @@
-package parser
-
-type Context struct {
-}
diff --git a/test/testfile/parser/parser.go b/test/testfile/parser/parser.go
deleted file mode 100644
index 7e1e57d32a6cdc11b9bad9db62d20b747fa9749f..0000000000000000000000000000000000000000
--- a/test/testfile/parser/parser.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package parser
-
-type Builder[O any] func(*Context) (O, bool)
diff --git a/test/testfile/parser/parsers/v0/parser.go b/test/testfile/parser/parsers/v0/parser.go
deleted file mode 100644
index a76501856891f5b92348f9916336eec13b032b10..0000000000000000000000000000000000000000
--- a/test/testfile/parser/parsers/v0/parser.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package parsers
-
-import (
-	"strings"
-
-	"pggat/test/testfile"
-	"pggat/test/testfile/parser"
-)
-
-func NewlineOrEOF(ctx *parser.Context) (struct{}, bool) {
-	c, ok := parser.Any(ctx)
-	if !ok || c == '\n' {
-		return struct{}{}, true
-	}
-	return struct{}{}, false
-}
-
-func Whitespace(ctx *parser.Context) (struct{}, bool) {
-	var n int
-	for {
-		_, ok := parser.SingleOf(ctx, func(r rune) bool {
-			switch r {
-			case ' ', '\t', '\r', '\n', '\v', '\f':
-				return true
-			default:
-				return false
-			}
-		})
-		if !ok {
-			break
-		}
-		n++
-	}
-	return struct{}{}, n > 0
-}
-
-func Identifier(ctx *parser.Context) (string, bool) {
-	var b strings.Builder
-	for {
-		c, ok := parser.SingleOf(ctx, func(r rune) bool {
-			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_'
-		})
-		if !ok {
-			break
-		}
-		b.WriteRune(c)
-	}
-	if b.Len() == 0 {
-		return "", false
-	}
-	return b.String(), true
-}
-
-func Opcode(ctx *parser.Context) (testfile.Opcode, bool) {
-	ident, ok := Identifier(ctx)
-	if !ok {
-		return 0, false
-	}
-	return testfile.OpcodeFromString(ident)
-}
-
-func StringArgument(ctx *parser.Context) (string, bool) {
-	// open quote
-	_, ok := parser.Single(ctx, '"')
-	if !ok {
-		return "", false
-	}
-
-	var b strings.Builder
-	for {
-		c, ok := parser.Any(ctx)
-		if !ok {
-			return "", false
-		}
-		if c == '"' {
-			break
-		}
-		b.WriteRune(c)
-	}
-
-	return b.String(), true
-}
-
-func BoolArgument(ctx *parser.Context) (bool, bool) {
-	ident, ok := Identifier(ctx)
-	if !ok {
-		return false, false
-	}
-	switch ident {
-	case "false":
-		return false, true
-	case "true":
-		return true, true
-	default:
-		return false, false
-	}
-}
-
-func FloatArgument(ctx *parser.Context) (float64, bool) {
-	return 0, false // TODO(garet)
-}
-
-func IntArgument(ctx *parser.Context) (int, bool) {
-	return 0, false // TODO(garet)
-}
-
-func NumberArgument(ctx *parser.Context) (any, bool) {
-	if arg, ok := parser.Try(ctx, FloatArgument); ok {
-		return arg, true
-	}
-	if arg, ok := parser.Try(ctx, IntArgument); ok {
-		return arg, true
-	}
-	return nil, false
-}
-
-func Argument(ctx *parser.Context) (any, bool) {
-	if arg, ok := parser.Try(ctx, StringArgument); ok {
-		return arg, true
-	}
-	if arg, ok := parser.Try(ctx, BoolArgument); ok {
-		return arg, true
-	}
-	if arg, ok := parser.Try(ctx, NumberArgument); ok {
-		return arg, true
-	}
-	return nil, false
-}
-
-func Arguments(ctx *parser.Context) ([]any, bool) {
-	var args []any
-	for {
-		arg, ok := parser.Try(ctx, Argument)
-		if !ok {
-			break
-		}
-		args = append(args, arg)
-		Whitespace(ctx)
-	}
-	return args, len(args) != 0
-}
-
-func Instruction(ctx *parser.Context) (testfile.Instruction, bool) {
-	Whitespace(ctx)
-	opcode, ok := Opcode(ctx)
-	if !ok {
-		return testfile.Instruction{}, false
-	}
-
-	Whitespace(ctx)
-	arguments, _ := Arguments(ctx)
-
-	Whitespace(ctx)
-	_, ok = NewlineOrEOF(ctx)
-	if !ok {
-		return testfile.Instruction{}, false
-	}
-
-	return testfile.Instruction{
-		Op:   opcode,
-		Args: arguments,
-	}, true
-}
diff --git a/test/testfile/parser/singleof.go b/test/testfile/parser/singleof.go
deleted file mode 100644
index a8cdc7c128a3298ab4155c25bce12cccac2f039c..0000000000000000000000000000000000000000
--- a/test/testfile/parser/singleof.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package parser
-
-func SingleOf(ctx *Context, fn func(rune) bool) (rune, bool) {
-	c, ok := Any(ctx)
-	if !ok {
-		return 0, false
-	}
-	if !fn(c) {
-		return 0, false
-	}
-	return c, true
-}
-
-func Single(ctx *Context, r rune) (struct{}, bool) {
-	c, ok := Any(ctx)
-	if !ok {
-		return struct{}{}, false
-	}
-	if c != r {
-		return struct{}{}, false
-	}
-	return struct{}{}, true
-}
diff --git a/test/testfile/parser/try.go b/test/testfile/parser/try.go
deleted file mode 100644
index 3dd8caf730c56f3c5f800d5be92c865ae2261c95..0000000000000000000000000000000000000000
--- a/test/testfile/parser/try.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package parser
-
-func Try[O any](ctx *Context, b Builder[O]) (O, bool) {
-
-}
diff --git a/test/tests/000_select b/test/tests/000_select
deleted file mode 100644
index be6a81acdfa4eb2d7bf22354818ca40f6b6d5442..0000000000000000000000000000000000000000
--- a/test/tests/000_select
+++ /dev/null
@@ -1 +0,0 @@
-SQ	"select 1;"
\ No newline at end of file
diff --git a/test/tests/simple_query.go b/test/tests/simple_query.go
new file mode 100644
index 0000000000000000000000000000000000000000..d1697c19cb21f005bb8b7f4a2321dacefba39b80
--- /dev/null
+++ b/test/tests/simple_query.go
@@ -0,0 +1,13 @@
+package tests
+
+import (
+	"pggat/test"
+	"pggat/test/inst"
+)
+
+var SimpleQuery = test.Test{
+	Parallel: true,
+	Instructions: []inst.Instruction{
+		inst.SimpleQuery("select 1;"),
+	},
+}