Compare commits

..

No commits in common. "547698872d621994d1e7264afff315e664b4a8e7" and "7f97e784b6496551ff8ba60563e6e5217606f3fa" have entirely different histories.

6 changed files with 121 additions and 172 deletions

View File

@ -9,8 +9,6 @@ import (
"git.rosemyrtle.work/personal-finance/server/internal/api" "git.rosemyrtle.work/personal-finance/server/internal/api"
"git.rosemyrtle.work/personal-finance/server/internal/dal" "git.rosemyrtle.work/personal-finance/server/internal/dal"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
_ "github.com/jackc/pgx/v5/stdlib" _ "github.com/jackc/pgx/v5/stdlib"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -41,7 +39,7 @@ func main() {
} }
// 2. Data Access Layer // 2. Data Access Layer
dal := dal.DalImpl{Db: db, Dialect: goqu.Dialect("postgres")} dal := dal.DalImpl{Db: db}
// 3. HTTP server // 3. HTTP server
e := echo.New() e := echo.New()

1
go.mod
View File

@ -5,7 +5,6 @@ go 1.21.1
require ( require (
github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/deepmap/oapi-codegen/v2 v2.1.0 github.com/deepmap/oapi-codegen/v2 v2.1.0
github.com/doug-martin/goqu/v9 v9.0.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.5.5 github.com/jackc/pgx/v5 v5.5.5
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1

26
go.sum
View File

@ -1,4 +1,3 @@
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
@ -10,20 +9,16 @@ 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/deepmap/oapi-codegen/v2 v2.1.0 h1:I/NMVhJCtuvL9x+S2QzZKpSjGi33oDZwPRdemvOZWyQ= github.com/deepmap/oapi-codegen/v2 v2.1.0 h1:I/NMVhJCtuvL9x+S2QzZKpSjGi33oDZwPRdemvOZWyQ=
github.com/deepmap/oapi-codegen/v2 v2.1.0/go.mod h1:R1wL226vc5VmCNJUvMyYr3hJMm5reyv25j952zAVXZ8= github.com/deepmap/oapi-codegen/v2 v2.1.0/go.mod h1:R1wL226vc5VmCNJUvMyYr3hJMm5reyv25j952zAVXZ8=
github.com/doug-martin/goqu/v9 v9.0.0 h1:de8P6AsHnCPZsdKQl8lrqI8XNxU3K//jrDrI2ZeYcDs=
github.com/doug-martin/goqu/v9 v9.0.0/go.mod h1:GPZzBWikLe6aaCT2uBT5X3MTseGBYevzWiVjMl2wIWk=
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
@ -52,8 +47,6 @@ github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+k
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -61,7 +54,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/oapi-codegen/echo-middleware v1.0.1 h1:edYGScq1phCcuDoz9AqA9eHX+tEI1LNL5PL1lkkQh1k= github.com/oapi-codegen/echo-middleware v1.0.1 h1:edYGScq1phCcuDoz9AqA9eHX+tEI1LNL5PL1lkkQh1k=
@ -78,10 +70,7 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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.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=
@ -93,44 +82,29 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
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=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -21,7 +21,7 @@ func (server *ServerImpl) GetBanks(ctx echo.Context) error {
banks, err := server.Dal.Banks() banks, err := server.Dal.Banks()
if err != nil { if err != nil {
return err return ctx.NoContent(http.StatusInternalServerError)
} }
if len(banks) == 0 { if len(banks) == 0 {
@ -36,7 +36,8 @@ func (server *ServerImpl) GetBankById(ctx echo.Context, bankId string) error {
bank, err := server.Dal.Bank(bankId) bank, err := server.Dal.Bank(bankId)
if err != nil { if err != nil {
return err log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
} }
if bank == nil { if bank == nil {
@ -77,7 +78,8 @@ func (server *ServerImpl) GetTransactionById(ctx echo.Context, transactionId int
transaction, err := server.Dal.Transaction(transactionId) transaction, err := server.Dal.Transaction(transactionId)
if err != nil { if err != nil {
return err log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
} }
if transaction == nil { if transaction == nil {
@ -96,7 +98,8 @@ func (server *ServerImpl) CreateTransaction(ctx echo.Context) error {
transaction, err := server.Dal.InsertTransaction(transaction2entity(*t)) transaction, err := server.Dal.InsertTransaction(transaction2entity(*t))
if err != nil { if err != nil {
return err log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
} }
return ctx.JSON(http.StatusCreated, entity2transaction(transaction)) return ctx.JSON(http.StatusCreated, entity2transaction(transaction))
@ -104,7 +107,8 @@ func (server *ServerImpl) CreateTransaction(ctx echo.Context) error {
func (server *ServerImpl) UpdateTransaction(ctx echo.Context, transactionId int64) error { func (server *ServerImpl) UpdateTransaction(ctx echo.Context, transactionId int64) error {
if exists, err := server.Dal.TransactionExists(entity.TransactionId(transactionId)); err != nil { if exists, err := server.Dal.TransactionExists(entity.TransactionId(transactionId)); err != nil {
return err log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
} else if !exists { } else if !exists {
return ctx.NoContent(http.StatusNotFound) return ctx.NoContent(http.StatusNotFound)
} }
@ -117,7 +121,8 @@ func (server *ServerImpl) UpdateTransaction(ctx echo.Context, transactionId int6
success, err := server.Dal.UpdateTransaction(entity.TransactionId(transactionId), update.Category) success, err := server.Dal.UpdateTransaction(entity.TransactionId(transactionId), update.Category)
if err != nil { if err != nil {
return err log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
} }
if !success { if !success {
@ -129,7 +134,7 @@ func (server *ServerImpl) UpdateTransaction(ctx echo.Context, transactionId int6
func (server *ServerImpl) GetCategories(ctx echo.Context) error { func (server *ServerImpl) GetCategories(ctx echo.Context) error {
categories, err := server.Dal.Categories() categories, err := server.Dal.Categories()
if err != nil { if err != nil {
return err return ctx.NoContent(http.StatusInternalServerError)
} }
if len(categories) == 0 { if len(categories) == 0 {

View File

@ -5,28 +5,17 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"strings"
"time" "time"
"git.rosemyrtle.work/personal-finance/server/internal/entity" "git.rosemyrtle.work/personal-finance/server/internal/entity"
"github.com/doug-martin/goqu/v9"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
type DalImpl struct { type DalImpl struct {
Db *sql.DB Db *sql.DB
Dialect goqu.DialectWrapper
} }
// Table aliases
var (
schema = goqu.S("pfbudget")
t = schema.Table("transactions")
tc = schema.Table("transactions_categorized")
b = schema.Table("banks")
bn = schema.Table("banks_nordigen")
c = schema.Table("categories")
)
func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error) { func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error) {
log.Printf("DAL::Transaction(%v)", transactionId) log.Printf("DAL::Transaction(%v)", transactionId)
@ -34,23 +23,15 @@ func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Select( stmts := []string{
t.Col("id"), "SELECT t.id, t.date, t.description, t.amount, tc.name",
t.Col("date"), "FROM pfbudget.transactions t",
t.Col("description"), "LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id",
t.Col("amount"), "WHERE t.id = $1",
tc.Col("name"),
).
From(t).
LeftJoin(tc, goqu.On(t.Col("id").Eq(tc.Col("id")))).
Where(t.Col("id").Eq(transactionId))
sqlStr, args, err := ds.Prepared(true).ToSQL()
if err != nil {
log.Panic(err)
} }
stmt := strings.Join(stmts, "\n") + "\n"
rows, err := dal.Db.Query(sqlStr, args...) rows, err := dal.Db.Query(stmt, transactionId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -70,30 +51,27 @@ func (dal *DalImpl) Transactions(limit, offset int, category *string) (entity.Tr
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Select( stmts := []string{
t.Col("id"), "SELECT t.id, t.date, t.description, t.amount, tc.name, count(*) OVER() AS total_count",
t.Col("date"), "FROM pfbudget.transactions t",
t.Col("description"), "LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id",
t.Col("amount"), }
tc.Col("name"), args := []any{limit, offset}
goqu.L("count(*) OVER() AS total_count"),
).
From(t).
LeftJoin(tc, goqu.On(t.Col("id").Eq(tc.Col("id")))).
Order(t.Col("date").Desc()).
Limit(uint(limit)).
Offset(uint(offset))
if category != nil { if category != nil {
ds = ds.Where(tc.Col("name").ILike(*category)) stmts = append(stmts, "WHERE tc.name SIMILAR TO '%' || $3 || '%'")
args = append(args, *category)
} }
stmts = append(stmts,
"ORDER BY t.date DESC",
"LIMIT $1",
"OFFSET $2",
)
stmt := strings.Join(stmts, "\n") + "\n"
sqlStr, args, err := ds.Prepared(true).ToSQL() log.Printf("DAL::Transactions::stmt: %s", stmt)
if err != nil {
return nil, 0, err
}
rows, err := dal.Db.Query(sqlStr, args...) rows, err := dal.Db.Query(stmt, args...)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -114,39 +92,33 @@ func (dal *DalImpl) Transactions(limit, offset int, category *string) (entity.Tr
var ans entity.Transactions var ans entity.Transactions
for _, twc := range transactions_with_count { for _, twc := range transactions_with_count {
tx := entity.Transaction{Id: twc.Id, Date: twc.Date, Description: twc.Description, Value: twc.Value, Category: twc.Category} t := entity.Transaction{Id: twc.Id, Date: twc.Date, Description: twc.Description, Value: twc.Value, Category: twc.Category}
ans = append(ans, tx) ans = append(ans, t)
} }
return ans, transactions_with_count[0].TotalCount, nil return ans, transactions_with_count[0].TotalCount, nil
} }
func (dal *DalImpl) InsertTransaction(tx entity.Transaction) (entity.Transaction, error) { func (dal *DalImpl) InsertTransaction(t entity.Transaction) (entity.Transaction, error) {
log.Print("DAL::InsertTransaction") log.Print("DAL::InsertTransaction")
if dal.Db == nil { if dal.Db == nil {
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Insert(t).Rows( stmts := []string{
goqu.Record{ "INSERT INTO pfbudget.transactions (date, description, amount)",
"date": tx.Date, "VALUES ($1, $2, $3)",
"description": tx.Description, "RETURNING id",
"amount": tx.Value,
}).
Returning(t.Col("id"))
sqlStr, args, err := ds.Prepared(true).ToSQL()
if err != nil {
return entity.Transaction{}, err
} }
stmt := strings.Join(stmts, "\n") + "\n"
id := new(uint64) id := new(uint64)
if err := dal.Db.QueryRow(sqlStr, args...).Scan(id); err != nil { if err := dal.Db.QueryRow(stmt, t.Date, t.Description, t.Value).Scan(id); err != nil {
return entity.Transaction{}, err return entity.Transaction{}, err
} }
tx.Id = *id t.Id = *id
return tx, nil return t, nil
} }
func (dal *DalImpl) UpdateTransaction(id entity.TransactionId, category *entity.CategoryName) (bool, error) { func (dal *DalImpl) UpdateTransaction(id entity.TransactionId, category *entity.CategoryName) (bool, error) {
@ -160,17 +132,14 @@ func (dal *DalImpl) UpdateTransaction(id entity.TransactionId, category *entity.
return false, errors.New("missing category") return false, errors.New("missing category")
} }
// TODO(43): implement upsert logic stmts := []string{
ds := dal.Dialect.Update(tc). "UPDATE pfbudget.transactions_categorized",
Set(goqu.Record{"name": *category}). "SET name = $2",
Where(tc.Col("id").Eq(id)) "WHERE id = $1",
sqlStr, args, err := ds.Prepared(true).ToSQL()
if err != nil {
return false, err
} }
stmt := strings.Join(stmts, "\n") + "\n"
result, err := dal.Db.Exec(sqlStr, args...) result, err := dal.Db.Exec(stmt, id, *category)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -195,15 +164,18 @@ func (dal *DalImpl) TransactionExists(id uint64) (bool, error) {
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Select(goqu.L("EXISTS(SELECT 1 FROM pfbudget.transactions WHERE id = ?)", id)) stmts := []string{
"SELECT EXISTS(",
sqlStr, args, err := ds.Prepared(true).ToSQL() " SELECT 1",
if err != nil { " FROM pfbudget.transactions",
return false, err " WHERE id = $1",
")",
} }
stmt := strings.Join(stmts, "\n") + "\n"
exists := new(bool) exists := new(bool)
if err := dal.Db.QueryRow(sqlStr, args...).Scan(exists); err != nil { err := dal.Db.QueryRow(stmt, id).Scan(&exists)
if err != nil {
return false, err return false, err
} }
@ -217,21 +189,15 @@ func (dal *DalImpl) Bank(bankId string) (*entity.Bank, error) {
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Select( stmts := []string{
b.Col("name"), "SELECT b.name, b.name, n.requisition_id",
b.Col("name"), "FROM pfbudget.banks b",
bn.Col("requisition_id"), "JOIN pfbudget.banks_nordigen n ON b.name = n.name",
). "WHERE b.name = $1",
From(b).
Join(bn, goqu.On(b.Col("name").Eq(bn.Col("name")))).
Where(b.Col("name").Eq(bankId))
sqlStr, args, err := ds.Prepared(true).ToSQL()
if err != nil {
return nil, err
} }
stmt := strings.Join(stmts, "\n") + "\n"
rows, err := dal.Db.Query(sqlStr, args...) rows, err := dal.Db.Query(stmt, bankId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -251,20 +217,14 @@ func (dal *DalImpl) Banks() (entity.Banks, error) {
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Select( stmts := []string{
b.Col("name"), "SELECT b.name, b.name, n.requisition_id",
b.Col("name"), "FROM pfbudget.banks b",
bn.Col("requisition_id"), "JOIN pfbudget.banks_nordigen n ON b.name = n.name",
).
From(b).
Join(bn, goqu.On(b.Col("name").Eq(bn.Col("name"))))
sqlStr, args, err := ds.Prepared(true).ToSQL()
if err != nil {
return entity.Banks{}, err
} }
stmt := strings.Join(stmts, "\n") + "\n"
rows, err := dal.Db.Query(sqlStr, args...) rows, err := dal.Db.Query(stmt)
if err != nil { if err != nil {
return entity.Banks{}, err return entity.Banks{}, err
} }
@ -279,18 +239,13 @@ func (dal *DalImpl) Categories() (entity.Categories, error) {
log.Panic("database not available") log.Panic("database not available")
} }
ds := dal.Dialect.Select( stmts := []string{
c.Col("name"), "SELECT c.name, c.group",
c.Col("group"), "FROM pfbudget.categories c",
).
From(c)
sqlStr, args, err := ds.Prepared(true).ToSQL()
if err != nil {
return []entity.Category{}, err
} }
stmt := strings.Join(stmts, "\n") + "\n"
rows, err := dal.Db.Query(sqlStr, args...) rows, err := dal.Db.Query(stmt)
if err != nil { if err != nil {
return []entity.Category{}, err return []entity.Category{}, err
} }

View File

@ -66,7 +66,12 @@ func TestDalImpl_Transaction(t *testing.T) {
} }
mock. mock.
ExpectQuery("^SELECT.*id.*date.*description.*amount.*name.*WHERE.*id.*= \\?"). ExpectQuery(`
^SELECT \w+\.id, \w+\.date, \w+\.description, \w+\.amount, \w+\.name
FROM \w+\.transactions \w+
LEFT JOIN \w+\.transactions_categorized \w+
ON \w+\.id = \w+\.id
WHERE \w+\.id = \$1$`).
WithArgs(tt.args.transactionId). WithArgs(tt.args.transactionId).
WillReturnRows( WillReturnRows(
mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...), mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...),
@ -109,7 +114,7 @@ func TestDalImpl_Transactions(t *testing.T) {
{ {
"SelectTransactions", "SelectTransactions",
fields{db}, fields{db},
args{limit: 30, offset: 30, category: nil}, args{limit: 30, offset: 0, category: nil},
[][]driver.Value{ [][]driver.Value{
{1, date, "income", 1000, nil, 2}, {1, date, "income", 1000, nil, 2},
{2, date, "expense", -10.50, nil, 2}, {2, date, "expense", -10.50, nil, 2},
@ -123,7 +128,7 @@ func TestDalImpl_Transactions(t *testing.T) {
{ {
"SelectTransactionsWithCategory", "SelectTransactionsWithCategory",
fields{db}, fields{db},
args{limit: 30, offset: 30, category: golang.Ptr("C1")}, args{limit: 30, offset: 0, category: golang.Ptr("C1")},
[][]driver.Value{ [][]driver.Value{
{1, date, "income", 1000, "C1", 2}, {1, date, "income", 1000, "C1", 2},
{2, date, "expense", -10.50, nil, 2}, {2, date, "expense", -10.50, nil, 2},
@ -135,7 +140,7 @@ func TestDalImpl_Transactions(t *testing.T) {
false, false,
}, },
{ {
"SelectNoTransactions", fields{db}, args{limit: 30, offset: 30, category: nil}, nil, nil, false, "SelectNoTransactions", fields{db}, args{limit: 30, offset: 0, category: nil}, nil, nil, false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -144,14 +149,22 @@ func TestDalImpl_Transactions(t *testing.T) {
Db: tt.fields.Db, Db: tt.fields.Db,
} }
args := []driver.Value{} args := []driver.Value{tt.args.limit, tt.args.offset}
stmt := "^SELECT.*id.*date.*description.*amount.*name.*count\\(\\*\\) OVER\\(\\) AS total_count" stmt := `
^SELECT \w+\.id, \w+\.date, \w+\.description, \w+\.amount, \w+\.name, count\(\*\) OVER\(\) AS total_count
FROM \w+\.transactions \w+
LEFT JOIN \w+\.transactions_categorized \w+
ON \w+\.id = \w+\.id
`
if tt.args.category != nil { if tt.args.category != nil {
stmt += ".*WHERE.*name.*ILIKE \\?" stmt += `WHERE \w+\.name SIMILAR TO '%' || \$3 || '%'`
args = append(args, *tt.args.category) args = append(args, *tt.args.category)
} }
stmt += ".*ORDER BY.*date.*DESC LIMIT \\? OFFSET \\?" stmt += `
args = append(args, tt.args.limit, tt.args.offset) ORDER BY \w+\.date DESC
LIMIT \$1
OFFSET \$2
`
mock. mock.
ExpectQuery(stmt). ExpectQuery(stmt).
@ -214,7 +227,7 @@ func TestDalImpl_Bank(t *testing.T) {
} }
mock. mock.
ExpectQuery("^SELECT.*name.*name.*requisition_id.*WHERE.*name.*= \\?"). ExpectQuery("^SELECT .* FROM .*banks b JOIN .*banks_nordigen n ON b.name = n.name WHERE b.name = \\$1$").
WithArgs(tt.args.bankId). WithArgs(tt.args.bankId).
WillReturnRows( WillReturnRows(
mock. mock.
@ -277,7 +290,7 @@ func TestDalImpl_Banks(t *testing.T) {
} }
mock. mock.
ExpectQuery("^SELECT.*name.*name.*requisition_id"). ExpectQuery("^SELECT .* FROM .*banks b JOIN .*banks_nordigen n ON b.name = n.name$").
WithoutArgs(). WithoutArgs().
WillReturnRows( WillReturnRows(
mock. mock.
@ -333,10 +346,10 @@ func TestDalImpl_InsertTransaction(t *testing.T) {
mock. mock.
ExpectQuery(` ExpectQuery(`
INSERT INTO.*.*amount.*date.*description.* INSERT INTO .* \(date, description, amount\)
VALUES.*\?, \?, \?.* VALUES \(\$1, \$2, \$3\)
RETURNING.*id`). RETURNING id`).
WithArgs(tt.args.t.Value, tt.args.t.Date, tt.args.t.Description). WithArgs(tt.args.t.Date, tt.args.t.Description, tt.args.t.Value).
WillReturnRows( WillReturnRows(
mock.NewRows([]string{"id"}). mock.NewRows([]string{"id"}).
AddRows([]driver.Value{tt.want.Id}), AddRows([]driver.Value{tt.want.Id}),
@ -400,10 +413,10 @@ func TestDalImpl_UpdateTransaction(t *testing.T) {
mock. mock.
ExpectExec(` ExpectExec(`
UPDATE.*transactions_categorized.* UPDATE \w+\.transactions_categorized
SET.*name.*= ?.* SET name = \$2
WHERE.*id.*= ?`). WHERE id = \$1`).
WithArgs(tt.args.category, tt.args.id). WithArgs(tt.args.id, tt.args.category).
WillReturnResult(tt.mocks) WillReturnResult(tt.mocks)
got, err := dal.UpdateTransaction(tt.args.id, tt.args.category) got, err := dal.UpdateTransaction(tt.args.id, tt.args.category)
@ -447,7 +460,12 @@ func TestDalImpl_TransactionExists(t *testing.T) {
} }
mock. mock.
ExpectQuery("EXISTS\\(SELECT 1 FROM pfbudget\\.transactions WHERE id = \\?\\)"). ExpectQuery(`
SELECT EXISTS\(
SELECT 1
FROM pfbudget.transactions
WHERE id = \$1
\)`).
WithArgs(tt.args.id). WithArgs(tt.args.id).
WillReturnRows( WillReturnRows(
mock.NewRows([]string{"exists"}). mock.NewRows([]string{"exists"}).
@ -511,7 +529,7 @@ func TestDalImpl_Categories(t *testing.T) {
} }
mock. mock.
ExpectQuery(`SELECT.*name.*group.*FROM.*categories`). ExpectQuery(`SELECT \w+\.name, \w+\.group FROM \w+.categories \w+`).
WithoutArgs(). WithoutArgs().
WillReturnRows(mock.NewRows([]string{"name", "group"}).AddRows(tt.mocks...)) WillReturnRows(mock.NewRows([]string{"name", "group"}).AddRows(tt.mocks...))