You've already forked testing-go-code-with-postgres
mirror of
https://github.com/xorcare/testing-go-code-with-postgres.git
synced 2025-06-30 23:23:40 +02:00
Add example of tests with isolated schema
This commit is contained in:
@ -78,6 +78,7 @@ total: (statements) 100.0%
|
|||||||
package.
|
package.
|
||||||
- [Example of integration testing with isolated database for each testcase](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/user_repository_with_isolated_database_test.go).
|
- [Example of integration testing with isolated database for each testcase](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/user_repository_with_isolated_database_test.go).
|
||||||
- [Example of integration testing with transaction cleanup for each testcase](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/user_repository_with_transactional_cleanup_test.go).
|
- [Example of integration testing with transaction cleanup for each testcase](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/user_repository_with_transactional_cleanup_test.go).
|
||||||
|
- [Example of integration testing with isolated schema for each testcase](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/user_repository_with_isolated_schema_test.go).
|
||||||
- And example
|
- And example
|
||||||
of [GitHub Actions](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/.github/workflows/go.yml)
|
of [GitHub Actions](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/.github/workflows/go.yml)
|
||||||
and [Gitlab CI](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/.gitlab-ci.yml).
|
and [Gitlab CI](https://github.com/xorcare/testing-go-code-with-postgres/blob/main/.gitlab-ci.yml).
|
||||||
|
9
go.mod
9
go.mod
@ -3,6 +3,7 @@ module github.com/xorcare/testing-go-code-with-postgres
|
|||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/jackc/pgx/v5 v5.6.0
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
@ -10,14 +11,18 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/lib/pq v1.10.9 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
golang.org/x/crypto v0.17.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/crypto v0.20.0 // indirect
|
||||||
|
golang.org/x/sync v0.5.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
53
go.sum
53
go.sum
@ -1,9 +1,32 @@
|
|||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg=
|
||||||
|
github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA=
|
||||||
|
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||||
|
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
|
||||||
|
github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
@ -16,6 +39,18 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||||
|
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
@ -25,12 +60,22 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
||||||
|
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||||
|
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||||
|
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
10
migrations/embed.go
Normal file
10
migrations/embed.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Copyright (c) 2024 Vasiliy Vasilyuk. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed *.up.sql
|
||||||
|
var FS embed.FS
|
@ -17,6 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||||
_ "github.com/jackc/pgx/v5/stdlib"
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -37,6 +38,10 @@ func NewWithIsolatedDatabase(t TestingT) *Postgres {
|
|||||||
return newPostgres(t, defaultPostgresURL).cloneFromReference()
|
return newPostgres(t, defaultPostgresURL).cloneFromReference()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewWithIsolatedSchema(t TestingT) *Postgres {
|
||||||
|
return newPostgres(t, defaultPostgresURL).createSchema(t)
|
||||||
|
}
|
||||||
|
|
||||||
func NewWithTransactionalCleanup(t TestingT) interface {
|
func NewWithTransactionalCleanup(t TestingT) interface {
|
||||||
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
|
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
|
||||||
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
|
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
|
||||||
@ -108,6 +113,40 @@ func (p *Postgres) DB() *sql.DB {
|
|||||||
return p.sqlDB
|
return p.sqlDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Postgres) createSchema(t TestingT) *Postgres {
|
||||||
|
schemaName := newUniqueHumanReadableDatabaseName(p.t)
|
||||||
|
|
||||||
|
// Unclear why, but if the scheme contains letters of different case, the
|
||||||
|
// tests stop working. At the moment I don't quite understand why this
|
||||||
|
// happens, but converting to lower case fixes the problem.
|
||||||
|
schemaName = strings.ToLower(schemaName)
|
||||||
|
|
||||||
|
ctx, done := context.WithCancel(context.Background())
|
||||||
|
t.Cleanup(done)
|
||||||
|
|
||||||
|
{
|
||||||
|
sql := fmt.Sprintf(`CREATE SCHEMA "%s";`, schemaName)
|
||||||
|
|
||||||
|
_, err := p.DB().ExecContext(ctx, sql)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
sql := fmt.Sprintf(`DROP SCHEMA "%s" CASCADE;`, schemaName)
|
||||||
|
|
||||||
|
_, err := p.DB().ExecContext(ctx, sql)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
pgurl := setSearchPath(t, p.URL(), schemaName)
|
||||||
|
|
||||||
|
return &Postgres{
|
||||||
|
t: p.t,
|
||||||
|
ref: p.ref,
|
||||||
|
url: pgurl.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Postgres) cloneFromReference() *Postgres {
|
func (p *Postgres) cloneFromReference() *Postgres {
|
||||||
newDBName := newUniqueHumanReadableDatabaseName(p.t)
|
newDBName := newUniqueHumanReadableDatabaseName(p.t)
|
||||||
|
|
||||||
@ -220,3 +259,14 @@ func open(t TestingT, dataSourceURL string) *sql.DB {
|
|||||||
|
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setSearchPath(t TestingT, pgURL string, schemaName string) *url.URL {
|
||||||
|
pgurl, err := url.Parse(pgURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := pgurl.Query()
|
||||||
|
query.Set("search_path", schemaName)
|
||||||
|
pgurl.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
return pgurl
|
||||||
|
}
|
||||||
|
@ -94,6 +94,86 @@ func TestNewPostgres(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewWithIsolatedSchema(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Successfully connect by URL and get version", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
postgres := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
dbPool, err := pgxpool.New(ctx, postgres.URL())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var version string
|
||||||
|
err = dbPool.QueryRow(ctx, "SHOW search_path;").Scan(&version)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, version)
|
||||||
|
t.Log(version)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Successfully obtained a version using a pre-configured conn", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
postgres := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var version string
|
||||||
|
err := postgres.DB().QueryRowContext(ctx, "SHOW search_path;").Scan(&version)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, version)
|
||||||
|
|
||||||
|
t.Log(version)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Changes are not visible in different instances", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
postgres1 := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
postgres2 := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const sqlStr = `CREATE TABLE "no_conflict" (id integer PRIMARY KEY)`
|
||||||
|
_, err1 := postgres1.DB().ExecContext(ctx, sqlStr)
|
||||||
|
_, err2 := postgres2.DB().ExecContext(ctx, sqlStr)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.NoError(t, err1)
|
||||||
|
require.NoError(t, err2, "databases must be isolated for each instance")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("URL is different at different instances", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
postgres1 := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
postgres2 := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
url1 := postgres1.URL()
|
||||||
|
url2 := postgres2.URL()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.NotEqual(t, url1, url2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewWithTransactionalCleanup(t *testing.T) {
|
func TestNewWithTransactionalCleanup(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping test in short mode")
|
t.Skip("skipping test in short mode")
|
||||||
|
@ -34,8 +34,7 @@ func (r *UserRepository) ReadUser(ctx context.Context, userID uuid.UUID) (User,
|
|||||||
|
|
||||||
err := row.Scan(&user.ID, &user.Username, &user.CreatedAt)
|
err := row.Scan(&user.ID, &user.Username, &user.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
const format = "failed selection of User from database: %v"
|
const format = "failed selection of User from database: %w"
|
||||||
|
|
||||||
return User{}, fmt.Errorf(format, err)
|
return User{}, fmt.Errorf(format, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,3 +9,4 @@ package testing_go_code_with_postgres_test
|
|||||||
// in the following files:
|
// in the following files:
|
||||||
// - user_repository_with_isolated_database_test.go
|
// - user_repository_with_isolated_database_test.go
|
||||||
// - user_repository_with_transactional_cleanup_test.go
|
// - user_repository_with_transactional_cleanup_test.go
|
||||||
|
// - user_repository_with_isolated_schema_test.go
|
||||||
|
134
user_repository_with_isolated_schema_test.go
Normal file
134
user_repository_with_isolated_schema_test.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (c) 2023-2024 Vasiliy Vasilyuk. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package testing_go_code_with_postgres_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||||||
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
|
|
||||||
|
"github.com/xorcare/testing-go-code-with-postgres/migrations"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
rootpkg "github.com/xorcare/testing-go-code-with-postgres"
|
||||||
|
"github.com/xorcare/testing-go-code-with-postgres/testingpg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func migrateDatabaseSchema(t *testing.T, pg *testingpg.Postgres) {
|
||||||
|
source, err := iofs.New(migrations.FS, ".")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mi, err := migrate.NewWithSourceInstance(
|
||||||
|
"iofs",
|
||||||
|
source,
|
||||||
|
pg.URL(),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = mi.Up()
|
||||||
|
|
||||||
|
if !errors.Is(err, migrate.ErrNoChange) {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Schema_UserRepository_CreateUser(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
newFullyFiledUser := func() rootpkg.User {
|
||||||
|
return rootpkg.User{
|
||||||
|
ID: uuid.New(),
|
||||||
|
Username: "gopher",
|
||||||
|
CreatedAt: time.Now().Truncate(time.Microsecond),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Successfully created a User", func(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
pg := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
|
||||||
|
migrateDatabaseSchema(t, pg)
|
||||||
|
|
||||||
|
repo := rootpkg.NewUserRepository(pg.DB())
|
||||||
|
user := newFullyFiledUser()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
err := repo.CreateUser(context.Background(), user)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
gotUser, err := repo.ReadUser(context.Background(), user.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, user, gotUser)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Cannot create a user with the same ID", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
pg := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
|
||||||
|
migrateDatabaseSchema(t, pg)
|
||||||
|
|
||||||
|
repo := rootpkg.NewUserRepository(pg.DB())
|
||||||
|
|
||||||
|
user := newFullyFiledUser()
|
||||||
|
|
||||||
|
err := repo.CreateUser(context.Background(), user)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
err = repo.CreateUser(context.Background(), user)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "duplicate key value violates unique constraint")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Schema_UserRepository_ReadUser(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Get an error if the user does not exist", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
pg := testingpg.NewWithIsolatedSchema(t)
|
||||||
|
|
||||||
|
migrateDatabaseSchema(t, pg)
|
||||||
|
|
||||||
|
repo := rootpkg.NewUserRepository(pg.DB())
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_, err := repo.ReadUser(context.Background(), uuid.New())
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||||
|
})
|
||||||
|
}
|
Reference in New Issue
Block a user