From 9108da715dcd2bfa339b10845601402ebbc3eff2 Mon Sep 17 00:00:00 2001 From: urec56 Date: Sun, 13 Apr 2025 23:43:35 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8E=20go.=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83?= =?UTF-8?q?=20=D0=B2=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/app/main.go | 11 +- config/config.go | 3 +- go.mod | 58 +- go.sum | 127 +- internal/domain/date.go | 51 + internal/domain/date_test.go | 190 +++ internal/domain/message.go | 2 +- internal/domain/user.go | 49 +- internal/domain/user_test.go | 182 --- internal/domain/uuid.go | 29 +- internal/domain/uuid_test.go | 44 +- internal/logger/logger.go | 2 +- internal/repository/messages.go | 61 +- internal/repository/mocks/mock.go | 36 +- internal/repository/repository.go | 8 +- internal/repository/users.go | 8 +- internal/service/auth.go | 2 +- internal/service/cache.go | 38 +- internal/service/messages.go | 66 +- internal/service/messages_test.go | 1338 +++++++++++++++++ internal/service/mocks/mock.go | 126 +- internal/service/service.go | 16 +- internal/service/service_test.go | 4 +- internal/service/users.go | 41 +- internal/service/users_test.go | 23 +- internal/transport/rest/handler/users.go | 16 +- internal/transport/rest/middleware/auth.go | 14 +- .../transport/rest/middleware/auth_test.go | 6 +- internal/transport/rest/server.go | 8 +- 29 files changed, 2023 insertions(+), 536 deletions(-) create mode 100644 internal/domain/date.go create mode 100644 internal/domain/date_test.go create mode 100644 internal/service/messages_test.go diff --git a/cmd/app/main.go b/cmd/app/main.go index 65e6e03..9d938f7 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -3,12 +3,13 @@ package main import ( "context" "errors" - "github.com/sirupsen/logrus" "net/http" "os" "os/signal" "syscall" + "github.com/sirupsen/logrus" + "git.urec56.ru/urec/chat_back_go/config" "git.urec56.ru/urec/chat_back_go/internal/database" "git.urec56.ru/urec/chat_back_go/internal/domain" @@ -26,7 +27,7 @@ func main() { db, rdb, mc, closeDBs, err := database.New(cfg, log) if err != nil { - log.Fatalf("error occurred while database initialization: %s", err.Error()) + log.Fatalf("error occurred while database initialization: %s", err) } defer closeDBs() @@ -39,11 +40,11 @@ func main() { go func() { if err = srv.Run(cfg.Srv.Port); !errors.Is(err, http.ErrServerClosed) { - log.Fatalf("error occurred while running rest server: %s", err.Error()) + log.Fatalf("error occurred while running rest server: %s", err) } }() - log.Infof("starting server on port %s\n", cfg.Srv.Port) + log.Infof("starting server on port %d\n", cfg.Srv.Port) quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) @@ -53,6 +54,6 @@ func main() { log.Info("shutting down the server") if err = srv.Shutdown(context.Background()); err != nil { - log.Fatalf("error occurred while shutting down rest server: %s", err.Error()) + log.Fatalf("error occurred while shutting down rest server: %s", err) } } diff --git a/config/config.go b/config/config.go index 895b8d5..ad3c8fd 100644 --- a/config/config.go +++ b/config/config.go @@ -42,9 +42,10 @@ type JWT struct { } type Server struct { - Port string `yaml:"port"` + Port int `yaml:"port"` RequestIDHeader string `yaml:"request_id_header"` } + type Migrations struct { Folder string `yaml:"folder"` } diff --git a/go.mod b/go.mod index f495e3c..85f4080 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module git.urec56.ru/urec/chat_back_go -go 1.22.6 +go 1.24.2 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/go-playground/validator/v10 v10.24.0 - github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang-migrate/migrate/v4 v4.18.2 github.com/google/uuid v1.6.0 github.com/ilyakaznacheev/cleanenv v1.5.0 @@ -22,35 +22,87 @@ require ( require ( github.com/BurntSushi/toml v1.2.1 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect + github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/inflect v0.21.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/loads v0.22.0 // indirect + github.com/go-openapi/runtime v0.28.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/strfmt v0.23.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/validate v0.24.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-swagger/go-swagger v0.31.0 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/gorilla/handlers v1.5.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jessevdk/go-flags v1.5.0 // indirect github.com/joho/godotenv v1.5.1 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/toqueteos/webbrowser v1.2.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + go.mongodb.org/mongo-driver v1.14.0 // indirect go.opentelemetry.io/otel/metric v1.32.0 // indirect go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.24.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect ) + +tool github.com/go-swagger/go-swagger/cmd/swagger diff --git a/go.sum b/go.sum index f519241..fb30667 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,17 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 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/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -33,12 +42,38 @@ 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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= +github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk= +github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw= +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/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= +github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +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/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -49,25 +84,38 @@ github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-swagger/go-swagger v0.31.0 h1:H8eOYQnY2u7vNKWDNykv2xJP3pBhRG/R+SOCAmKrLlc= +github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8= github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= 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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 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/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -76,13 +124,17 @@ github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -91,18 +143,34 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 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/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 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.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -114,13 +182,42 @@ github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93Ge github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +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/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= 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= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= +github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/urec56/pathparams v0.0.6 h1:OuCbamKVdfVtprL+arL5QUuy84R909haBHLburAAp1c= github.com/urec56/pathparams v0.0.6/go.mod h1:EymabShlKrvvNckqTZ6zVki6wEYTQmcg41nxFtd7G8Y= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -132,6 +229,8 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.mongodb.org/mongo-driver/v2 v2.1.0 h1:/ELnVNjmfUKDsoBisXxuJL0noR9CfeUIrP7Yt3R+egg= go.mongodb.org/mongo-driver/v2 v2.1.0/go.mod h1:AWiLRShSrk5RHQS3AEn3RL19rqOzVq49MCpWQ3x/huI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= @@ -146,14 +245,22 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -162,27 +269,39 @@ golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/domain/date.go b/internal/domain/date.go new file mode 100644 index 0000000..4cc9b46 --- /dev/null +++ b/internal/domain/date.go @@ -0,0 +1,51 @@ +package domain + +import ( + "database/sql/driver" + "fmt" + "time" +) + +type CustomDate struct { + time.Time +} + +func (ct *CustomDate) UnmarshalJSON(data []byte) error { + str := string(data[1 : len(data)-1]) + + t, err := time.Parse(time.DateOnly, str) + if err != nil { + return err + } + + ct.Time = t + return nil +} + +func (ct CustomDate) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("\"%s\"", ct.Format(time.DateOnly))), nil +} + +func (ct CustomDate) String() string { + return ct.Format(time.DateOnly) +} + +func (ct *CustomDate) Scan(value any) error { + switch v := value.(type) { + case string: + t, err := time.Parse(time.DateOnly, v) + if err != nil { + return err + } + *ct = CustomDate{Time: t} + case time.Time: + *ct = CustomDate{Time: v} + default: + panic("incorrect time type received") + } + return nil +} + +func (ct CustomDate) Value() (driver.Value, error) { + return ct.Time.Format(time.DateOnly), nil +} diff --git a/internal/domain/date_test.go b/internal/domain/date_test.go new file mode 100644 index 0000000..9ae4f90 --- /dev/null +++ b/internal/domain/date_test.go @@ -0,0 +1,190 @@ +package domain + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestCustomDate_UnmarshalJSON(t *testing.T) { + testTable := []struct { + name string + input string + expectErr bool + expected time.Time + }{ + { + name: "ok", + input: `"2024-02-25"`, + expected: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC), + }, + { + name: "invalid_format", + input: `"25-02-2024"`, + expectErr: true, + }, + { + name: "invalid_characters", + input: `"invalid-date"`, + expectErr: true, + }, + { + name: "empty_string", + input: `""`, + expectErr: true, + }, + { + name: "whitespace", + input: `" "`, + expectErr: true, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + var cd CustomDate + err := json.Unmarshal([]byte(tc.input), &cd) + + assert.Equal(t, tc.expectErr, err != nil) + assert.True(t, cd.Time.Equal(tc.expected) || tc.expectErr) + }) + } +} + +func TestCustomDate_MarshalJSON(t *testing.T) { + testTable := []struct { + name string + date CustomDate + expected string + }{ + { + name: "ok_1", + date: CustomDate{Time: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC)}, + expected: `"2024-02-25"`, + }, + { + name: "ok_2", + date: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, + expected: `"2023-08-10"`, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + result, err := json.Marshal(tc.date) + assert.NoError(t, err) + assert.Equal(t, tc.expected, string(result)) + }) + } +} + +func TestCustomDate_String(t *testing.T) { + testTable := []struct { + name string + date CustomDate + expected string + }{ + { + name: "ok_1", + date: CustomDate{Time: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC)}, + expected: "2024-02-25", + }, + { + name: "ok_2", + date: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, + expected: "2023-08-10", + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + result := tc.date.String() + + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestCustomDate_Scan(t *testing.T) { + testTable := []struct { + name string + dbValue any + expected CustomDate + expectedErr error + panics bool + }{ + { + name: "ok_1", + dbValue: "2023-08-10", + expected: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, + }, + { + name: "ok_2", + dbValue: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC), + expected: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, + }, + { + name: "invalid_date", + dbValue: "", + expectedErr: &time.ParseError{}, + }, + { + name: "invalid_type", + panics: true, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + if tc.panics { + defer func() { + if r := recover(); r != nil { + assert.Equal(t, "incorrect time type received", r) + } + }() + } + + date := &CustomDate{} + + err := date.Scan(tc.dbValue) + + if tc.expectedErr != nil { + assert.ErrorAs(t, err, &tc.expectedErr) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tc.expected, *date) + }) + } +} + +func TestCustomDate_Value(t *testing.T) { + testTable := []struct { + name string + date CustomDate + expected string + }{ + { + name: "ok_1", + date: CustomDate{Time: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC)}, + expected: "2024-02-25", + }, + { + name: "ok_2", + date: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, + expected: "2023-08-10", + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + result, err := tc.date.Value() + assert.NoError(t, err) + + date, ok := result.(string) + assert.True(t, ok) + assert.Equal(t, tc.expected, date) + }) + } +} diff --git a/internal/domain/message.go b/internal/domain/message.go index f211545..f036a2c 100644 --- a/internal/domain/message.go +++ b/internal/domain/message.go @@ -4,7 +4,7 @@ import ( "time" ) -type MessageRaw struct { +type RawMessage struct { ID UUID `bson:"id"` Message string `bson:"message,omitempty"` ImageUrl string `bson:"image_url,omitempty"` diff --git a/internal/domain/user.go b/internal/domain/user.go index 6de11b8..5cd556d 100644 --- a/internal/domain/user.go +++ b/internal/domain/user.go @@ -1,10 +1,7 @@ package domain import ( - "database/sql/driver" "encoding/json" - "fmt" - "time" ) const ( @@ -29,7 +26,7 @@ func (u *User) UnmarshalBinary(b []byte) error { return json.Unmarshal(b, u) } -func (u User) MarshalBinary() ([]byte, error) { +func (u *User) MarshalBinary() ([]byte, error) { return json.Marshal(u) } @@ -49,47 +46,3 @@ type UserRegister struct { Password2 string `json:"password2" validate:"eqfield=Password"` DateOfBirth CustomDate `json:"date_of_birth" validate:"date_of_birth"` } - -type CustomDate struct { - time.Time -} - -func (ct *CustomDate) UnmarshalJSON(data []byte) error { - str := string(data[1 : len(data)-1]) - - t, err := time.Parse(time.DateOnly, str) - if err != nil { - return err - } - - ct.Time = t - return nil -} - -func (ct CustomDate) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("\"%s\"", ct.Format(time.DateOnly))), nil -} - -func (ct CustomDate) String() string { - return ct.Format(time.DateOnly) -} - -func (ct *CustomDate) Scan(value any) error { - switch v := value.(type) { - case string: - t, err := time.Parse(time.DateOnly, v) - if err != nil { - return err - } - *ct = CustomDate{Time: t} - case time.Time: - *ct = CustomDate{Time: v} - default: - panic("incorrect time type received") - } - return nil -} - -func (ct CustomDate) Value() (driver.Value, error) { - return ct.Time.Format(time.DateOnly), nil -} diff --git a/internal/domain/user_test.go b/internal/domain/user_test.go index 55d6f38..80a52b4 100644 --- a/internal/domain/user_test.go +++ b/internal/domain/user_test.go @@ -173,185 +173,3 @@ func TestUser_UnmarshalBinary(t *testing.T) { }) } } - -func TestCustomDate_UnmarshalJSON(t *testing.T) { - testTable := []struct { - name string - input string - expectErr bool - expected time.Time - }{ - { - name: "ok", - input: `"2024-02-25"`, - expected: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC), - }, - { - name: "invalid_format", - input: `"25-02-2024"`, - expectErr: true, - }, - { - name: "invalid_characters", - input: `"invalid-date"`, - expectErr: true, - }, - { - name: "empty_string", - input: `""`, - expectErr: true, - }, - { - name: "whitespace", - input: `" "`, - expectErr: true, - }, - } - - for _, tc := range testTable { - t.Run(tc.name, func(t *testing.T) { - var cd CustomDate - err := json.Unmarshal([]byte(tc.input), &cd) - - assert.Equal(t, tc.expectErr, err != nil) - assert.True(t, cd.Time.Equal(tc.expected) || tc.expectErr) - }) - } -} - -func TestCustomDate_MarshalJSON(t *testing.T) { - testTable := []struct { - name string - date CustomDate - expected string - }{ - { - name: "ok_1", - date: CustomDate{Time: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC)}, - expected: `"2024-02-25"`, - }, - { - name: "ok_2", - date: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, - expected: `"2023-08-10"`, - }, - } - - for _, tc := range testTable { - t.Run(tc.name, func(t *testing.T) { - result, err := json.Marshal(tc.date) - assert.NoError(t, err) - assert.Equal(t, tc.expected, string(result)) - }) - } -} - -func TestCustomDate_String(t *testing.T) { - testTable := []struct { - name string - date CustomDate - expected string - }{ - { - name: "ok_1", - date: CustomDate{Time: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC)}, - expected: "2024-02-25", - }, - { - name: "ok_2", - date: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, - expected: "2023-08-10", - }, - } - - for _, tc := range testTable { - t.Run(tc.name, func(t *testing.T) { - result := tc.date.String() - - assert.Equal(t, tc.expected, result) - }) - } -} - -func TestCustomDate_Scan(t *testing.T) { - testTable := []struct { - name string - dbValue any - expected CustomDate - expectedErr error - panics bool - }{ - { - name: "ok_1", - dbValue: "2023-08-10", - expected: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, - }, - { - name: "ok_2", - dbValue: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC), - expected: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, - }, - { - name: "invalid_date", - dbValue: "", - expectedErr: &time.ParseError{}, - }, - { - name: "invalid_type", - panics: true, - }, - } - - for _, tc := range testTable { - t.Run(tc.name, func(t *testing.T) { - if tc.panics { - defer func() { - if r := recover(); r != nil { - assert.Equal(t, "incorrect time type received", r) - } - }() - } - - date := &CustomDate{} - - err := date.Scan(tc.dbValue) - - if tc.expectedErr != nil { - assert.ErrorAs(t, err, &tc.expectedErr) - } else { - assert.NoError(t, err) - } - assert.Equal(t, tc.expected, *date) - }) - } -} - -func TestCustomDate_Value(t *testing.T) { - testTable := []struct { - name string - date CustomDate - expected string - }{ - { - name: "ok_1", - date: CustomDate{Time: time.Date(2024, 2, 25, 0, 0, 0, 0, time.UTC)}, - expected: "2024-02-25", - }, - { - name: "ok_2", - date: CustomDate{Time: time.Date(2023, 8, 10, 0, 0, 0, 0, time.UTC)}, - expected: "2023-08-10", - }, - } - - for _, tc := range testTable { - t.Run(tc.name, func(t *testing.T) { - result, err := tc.date.Value() - assert.NoError(t, err) - - date, ok := result.(string) - assert.True(t, ok) - assert.Equal(t, tc.expected, date) - }) - } -} diff --git a/internal/domain/uuid.go b/internal/domain/uuid.go index 3d60ea8..ed4fefa 100644 --- a/internal/domain/uuid.go +++ b/internal/domain/uuid.go @@ -5,15 +5,13 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" ) -type UUID struct { - uuid.NullUUID -} +type UUID uuid.NullUUID -func (u UUID) IsZero() bool { +func (u *UUID) IsZero() bool { return !u.Valid } -func (u UUID) MarshalBSONValue() (byte, []byte, error) { +func (u *UUID) MarshalBSONValue() (byte, []byte, error) { if !u.Valid { return byte(bson.TypeString), nil, nil } @@ -46,7 +44,7 @@ func (u *UUID) UnmarshalBSONValue(_ byte, data []byte) error { return nil } -func (u UUID) String() string { +func (u *UUID) String() string { if !u.Valid { return "" } @@ -54,8 +52,19 @@ func (u UUID) String() string { } func GenerateUUID() UUID { - return UUID{NullUUID: uuid.NullUUID{ - UUID: uuid.New(), - Valid: true, - }} + return UUID( + uuid.NullUUID{ + UUID: uuid.New(), + Valid: true, + }, + ) +} + +func GenerateTestUUID() UUID { + return UUID( + uuid.NullUUID{ + UUID: uuid.MustParse("5c94e2c5-7615-4fdc-a2e2-00f71ae1e380"), + Valid: true, + }, + ) } diff --git a/internal/domain/uuid_test.go b/internal/domain/uuid_test.go index 9a10d3b..1991ae4 100644 --- a/internal/domain/uuid_test.go +++ b/internal/domain/uuid_test.go @@ -1,10 +1,11 @@ package domain import ( + "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/v2/bson" - "testing" ) func TestUUID_IsZero(t *testing.T) { @@ -19,7 +20,7 @@ func TestUUID_IsZero(t *testing.T) { }, { name: "non_zero", - uuid: UUID{NullUUID: uuid.NullUUID{Valid: true}}, + uuid: UUID(uuid.NullUUID{Valid: true}), expected: false, }, } @@ -44,10 +45,12 @@ func TestUUID_MarshalBSONValue(t *testing.T) { }, { name: "non_zero", - uuid: UUID{NullUUID: uuid.NullUUID{ - UUID: [16]byte{183, 27, 35, 52, 27, 195, 79, 180, 160, 72, 9, 103, 64, 182, 247, 131}, - Valid: true, - }}, + uuid: UUID( + uuid.NullUUID{ + UUID: [16]byte{183, 27, 35, 52, 27, 195, 79, 180, 160, 72, 9, 103, 64, 182, 247, 131}, + Valid: true, + }, + ), expectedUuid: []byte{37, 0, 0, 0, 98, 55, 49, 98, 50, 51, 51, 52, 45, 49, 98, 99, 51, 45, 52, 102, 98, 52, 45, 97, 48, 52, 56, 45, 48, 57, 54, 55, 52, 48, 98, 54, 102, 55, 56, 51, 0}, }, } @@ -76,10 +79,12 @@ func TestUUID_UnmarshalBSONValue(t *testing.T) { { name: "valid_not_zero", data: []byte{37, 0, 0, 0, 98, 55, 49, 98, 50, 51, 51, 52, 45, 49, 98, 99, 51, 45, 52, 102, 98, 52, 45, 97, 48, 52, 56, 45, 48, 57, 54, 55, 52, 48, 98, 54, 102, 55, 56, 51, 0}, - expectedUuid: UUID{NullUUID: uuid.NullUUID{ - UUID: [16]byte{183, 27, 35, 52, 27, 195, 79, 180, 160, 72, 9, 103, 64, 182, 247, 131}, - Valid: true, - }}, + expectedUuid: UUID( + uuid.NullUUID{ + UUID: [16]byte{183, 27, 35, 52, 27, 195, 79, 180, 160, 72, 9, 103, 64, 182, 247, 131}, + Valid: true, + }, + ), }, { name: "parsing_error", @@ -111,10 +116,12 @@ func TestUUID_String(t *testing.T) { }, { name: "non_zero", - uuid: UUID{NullUUID: uuid.NullUUID{ - UUID: [16]byte{183, 27, 35, 52, 27, 195, 79, 180, 160, 72, 9, 103, 64, 182, 247, 131}, - Valid: true, - }}, + uuid: UUID( + uuid.NullUUID{ + UUID: [16]byte{183, 27, 35, 52, 27, 195, 79, 180, 160, 72, 9, 103, 64, 182, 247, 131}, + Valid: true, + }, + ), expectedUuid: "b71b2334-1bc3-4fb4-a048-096740b6f783", }, } @@ -131,3 +138,12 @@ func TestUUID_String(t *testing.T) { func Test_GenerateUUID(t *testing.T) { _ = GenerateUUID() } + +func Test_GenerateTestUUID(t *testing.T) { + uid1 := GenerateTestUUID() + uid2 := GenerateTestUUID() + uid3 := GenerateTestUUID() + + assert.Equal(t, uid1, uid2) + assert.Equal(t, uid1, uid3) +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go index c4f21fb..be1c042 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -38,7 +38,7 @@ func NewLogger(cfg config.Config) *Logger { logger.SetOutput(io.Discard) logger.SetLevel(log.PanicLevel) default: - logger.Fatal("incorrect MODE was specified") + logger.Fatalf("incorrect MODE was specified: %s", cfg.Mode) } logger.Infof("logger initialized") diff --git a/internal/repository/messages.go b/internal/repository/messages.go index 0e033db..b461237 100644 --- a/internal/repository/messages.go +++ b/internal/repository/messages.go @@ -48,82 +48,91 @@ type UnpinMessage struct { } type GetSome struct { - ChatID, MessageNumberFrom, MessagesToGet int + ChatID, Offset, Limit int } func newMessageRepo(db *sqlx.DB, mdb *mongo.Database, l *logger.Logger) *messageRepository { return &messageRepository{db: db, mc: mdb.Collection("message"), l: l} } -func (r *messageRepository) Get(ID domain.UUID) (domain.MessageRaw, error) { - var msg domain.MessageRaw +func (r *messageRepository) Get(ID domain.UUID) (domain.RawMessage, error) { + var msg domain.RawMessage res := r.mc.FindOne(nil, bson.M{"id": ID}) if err := res.Decode(&msg); err != nil { - r.l.Errorf("getting message: %s", err.Error()) - return domain.MessageRaw{}, domain.InternalServerError + r.l.Errorf("getting message: %s", err) + return domain.RawMessage{}, domain.InternalServerError } return msg, nil } -func (r *messageRepository) GetByIDs(IDs []domain.UUID) ([]domain.MessageRaw, error) { - var messages []domain.MessageRaw +func (r *messageRepository) GetByIDs(IDs []domain.UUID) ([]domain.RawMessage, error) { + var messages []domain.RawMessage res, err := r.mc.Find(nil, bson.M{"visibility": true, "id": bson.M{"$in": IDs}}) if err != nil { - r.l.Errorf("getting messages by ids: %s", err.Error()) + r.l.Errorf("getting messages by ids: %s", err) return nil, domain.InternalServerError } if err = res.All(nil, &messages); err != nil { - r.l.Errorf("decoding messages by ids: %s", err.Error()) + r.l.Errorf("decoding messages by ids: %s", err) return nil, domain.InternalServerError } return messages, nil } -func (r *messageRepository) GetSome(messagesInfo GetSome) ([]domain.MessageRaw, error) { - var messages []domain.MessageRaw - findOptions := options.Find().SetSort(bson.M{"created_at": -1}).SetLimit(int64(messagesInfo.MessagesToGet)).SetSkip(int64(messagesInfo.MessageNumberFrom)) +func (r *messageRepository) GetSome(messagesInfo GetSome) ([]domain.RawMessage, error) { + var messages []domain.RawMessage + findOptions := options.Find().SetSort(bson.M{"created_at": -1}).SetLimit(int64(messagesInfo.Limit)).SetSkip(int64(messagesInfo.Offset)) res, err := r.mc.Find(nil, bson.M{"visibility": true, "chat_id": messagesInfo.ChatID}, findOptions) if err != nil { - r.l.Errorf("getting some messages: %s", err.Error()) + r.l.Errorf("getting some messages: %s", err) return nil, domain.InternalServerError } if err = res.All(nil, &messages); err != nil { - r.l.Errorf("decoding some messages: %s", err.Error()) + r.l.Errorf("decoding some messages: %s", err) return nil, domain.InternalServerError } return messages, nil } -func (r *messageRepository) Send(message SendMessage) (domain.UUID, error) { +func (r *messageRepository) Send(message SendMessage) (domain.RawMessage, error) { message.ID = domain.GenerateUUID() message.CreatedAt = time.Now() message.Visibility = true _, err := r.mc.InsertOne(nil, message) if err != nil { - r.l.Errorf("inserting message: %s", err.Error()) - return domain.UUID{}, domain.InternalServerError + r.l.Errorf("inserting message: %s", err) + return domain.RawMessage{}, domain.InternalServerError } - return message.ID, nil + return domain.RawMessage{ + ID: message.ID, + Message: message.Message, + ImageUrl: message.ImageUrl, + ChatID: message.ChatID, + UserID: message.UserID, + CreatedAt: message.CreatedAt, + AnswerID: message.AnswerID, + AnswerMessage: message.AnswerMessage, + AnswerImageUrl: message.AnswerImageUrl, + }, nil } func (r *messageRepository) Delete(ID domain.UUID) error { - if _, err := r.mc.UpdateOne(nil, bson.M{"id": ID}, bson.M{"$set": bson.M{"visibility": false}}); err != nil { - r.l.Errorf("deleting message: %s", err.Error()) + r.l.Errorf("deleting message: %s", err) return domain.InternalServerError } _, err := r.mc.UpdateMany(nil, bson.M{"answer_id": ID}, bson.M{"$set": bson.M{"answer_message": nil, "answer_image_url": nil}}) if err != nil { - r.l.Errorf("deleting message: %s", err.Error()) + r.l.Errorf("deleting message: %s", err) return domain.InternalServerError } @@ -132,13 +141,13 @@ func (r *messageRepository) Delete(ID domain.UUID) error { func (r *messageRepository) Edit(newMessage EditMessage) error { _, err := r.mc.UpdateOne(nil, bson.M{"id": newMessage.ID}, bson.M{"$set": bson.M{"message": newMessage.Message, "image_url": newMessage.ImageUrl}}) if err != nil { - r.l.Errorf("edditing message: %s", err.Error()) + r.l.Errorf("edditing message: %s", err) return domain.InternalServerError } _, err = r.mc.UpdateMany(nil, bson.M{"answer_id": newMessage.ID}, bson.M{"$set": bson.M{"answer_message": newMessage.Message, "answer_image_url": newMessage.ImageUrl}}) if err != nil { - r.l.Errorf("edditing message: %s", err.Error()) + r.l.Errorf("edditing message: %s", err) return domain.InternalServerError } @@ -152,7 +161,7 @@ func (r *messageRepository) Pin(msg PinMessage) error { return domain.MessageAlreadyPinnedError } - r.l.Errorf("pining message: %s", err.Error()) + r.l.Errorf("pining message: %s", err) return domain.InternalServerError } @@ -162,7 +171,7 @@ func (r *messageRepository) Pin(msg PinMessage) error { func (r *messageRepository) Unpin(msg UnpinMessage) error { query := `DELETE FROM pinned_message WHERE chat_id = $1 AND message_id = $2` if _, err := r.db.Exec(query, msg.ChatID, msg.MessageID); err != nil { - r.l.Errorf("unpining message: %s", err.Error()) + r.l.Errorf("unpining message: %s", err) return domain.InternalServerError } @@ -173,7 +182,7 @@ func (r *messageRepository) GetPinnedIDs(chatID int) ([]domain.UUID, error) { var IDs []domain.UUID query := `SELECT message_id FROM pinned_message WHERE chat_id = $1` if err := r.db.Select(&IDs, query, chatID); err != nil { - r.l.Errorf("getting pinned ids: %s", err.Error()) + r.l.Errorf("getting pinned ids: %s", err) return []domain.UUID{}, domain.InternalServerError } diff --git a/internal/repository/mocks/mock.go b/internal/repository/mocks/mock.go index 0401a82..85fe4b9 100644 --- a/internal/repository/mocks/mock.go +++ b/internal/repository/mocks/mock.go @@ -22,7 +22,6 @@ import ( type MockUser struct { ctrl *gomock.Controller recorder *MockUserMockRecorder - isgomock struct{} } // MockUserMockRecorder is the mock recorder for MockUser. @@ -106,7 +105,6 @@ func (mr *MockUserMockRecorder) Register(email, hashedPassword, username, dateOf type MockChat struct { ctrl *gomock.Controller recorder *MockChatMockRecorder - isgomock struct{} } // MockChatMockRecorder is the mock recorder for MockChat. @@ -130,7 +128,6 @@ func (m *MockChat) EXPECT() *MockChatMockRecorder { type MockMessage struct { ctrl *gomock.Controller recorder *MockMessageMockRecorder - isgomock struct{} } // MockMessageMockRecorder is the mock recorder for MockMessage. @@ -179,10 +176,10 @@ func (mr *MockMessageMockRecorder) Edit(newMessage any) *gomock.Call { } // Get mocks base method. -func (m *MockMessage) Get(ID domain.UUID) (domain.MessageRaw, error) { +func (m *MockMessage) Get(ID domain.UUID) (domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", ID) - ret0, _ := ret[0].(domain.MessageRaw) + ret0, _ := ret[0].(domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -194,10 +191,10 @@ func (mr *MockMessageMockRecorder) Get(ID any) *gomock.Call { } // GetByIDs mocks base method. -func (m *MockMessage) GetByIDs(IDs []domain.UUID) ([]domain.MessageRaw, error) { +func (m *MockMessage) GetByIDs(IDs []domain.UUID) ([]domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetByIDs", IDs) - ret0, _ := ret[0].([]domain.MessageRaw) + ret0, _ := ret[0].([]domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -224,10 +221,10 @@ func (mr *MockMessageMockRecorder) GetPinnedIDs(chatID any) *gomock.Call { } // GetSome mocks base method. -func (m *MockMessage) GetSome(messagesInfo repository.GetSome) ([]domain.MessageRaw, error) { +func (m *MockMessage) GetSome(messagesInfo repository.GetSome) ([]domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSome", messagesInfo) - ret0, _ := ret[0].([]domain.MessageRaw) + ret0, _ := ret[0].([]domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -253,10 +250,10 @@ func (mr *MockMessageMockRecorder) Pin(msg any) *gomock.Call { } // Send mocks base method. -func (m *MockMessage) Send(message repository.SendMessage) (domain.UUID, error) { +func (m *MockMessage) Send(message repository.SendMessage) (domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Send", message) - ret0, _ := ret[0].(domain.UUID) + ret0, _ := ret[0].(domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -285,7 +282,6 @@ func (mr *MockMessageMockRecorder) Unpin(msg any) *gomock.Call { type MockRepo struct { ctrl *gomock.Controller recorder *MockRepoMockRecorder - isgomock struct{} } // MockRepoMockRecorder is the mock recorder for MockRepo. @@ -349,10 +345,10 @@ func (mr *MockRepoMockRecorder) FindOne(username, email any) *gomock.Call { } // Get mocks base method. -func (m *MockRepo) Get(ID domain.UUID) (domain.MessageRaw, error) { +func (m *MockRepo) Get(ID domain.UUID) (domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", ID) - ret0, _ := ret[0].(domain.MessageRaw) + ret0, _ := ret[0].(domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -394,10 +390,10 @@ func (mr *MockRepoMockRecorder) GetByID(userID any) *gomock.Call { } // GetByIDs mocks base method. -func (m *MockRepo) GetByIDs(IDs []domain.UUID) ([]domain.MessageRaw, error) { +func (m *MockRepo) GetByIDs(IDs []domain.UUID) ([]domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetByIDs", IDs) - ret0, _ := ret[0].([]domain.MessageRaw) + ret0, _ := ret[0].([]domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -424,10 +420,10 @@ func (mr *MockRepoMockRecorder) GetPinnedIDs(chatID any) *gomock.Call { } // GetSome mocks base method. -func (m *MockRepo) GetSome(messagesInfo repository.GetSome) ([]domain.MessageRaw, error) { +func (m *MockRepo) GetSome(messagesInfo repository.GetSome) ([]domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSome", messagesInfo) - ret0, _ := ret[0].([]domain.MessageRaw) + ret0, _ := ret[0].([]domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -468,10 +464,10 @@ func (mr *MockRepoMockRecorder) Register(email, hashedPassword, username, dateOf } // Send mocks base method. -func (m *MockRepo) Send(message repository.SendMessage) (domain.UUID, error) { +func (m *MockRepo) Send(message repository.SendMessage) (domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Send", message) - ret0, _ := ret[0].(domain.UUID) + ret0, _ := ret[0].(domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 09f7978..9b59203 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,10 +21,10 @@ type User interface { type Chat interface{} type Message interface { - Get(ID domain.UUID) (domain.MessageRaw, error) - GetByIDs(IDs []domain.UUID) ([]domain.MessageRaw, error) - GetSome(messagesInfo GetSome) ([]domain.MessageRaw, error) - Send(message SendMessage) (domain.UUID, error) + Get(ID domain.UUID) (domain.RawMessage, error) + GetByIDs(IDs []domain.UUID) ([]domain.RawMessage, error) + GetSome(messagesInfo GetSome) ([]domain.RawMessage, error) + Send(message SendMessage) (domain.RawMessage, error) Delete(ID domain.UUID) error Edit(newMessage EditMessage) error Pin(msg PinMessage) error diff --git a/internal/repository/users.go b/internal/repository/users.go index b94120c..89a1b84 100644 --- a/internal/repository/users.go +++ b/internal/repository/users.go @@ -42,7 +42,7 @@ func (r *userRepository) GetAll(username string) ([]domain.User, error) { query := fmt.Sprintf(`SELECT * FROM users WHERE username ILIKE $1 AND role != %d`, domain.AdminUser) err := r.db.Select(&users, query, username) if err != nil { - r.l.Errorf("getting users: %s", err.Error()) + r.l.Errorf("getting users: %s", err) return nil, domain.InternalServerError } return users, nil @@ -84,18 +84,18 @@ func (r *userRepository) Register(email, hashedPassword, username string, dateOf tx, err := r.db.Beginx() if err != nil { - r.l.Errorf("user registration: tx begin: %s", err.Error()) + r.l.Errorf("user registration: tx begin: %s", err) return domain.User{}, domain.InternalServerError } defer func() { if err != nil { var pgError *pgconn.PgError if !errors.As(err, &pgError) { - r.l.Errorf("user registration: %s", err.Error()) + r.l.Errorf("user registration: %s", err) } if err = tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) { - r.l.Errorf("user registration: tx rollback: %s", err.Error()) + r.l.Errorf("user registration: tx rollback: %s", err) } } }() diff --git a/internal/service/auth.go b/internal/service/auth.go index 1e302e5..85e4981 100644 --- a/internal/service/auth.go +++ b/internal/service/auth.go @@ -81,7 +81,7 @@ func (s *authService) EncodeAuthToken(userID int) (string, error) { func (s *authService) HashPassword(p string) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(p)[:maxBcryptPasswordLen], bcrypt.DefaultCost) if err != nil { - s.l.Errorf("error during password hashing: %s", err.Error()) + s.l.Errorf("error during password hashing: %s", err) return "", domain.HashingError } return string(hash), nil diff --git a/internal/service/cache.go b/internal/service/cache.go index 54152a7..cbab048 100644 --- a/internal/service/cache.go +++ b/internal/service/cache.go @@ -5,6 +5,7 @@ import ( "encoding" "errors" "github.com/redis/go-redis/v9" + "sync" "time" "git.urec56.ru/urec/chat_back_go/internal/domain" @@ -12,25 +13,41 @@ import ( ) type cacheService struct { - l *logger.Logger - r redis.UniversalClient + l *logger.Logger + r redis.UniversalClient + storage *sync.Map +} + +type userCache struct { + u []byte // domain.User + expiresAt time.Time } func newCacheService(r redis.UniversalClient, l *logger.Logger) *cacheService { - return &cacheService{r: r, l: l} + return &cacheService{r: r, l: l, storage: &sync.Map{}} } -func (c *cacheService) Get(key string, v encoding.BinaryUnmarshaler) error { - return c.GetCtx(context.Background(), key, v) +func (c *cacheService) Get(key string, v encoding.BinaryUnmarshaler, isFast bool) error { + return c.GetCtx(context.Background(), key, v, isFast) } -func (c *cacheService) GetCtx(ctx context.Context, key string, v encoding.BinaryUnmarshaler) error { +func (c *cacheService) GetCtx(ctx context.Context, key string, v encoding.BinaryUnmarshaler, isFast bool) error { + if isFast { + item, ok := c.storage.Load(key) + if ok { + if userData, ok := item.(userCache); ok && userData.expiresAt.After(time.Now()) { + err := v.UnmarshalBinary(userData.u) + return err + } + } + } + err := c.r.Get(ctx, key).Scan(v) if err != nil { if errors.Is(err, redis.Nil) { return domain.NoKeyFoundError } - c.l.Errorf("getting key %s: %s", key, err.Error()) + c.l.Errorf("getting key %s: %s", key, err) return domain.InternalServerError } return nil @@ -44,5 +61,12 @@ func (c *cacheService) SetCtx(ctx context.Context, key string, v encoding.Binary if err := c.r.SetEx(ctx, key, v, ex).Err(); err != nil { return err } + + data, err := v.MarshalBinary() + if err != nil { + return err + } + + c.storage.Store(key, userCache{u: data, expiresAt: time.Now().Add(time.Minute)}) return nil } diff --git a/internal/service/messages.go b/internal/service/messages.go index de0fbac..ddeb9f3 100644 --- a/internal/service/messages.go +++ b/internal/service/messages.go @@ -18,9 +18,10 @@ func newMessageService(repo repository.Message, u User, l *logger.Logger) *messa return &messageService{repo: repo, u: u, l: l} } -func (s *messageService) AddAvatarImageAndUsernameToMessage(msgRaw domain.MessageRaw) (domain.Message, error) { - u, err := s.u.GetCachedUser(msgRaw.UserID) +func (s *messageService) AddAvatarImageAndUsernameToMessage(msgRaw domain.RawMessage) (domain.Message, error) { + u, err := s.u.Get(msgRaw.UserID, true) if err != nil { + s.l.Infof("error adding image and username to message: %s", err) return domain.Message{}, domain.InternalServerError } @@ -39,7 +40,7 @@ func (s *messageService) AddAvatarImageAndUsernameToMessage(msgRaw domain.Messag }, nil } -func (s *messageService) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.MessageRaw) []domain.Message { +func (s *messageService) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.RawMessage) []domain.Message { msgs := make([]domain.Message, 0, len(msgsRaw)) for _, v := range msgsRaw { msg, err := s.AddAvatarImageAndUsernameToMessage(v) @@ -51,10 +52,19 @@ func (s *messageService) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.Me return msgs } +func (s *messageService) GetRaw(ID domain.UUID) (domain.RawMessage, error) { + rawMessage, err := s.repo.Get(ID) + if err != nil { + s.l.Infof("error getting message: %s", err) + return domain.RawMessage{}, domain.InternalServerError + } + + return rawMessage, nil +} + func (s *messageService) GetMessage(ID domain.UUID) (domain.Message, error) { rawMessage, err := s.GetRaw(ID) if err != nil { - s.l.Infof("error getting message: %s", err.Error()) return domain.Message{}, domain.InternalServerError } @@ -66,26 +76,16 @@ func (s *messageService) GetMessage(ID domain.UUID) (domain.Message, error) { return newMessage, nil } -func (s *messageService) GetRaw(ID domain.UUID) (domain.MessageRaw, error) { - rawMessage, err := s.repo.Get(ID) - if err != nil { - s.l.Infof("error getting message: %s", err.Error()) - return domain.MessageRaw{}, domain.InternalServerError - } - - return rawMessage, nil -} - func (s *messageService) GetPinned(chatID int) ([]domain.Message, error) { IDs, err := s.repo.GetPinnedIDs(chatID) if err != nil { - s.l.Infof("error getting pinned ids: %s", err.Error()) + s.l.Infof("error getting pinned ids: %s", err) return nil, domain.InternalServerError } rawMessages, err := s.repo.GetByIDs(IDs) if err != nil { - s.l.Infof("error messages by ids: %s", err.Error()) + s.l.Infof("error messages by ids: %s", err) return nil, domain.InternalServerError } @@ -94,11 +94,11 @@ func (s *messageService) GetPinned(chatID int) ([]domain.Message, error) { return messages, nil } -func (s *messageService) GetSome(chatID, messageNumberFrom, messagesToGet int) ([]domain.Message, error) { - messagesRaw, err := s.repo.GetSome(repository.GetSome{ChatID: chatID, MessageNumberFrom: messageNumberFrom, MessagesToGet: messagesToGet}) +func (s *messageService) GetSome(chatID, offset, limit int) ([]domain.Message, error) { + messagesRaw, err := s.repo.GetSome(repository.GetSome{ChatID: chatID, Offset: offset, Limit: limit}) if err != nil { - s.l.Infof("error getting some messages: %s", err.Error()) - return nil, err + s.l.Infof("error getting some messages: %s", err) + return nil, domain.InternalServerError } messages := s.AddAvatarImageAndUsernameToMessages(messagesRaw) @@ -107,28 +107,28 @@ func (s *messageService) GetSome(chatID, messageNumberFrom, messagesToGet int) ( } func (s *messageService) SendMessage(userID, chatID int, message domain.SendMessage) (domain.Message, error) { - var messageID domain.UUID + var rawMessage domain.RawMessage var err error if message.Answer.IsZero() { - messageID, err = s.repo.Send(repository.SendMessage{ + rawMessage, err = s.repo.Send(repository.SendMessage{ Message: message.Message, ImageUrl: message.ImageUrl, ChatID: chatID, UserID: userID, }) if err != nil { - s.l.Infof("error sending message: %s", err.Error()) + s.l.Infof("error sending message: %s", err) return domain.Message{}, domain.InternalServerError } } else { answerMessage, err := s.GetRaw(message.Answer) if err != nil { - s.l.Infof("error getting message: %s", err.Error()) + s.l.Infof("error getting message: %s", err) return domain.Message{}, domain.InternalServerError } - messageID, err = s.repo.Send(repository.SendMessage{ + rawMessage, err = s.repo.Send(repository.SendMessage{ Message: message.Message, ImageUrl: message.ImageUrl, ChatID: chatID, @@ -138,14 +138,14 @@ func (s *messageService) SendMessage(userID, chatID int, message domain.SendMess AnswerImageUrl: answerMessage.ImageUrl, }) if err != nil { - s.l.Infof("error sending message: %s", err.Error()) + s.l.Infof("error sending message: %s", err) return domain.Message{}, domain.InternalServerError } } - newMessage, err := s.GetMessage(messageID) + newMessage, err := s.AddAvatarImageAndUsernameToMessage(rawMessage) if err != nil { - s.l.Infof("error getting message: %s", err.Error()) + s.l.Infof("error getting message: %s", err) return domain.Message{}, domain.InternalServerError } @@ -154,7 +154,7 @@ func (s *messageService) SendMessage(userID, chatID int, message domain.SendMess func (s *messageService) Delete(ID domain.UUID) error { if err := s.repo.Delete(ID); err != nil { - s.l.Infof("error deleting message: %s", err.Error()) + s.l.Infof("error deleting message: %s", err) return domain.InternalServerError } @@ -163,7 +163,7 @@ func (s *messageService) Delete(ID domain.UUID) error { func (s *messageService) Edit(ID domain.UUID, newMessage, newImageUrl string) error { if err := s.repo.Edit(repository.EditMessage{ID: ID, Message: newMessage, ImageUrl: newImageUrl}); err != nil { - s.l.Infof("error editing message: %s", err.Error()) + s.l.Infof("error editing message: %s", err) return domain.InternalServerError } @@ -177,13 +177,13 @@ func (s *messageService) Pin(chatID, userID int, messageID domain.UUID) (domain. return domain.Message{}, err } - s.l.Infof("error pinning message: %s", err.Error()) + s.l.Infof("error pinning message: %s", err) return domain.Message{}, domain.InternalServerError } message, err := s.GetMessage(messageID) if err != nil { - s.l.Infof("error getting message: %s", err.Error()) + s.l.Infof("error getting message: %s", err) return domain.Message{}, domain.InternalServerError } @@ -193,7 +193,7 @@ func (s *messageService) Pin(chatID, userID int, messageID domain.UUID) (domain. func (s *messageService) Unpin(chatID int, messageID domain.UUID) error { err := s.repo.Unpin(repository.UnpinMessage{ChatID: chatID, MessageID: messageID}) if err != nil { - s.l.Infof("error unpinning message: %s", err.Error()) + s.l.Infof("error unpinning message: %s", err) return domain.InternalServerError } diff --git a/internal/service/messages_test.go b/internal/service/messages_test.go new file mode 100644 index 0000000..39f20e5 --- /dev/null +++ b/internal/service/messages_test.go @@ -0,0 +1,1338 @@ +package service + +import ( + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "testing" + "time" + + "git.urec56.ru/urec/chat_back_go/config" + "git.urec56.ru/urec/chat_back_go/internal/domain" + "git.urec56.ru/urec/chat_back_go/internal/logger" + "git.urec56.ru/urec/chat_back_go/internal/repository" + mock_repository "git.urec56.ru/urec/chat_back_go/internal/repository/mocks" + mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks" +) + +func Test_newMessageService(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + l := logger.NewLogger(config.Config{Mode: "TEST"}) + u := &userService{} + + serv := newMessageService(repo, u, l) + + assert.Equal(t, &messageService{repo: repo, u: u, l: l}, serv) +} + +func TestMessageService_AddAvatarImageAndUsernameToMessage(t *testing.T) { + type userServiceBehavior func(u *mock_service.MockUser, userID int, user domain.User, err error) + testTable := []struct { + name string + userServiceBehavior userServiceBehavior + inputMsg domain.RawMessage + serviceInput int + serviceUser domain.User + serviceErr error + expectedMsg domain.Message + expectedErr error + }{ + { + name: "ok", + userServiceBehavior: func(u *mock_service.MockUser, userID int, user domain.User, err error) { + u.EXPECT().Get(userID, true).Return(user, err) + }, + inputMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "some text", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "some sendMsg", + AnswerImageUrl: "image2", + }, + serviceInput: 2, + serviceUser: domain.User{ + ID: 1, + Role: 1, + Username: "urec", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: false, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + expectedMsg: domain.Message{ + ID: domain.GenerateTestUUID(), + Message: "some text", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + Username: "urec", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "some sendMsg", + AnswerImageUrl: "image2", + }, + }, + { + name: "any_error", + userServiceBehavior: func(u *mock_service.MockUser, userID int, user domain.User, err error) { + u.EXPECT().Get(userID, true).Return(user, err) + }, + inputMsg: domain.RawMessage{UserID: 2}, + serviceInput: 2, + serviceErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + u := mock_service.NewMockUser(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.userServiceBehavior(u, tc.serviceInput, tc.serviceUser, tc.serviceErr) + + serv := &messageService{u: u, l: log} + + res, err := serv.AddAvatarImageAndUsernameToMessage(tc.inputMsg) + + assert.Equal(t, tc.expectedMsg, res) + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_AddAvatarImageAndUsernameToMessages(t *testing.T) { + type userServiceBehavior func(u *mock_service.MockUser, userID int, user domain.User, err error) + testTable := []struct { + name string + userServiceBehavior userServiceBehavior + inputMsgs []domain.RawMessage + serviceInput int + serviceUser domain.User + serviceErr error + expectedMsgs []domain.Message + }{ + { + name: "ok", + userServiceBehavior: func(u *mock_service.MockUser, userID int, user domain.User, err error) { + u.EXPECT().Get(userID, true).Return(user, err) + }, + inputMsgs: []domain.RawMessage{ + { + ID: domain.GenerateTestUUID(), + Message: "some text", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "some sendMsg", + AnswerImageUrl: "image2", + }, + }, + serviceInput: 2, + serviceUser: domain.User{ + ID: 1, + Role: 1, + Username: "urec", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: false, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + expectedMsgs: []domain.Message{ + { + ID: domain.GenerateTestUUID(), + Message: "some text", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + Username: "urec", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "some sendMsg", + AnswerImageUrl: "image2", + }, + }, + }, + { + name: "any_error", + userServiceBehavior: func(u *mock_service.MockUser, userID int, user domain.User, err error) { + u.EXPECT().Get(userID, true).Return(user, err) + }, + inputMsgs: []domain.RawMessage{{UserID: 2}}, + serviceInput: 2, + serviceErr: domain.AnyError, + expectedMsgs: []domain.Message{}, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + u := mock_service.NewMockUser(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.userServiceBehavior(u, tc.serviceInput, tc.serviceUser, tc.serviceErr) + + serv := &messageService{u: u, l: log} + + msgs := serv.AddAvatarImageAndUsernameToMessages(tc.inputMsgs) + + assert.Equal(t, tc.expectedMsgs, msgs) + }) + } +} + +func TestMessageService_GetRaw(t *testing.T) { + type repoBehavior func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) + + testTable := []struct { + name string + repoBehavior repoBehavior + inputID domain.UUID + repoInput domain.UUID + repoMsg domain.RawMessage + repoErr error + expectedMessage domain.RawMessage + expectedErr error + }{ + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(input).Return(msg, err) + }, + inputID: domain.GenerateTestUUID(), + repoInput: domain.GenerateTestUUID(), + repoMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg1", + AnswerImageUrl: "image2", + }, + expectedMessage: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg1", + AnswerImageUrl: "image2", + }, + }, + { + name: "any_error", + repoBehavior: func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(input).Return(msg, err) + }, + inputID: domain.GenerateTestUUID(), + repoInput: domain.GenerateTestUUID(), + repoErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoBehavior(repo, tc.repoInput, tc.repoMsg, tc.repoErr) + + serv := &messageService{repo: repo, l: log} + + msg, err := serv.GetRaw(tc.inputID) + + assert.Equal(t, msg, tc.expectedMessage) + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_GetMessage(t *testing.T) { + type repoBehavior func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) + type serviceBehavior func(s *mock_service.MockUser, userID int, user domain.User, err error) + + testTable := []struct { + name string + repoBehavior repoBehavior + serviceBehavior serviceBehavior + inputID domain.UUID + repoInput domain.UUID + repoMsg domain.RawMessage + serviceInput int + serviceUser domain.User + serviceErr error + repoErr error + expectedMessage domain.Message + expectedErr error + }{ + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(input).Return(msg, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + inputID: domain.GenerateTestUUID(), + repoInput: domain.GenerateTestUUID(), + repoMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg1", + AnswerImageUrl: "image2", + }, + serviceInput: 2, + serviceUser: domain.User{ + ID: 2, + Role: 1, + Username: "username", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "image", + BlackPhoenix: true, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + expectedMessage: domain.Message{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + Username: "username", + AvatarImage: "image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg1", + AnswerImageUrl: "image2", + }, + }, + { + name: "repoErr", + repoBehavior: func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(input).Return(msg, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + inputID: domain.GenerateTestUUID(), + repoInput: domain.GenerateTestUUID(), + repoErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, input domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(input).Return(msg, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + inputID: domain.GenerateTestUUID(), + repoInput: domain.GenerateTestUUID(), + repoMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 1, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg1", + AnswerImageUrl: "image2", + }, + serviceInput: 2, + serviceErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + u := mock_service.NewMockUser(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoBehavior(repo, tc.repoInput, tc.repoMsg, tc.repoErr) + tc.serviceBehavior(u, tc.serviceInput, tc.serviceUser, tc.serviceErr) + + serv := &messageService{repo: repo, u: u, l: log} + + msg, err := serv.GetMessage(tc.inputID) + + assert.Equal(t, msg, tc.expectedMessage) + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_GetPinned(t *testing.T) { + type repoGetPinnedIDsBehavior func(r *mock_repository.MockMessage, chatID int, IDs []domain.UUID, err error) + type repoGetByIDsBehavior func(r *mock_repository.MockMessage, IDs []domain.UUID, msgs []domain.RawMessage, err error) + type userServiceBehavior func(s *mock_service.MockUser, userID int, user domain.User, err error) + type repo1Data struct { + chatID int + IDs []domain.UUID + err error + } + type repo2Data struct { + IDs []domain.UUID + msgs []domain.RawMessage + err error + } + type serviceData struct { + userID int + user domain.User + err error + } + + testTable := []struct { + name string + repoGetPinnedIDsBehavior repoGetPinnedIDsBehavior + repoGetByIDsBehavior repoGetByIDsBehavior + userServiceBehavior userServiceBehavior + repo1Data repo1Data + repo2Data repo2Data + serviceData serviceData + chatID int + expectedMsgs []domain.Message + expectedErr error + }{ + { + name: "ok", + repoGetPinnedIDsBehavior: func(r *mock_repository.MockMessage, chatID int, IDs []domain.UUID, err error) { + r.EXPECT().GetPinnedIDs(chatID).Return(IDs, err) + }, + repoGetByIDsBehavior: func(r *mock_repository.MockMessage, IDs []domain.UUID, msgs []domain.RawMessage, err error) { + r.EXPECT().GetByIDs(IDs).Return(msgs, err) + }, + userServiceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + repo1Data: repo1Data{ + chatID: 32, + IDs: []domain.UUID{domain.GenerateTestUUID()}, + }, + repo2Data: repo2Data{ + IDs: []domain.UUID{domain.GenerateTestUUID()}, + msgs: []domain.RawMessage{ + { + ID: domain.GenerateTestUUID(), + Message: "message", + ImageUrl: "image", + ChatID: 32, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg2", + AnswerImageUrl: "image2", + }, + }, + }, + serviceData: serviceData{ + userID: 2, + user: domain.User{ + ID: 2, + Role: 1, + Username: "username", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: true, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + }, + chatID: 32, + expectedMsgs: []domain.Message{ + { + ID: domain.GenerateTestUUID(), + Message: "message", + ImageUrl: "image", + ChatID: 32, + UserID: 2, + Username: "username", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg2", + AnswerImageUrl: "image2", + }, + }, + }, + { + name: "GetPinnedIDs_error", + repoGetPinnedIDsBehavior: func(r *mock_repository.MockMessage, chatID int, IDs []domain.UUID, err error) { + r.EXPECT().GetPinnedIDs(chatID).Return(IDs, err) + }, + repoGetByIDsBehavior: func(r *mock_repository.MockMessage, IDs []domain.UUID, msgs []domain.RawMessage, err error) {}, + userServiceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + repo1Data: repo1Data{ + chatID: 32, + err: domain.AnyError, + }, + chatID: 32, + expectedErr: domain.InternalServerError, + }, + { + name: "GetByIDs_error", + repoGetPinnedIDsBehavior: func(r *mock_repository.MockMessage, chatID int, IDs []domain.UUID, err error) { + r.EXPECT().GetPinnedIDs(chatID).Return(IDs, err) + }, + repoGetByIDsBehavior: func(r *mock_repository.MockMessage, IDs []domain.UUID, msgs []domain.RawMessage, err error) { + r.EXPECT().GetByIDs(IDs).Return(msgs, err) + }, + userServiceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + repo1Data: repo1Data{ + chatID: 32, + IDs: []domain.UUID{domain.GenerateTestUUID()}, + }, + repo2Data: repo2Data{ + IDs: []domain.UUID{domain.GenerateTestUUID()}, + err: domain.AnyError, + }, + chatID: 32, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + userServ := mock_service.NewMockUser(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoGetPinnedIDsBehavior(repo, tc.repo1Data.chatID, tc.repo1Data.IDs, tc.repo1Data.err) + tc.repoGetByIDsBehavior(repo, tc.repo2Data.IDs, tc.repo2Data.msgs, tc.repo2Data.err) + tc.userServiceBehavior(userServ, tc.serviceData.userID, tc.serviceData.user, tc.serviceData.err) + + serv := &messageService{repo: repo, u: userServ, l: log} + + msgs, err := serv.GetPinned(tc.chatID) + + assert.Equal(t, tc.expectedMsgs, msgs) + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_GetSome(t *testing.T) { + type repoBehavior func(r *mock_repository.MockMessage, messagesInfo repository.GetSome, msgs []domain.RawMessage, err error) + type serviceBehavior func(s *mock_service.MockUser, userID int, user domain.User, err error) + type repoData struct { + messagesInfo repository.GetSome + msgs []domain.RawMessage + err error + } + type serviceData struct { + userID int + user domain.User + err error + } + + testTable := []struct { + name string + repoBehavior repoBehavior + serviceBehavior serviceBehavior + repoData repoData + serviceData serviceData + chatID, offset, limit int + expectedMsgs []domain.Message + expectedErr error + }{ + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, messagesInfo repository.GetSome, msgs []domain.RawMessage, err error) { + r.EXPECT().GetSome(messagesInfo).Return(msgs, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + repoData: repoData{ + messagesInfo: repository.GetSome{ChatID: 15, Offset: 10, Limit: 15}, + msgs: []domain.RawMessage{ + { + ID: domain.GenerateTestUUID(), + Message: "message", + ImageUrl: "image", + ChatID: 15, + UserID: 2, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg2", + AnswerImageUrl: "image2", + }, + }, + }, + serviceData: serviceData{ + userID: 2, + user: domain.User{ + ID: 2, + Role: 1, + Username: "username", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: true, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + }, + chatID: 15, + offset: 10, + limit: 15, + expectedMsgs: []domain.Message{ + { + ID: domain.GenerateTestUUID(), + Message: "message", + ImageUrl: "image", + ChatID: 15, + UserID: 2, + Username: "username", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "msg2", + AnswerImageUrl: "image2", + }, + }, + }, + { + name: "repo_error", + repoBehavior: func(r *mock_repository.MockMessage, messagesInfo repository.GetSome, msgs []domain.RawMessage, err error) { + r.EXPECT().GetSome(messagesInfo).Return(msgs, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + repoData: repoData{ + messagesInfo: repository.GetSome{ChatID: 15, Offset: 10, Limit: 15}, + err: domain.AnyError, + }, + chatID: 15, + offset: 10, + limit: 15, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + userServ := mock_service.NewMockUser(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoBehavior(repo, tc.repoData.messagesInfo, tc.repoData.msgs, tc.repoData.err) + tc.serviceBehavior(userServ, tc.serviceData.userID, tc.serviceData.user, tc.serviceData.err) + + serv := &messageService{repo: repo, u: userServ, l: log} + + msgs, err := serv.GetSome(tc.chatID, tc.offset, tc.limit) + + assert.Equal(t, tc.expectedMsgs, msgs) + assert.ErrorIs(t, tc.expectedErr, err) + }) + } +} + +func TestMessageService_SendMessage(t *testing.T) { + type getRawBehavior func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) + type sendBehavior func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) + type userGetBehavior func(s *mock_service.MockUser, userID int, user domain.User, err error) + + type getRawData struct { + answerID domain.UUID + msg domain.RawMessage + err error + } + type sendData struct { + sendMsg repository.SendMessage + rawMsg domain.RawMessage + err error + } + type userGetData struct { + userID int + user domain.User + err error + } + + type input struct { + userID, chatID int + message domain.SendMessage + } + + testTable := []struct { + name string + getRawBehavior getRawBehavior + sendBehavior sendBehavior + userGetBehavior userGetBehavior + getRawData getRawData + sendData sendData + userGetData userGetData + input input + expectedMsg domain.Message + expectedErr error + }{ + { + name: "message_without_answer", + getRawBehavior: func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) {}, + sendBehavior: func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) { + r.EXPECT().Send(msg).Return(rawMsg, err) + }, + userGetBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + input: input{ + userID: 1, + chatID: 2, + message: domain.SendMessage{ + Flag: "send", + Message: "msg", + ImageUrl: "image", + }, + }, + sendData: sendData{ + sendMsg: repository.SendMessage{ + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + }, + rawMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + }, + }, + userGetData: userGetData{ + userID: 1, + user: domain.User{ + ID: 1, + Role: 1, + Username: "name", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: true, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + }, + expectedMsg: domain.Message{ + ID: domain.GenerateTestUUID(), + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + Username: "name", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + }, + }, + { + name: "message_with_answer", + getRawBehavior: func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(answerID).Return(msg, err) + }, + sendBehavior: func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) { + r.EXPECT().Send(msg).Return(rawMsg, err) + }, + userGetBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + input: input{ + userID: 1, + chatID: 2, + message: domain.SendMessage{ + Flag: "send", + Message: "msg", + ImageUrl: "image", + Answer: domain.GenerateTestUUID(), + }, + }, + getRawData: getRawData{ + answerID: domain.GenerateTestUUID(), + msg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "answer_msg", + ImageUrl: "answer_image", + ChatID: 2, + UserID: 3, + CreatedAt: time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC), + }, + }, + sendData: sendData{ + sendMsg: repository.SendMessage{ + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "answer_msg", + AnswerImageUrl: "answer_image", + }, + rawMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "answer_msg", + AnswerImageUrl: "answer_image", + }, + }, + userGetData: userGetData{ + userID: 1, + user: domain.User{ + ID: 1, + Role: 1, + Username: "name", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: true, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + }, + expectedMsg: domain.Message{ + ID: domain.GenerateTestUUID(), + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + Username: "name", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "answer_msg", + AnswerImageUrl: "answer_image", + }, + }, + { + name: "message_without_answer_send_error", + getRawBehavior: func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) {}, + sendBehavior: func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) { + r.EXPECT().Send(msg).Return(rawMsg, err) + }, + userGetBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + input: input{ + userID: 1, + chatID: 2, + message: domain.SendMessage{ + Flag: "send", + Message: "msg", + ImageUrl: "image", + }, + }, + sendData: sendData{ + sendMsg: repository.SendMessage{ + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + }, + err: domain.AnyError, + }, + expectedErr: domain.InternalServerError, + }, + { + name: "message_with_answer_getRaw_error", + getRawBehavior: func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(answerID).Return(msg, err) + }, + sendBehavior: func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) { + }, + userGetBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + input: input{ + userID: 1, + chatID: 2, + message: domain.SendMessage{ + Flag: "send", + Message: "msg", + ImageUrl: "image", + Answer: domain.GenerateTestUUID(), + }, + }, + getRawData: getRawData{ + answerID: domain.GenerateTestUUID(), + err: domain.AnyError, + }, + expectedErr: domain.InternalServerError, + }, + { + name: "message_with_answer_send_error", + getRawBehavior: func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(answerID).Return(msg, err) + }, + sendBehavior: func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) { + r.EXPECT().Send(msg).Return(rawMsg, err) + }, + userGetBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + input: input{ + userID: 1, + chatID: 2, + message: domain.SendMessage{ + Flag: "send", + Message: "msg", + ImageUrl: "image", + Answer: domain.GenerateTestUUID(), + }, + }, + getRawData: getRawData{ + answerID: domain.GenerateTestUUID(), + msg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "answer_msg", + ImageUrl: "answer_image", + ChatID: 2, + UserID: 3, + CreatedAt: time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC), + }, + }, + sendData: sendData{ + sendMsg: repository.SendMessage{ + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "answer_msg", + AnswerImageUrl: "answer_image", + }, + err: domain.AnyError, + }, + expectedErr: domain.InternalServerError, + }, + { + name: "get_user_error", + getRawBehavior: func(r *mock_repository.MockMessage, answerID domain.UUID, msg domain.RawMessage, err error) {}, + sendBehavior: func(r *mock_repository.MockMessage, msg repository.SendMessage, rawMsg domain.RawMessage, err error) { + r.EXPECT().Send(msg).Return(rawMsg, err) + }, + userGetBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + input: input{ + userID: 1, + chatID: 2, + message: domain.SendMessage{ + Flag: "send", + Message: "msg", + ImageUrl: "image", + }, + }, + sendData: sendData{ + sendMsg: repository.SendMessage{ + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + }, + rawMsg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "msg", + ImageUrl: "image", + ChatID: 2, + UserID: 1, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + }, + }, + userGetData: userGetData{ + userID: 1, + err: domain.AnyError, + }, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + userServ := mock_service.NewMockUser(c) + + tc.getRawBehavior(repo, tc.getRawData.answerID, tc.getRawData.msg, tc.getRawData.err) + tc.sendBehavior(repo, tc.sendData.sendMsg, tc.sendData.rawMsg, tc.sendData.err) + tc.userGetBehavior(userServ, tc.userGetData.userID, tc.userGetData.user, tc.userGetData.err) + + serv := &messageService{repo: repo, u: userServ, l: log} + + msg, err := serv.SendMessage(tc.input.userID, tc.input.chatID, tc.input.message) + + assert.Equal(t, tc.expectedMsg, msg) + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_Delete(t *testing.T) { + type repoBehavior func(r *mock_repository.MockMessage, ID domain.UUID, err error) + + testTable := []struct { + name string + repoBehavior repoBehavior + ID domain.UUID + repoID domain.UUID + repoErr error + expectedErr error + }{ + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, ID domain.UUID, err error) { + r.EXPECT().Delete(ID).Return(err) + }, + ID: domain.GenerateTestUUID(), + repoID: domain.GenerateTestUUID(), + }, + { + name: "any_error", + repoBehavior: func(r *mock_repository.MockMessage, ID domain.UUID, err error) { + r.EXPECT().Delete(ID).Return(err) + }, + ID: domain.GenerateTestUUID(), + repoID: domain.GenerateTestUUID(), + repoErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoBehavior(repo, tc.repoID, tc.repoErr) + + serv := &messageService{repo: repo, l: log} + + err := serv.Delete(tc.ID) + + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_Edit(t *testing.T) { + type repoBehavior func(r *mock_repository.MockMessage, msg repository.EditMessage, err error) + + testTable := []struct { + name string + repoBehavior repoBehavior + ID domain.UUID + newMessage, newImageUrl string + newMsg repository.EditMessage + repoErr error + expectedErr error + }{ + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, msg repository.EditMessage, err error) { + r.EXPECT().Edit(msg).Return(err) + }, + ID: domain.GenerateTestUUID(), + newMessage: "new_message", + newImageUrl: "new_image_url", + newMsg: repository.EditMessage{ + ID: domain.GenerateTestUUID(), + Message: "new_message", + ImageUrl: "new_image_url", + }, + }, + { + name: "any_error", + repoBehavior: func(r *mock_repository.MockMessage, msg repository.EditMessage, err error) { + r.EXPECT().Edit(msg).Return(err) + }, + ID: domain.GenerateTestUUID(), + newMessage: "new_message", + newImageUrl: "new_image_url", + newMsg: repository.EditMessage{ + ID: domain.GenerateTestUUID(), + Message: "new_message", + ImageUrl: "new_image_url", + }, + repoErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoBehavior(repo, tc.newMsg, tc.repoErr) + + serv := &messageService{repo: repo, l: log} + + err := serv.Edit(tc.ID, tc.newMessage, tc.newImageUrl) + + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_Pin(t *testing.T) { + type repo1Behavior func(r *mock_repository.MockMessage, msg repository.PinMessage, err error) + type repo2Behavior func(r *mock_repository.MockMessage, ID domain.UUID, msg domain.RawMessage, err error) + type serviceBehavior func(s *mock_service.MockUser, userID int, user domain.User, err error) + type repo2Data struct { + ID domain.UUID + msg domain.RawMessage + err error + } + type serviceData struct { + userID int + user domain.User + err error + } + + testTable := []struct { + name string + repo1Behavior repo1Behavior + repo2Behavior repo2Behavior + serviceBehavior serviceBehavior + repo2Data repo2Data + serviceData serviceData + pinMsg repository.PinMessage + chatID, userID int + messageID domain.UUID + repoErr error + expectedMsg domain.Message + expectedErr error + }{ + { + name: "ok", + repo1Behavior: func(r *mock_repository.MockMessage, msg repository.PinMessage, err error) { + r.EXPECT().Pin(msg).Return(err) + }, + repo2Behavior: func(r *mock_repository.MockMessage, ID domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(ID).Return(msg, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) { + s.EXPECT().Get(userID, true).Return(user, err) + }, + repo2Data: repo2Data{ + ID: domain.GenerateTestUUID(), + msg: domain.RawMessage{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 17, + UserID: 3, + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "answer_msg", + AnswerImageUrl: "answer_image", + }, + }, + serviceData: serviceData{ + userID: 3, + user: domain.User{ + ID: 3, + Role: 1, + Username: "name", + Email: "mail@mail.ru", + HashedPassword: "hp", + AvatarImage: "avatar_image", + BlackPhoenix: true, + DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)}, + DateOfRegistration: domain.CustomDate{Time: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC)}, + }, + }, + pinMsg: repository.PinMessage{MessageID: domain.GenerateTestUUID(), ChatID: 17, UserID: 2}, + chatID: 17, + userID: 2, + messageID: domain.GenerateTestUUID(), + expectedMsg: domain.Message{ + ID: domain.GenerateTestUUID(), + Message: "sendMsg", + ImageUrl: "image", + ChatID: 17, + UserID: 3, + Username: "name", + AvatarImage: "avatar_image", + CreatedAt: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC), + AnswerID: domain.GenerateTestUUID(), + AnswerMessage: "answer_msg", + AnswerImageUrl: "answer_image", + }, + }, + { + name: "message_already_pinned", + repo1Behavior: func(r *mock_repository.MockMessage, msg repository.PinMessage, err error) { + r.EXPECT().Pin(msg).Return(err) + }, + repo2Behavior: func(r *mock_repository.MockMessage, ID domain.UUID, msg domain.RawMessage, err error) {}, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + pinMsg: repository.PinMessage{MessageID: domain.GenerateTestUUID(), ChatID: 17, UserID: 2}, + chatID: 17, + userID: 2, + messageID: domain.GenerateTestUUID(), + repoErr: domain.MessageAlreadyPinnedError, + expectedErr: domain.MessageAlreadyPinnedError, + }, + { + name: "repo_error", + repo1Behavior: func(r *mock_repository.MockMessage, msg repository.PinMessage, err error) { + r.EXPECT().Pin(msg).Return(err) + }, + repo2Behavior: func(r *mock_repository.MockMessage, ID domain.UUID, msg domain.RawMessage, err error) {}, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + pinMsg: repository.PinMessage{MessageID: domain.GenerateTestUUID(), ChatID: 17, UserID: 2}, + chatID: 17, + userID: 2, + messageID: domain.GenerateTestUUID(), + repoErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + { + name: "GetMessage_error", + repo1Behavior: func(r *mock_repository.MockMessage, msg repository.PinMessage, err error) { + r.EXPECT().Pin(msg).Return(err) + }, + repo2Behavior: func(r *mock_repository.MockMessage, ID domain.UUID, msg domain.RawMessage, err error) { + r.EXPECT().Get(ID).Return(msg, err) + }, + serviceBehavior: func(s *mock_service.MockUser, userID int, user domain.User, err error) {}, + repo2Data: repo2Data{ + ID: domain.GenerateTestUUID(), + err: domain.AnyError, + }, + pinMsg: repository.PinMessage{MessageID: domain.GenerateTestUUID(), ChatID: 17, UserID: 2}, + chatID: 17, + userID: 2, + messageID: domain.GenerateTestUUID(), + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + userServ := mock_service.NewMockUser(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repo1Behavior(repo, tc.pinMsg, tc.repoErr) + tc.repo2Behavior(repo, tc.repo2Data.ID, tc.repo2Data.msg, tc.repo2Data.err) + tc.serviceBehavior(userServ, tc.serviceData.userID, tc.serviceData.user, tc.serviceData.err) + + serv := &messageService{repo: repo, u: userServ, l: log} + + msg, err := serv.Pin(tc.chatID, tc.userID, tc.messageID) + + assert.Equal(t, tc.expectedMsg, msg) + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +func TestMessageService_Unpin(t *testing.T) { + type repoBehavior func(r *mock_repository.MockMessage, msg repository.UnpinMessage, err error) + + testTable := []struct { + name string + repoBehavior repoBehavior + repoMsg repository.UnpinMessage + chatID int + messageID domain.UUID + repoErr error + expectedErr error + }{ + { + name: "ok", + repoBehavior: func(r *mock_repository.MockMessage, msg repository.UnpinMessage, err error) { + r.EXPECT().Unpin(msg).Return(err) + }, + repoMsg: repository.UnpinMessage{MessageID: domain.GenerateTestUUID(), ChatID: 2}, + chatID: 2, + messageID: domain.GenerateTestUUID(), + }, + { + name: "repo_error", + repoBehavior: func(r *mock_repository.MockMessage, msg repository.UnpinMessage, err error) { + r.EXPECT().Unpin(msg).Return(err) + }, + repoMsg: repository.UnpinMessage{MessageID: domain.GenerateTestUUID(), ChatID: 2}, + chatID: 2, + messageID: domain.GenerateTestUUID(), + repoErr: domain.AnyError, + expectedErr: domain.InternalServerError, + }, + } + + for _, tc := range testTable { + t.Run(tc.name, func(t *testing.T) { + c := gomock.NewController(t) + defer c.Finish() + + repo := mock_repository.NewMockMessage(c) + log := logger.NewLogger(config.Config{Mode: "TEST"}) + + tc.repoBehavior(repo, tc.repoMsg, tc.repoErr) + + serv := &messageService{repo: repo, l: log} + + err := serv.Unpin(tc.chatID, tc.messageID) + + assert.ErrorIs(t, err, tc.expectedErr) + }) + } +} diff --git a/internal/service/mocks/mock.go b/internal/service/mocks/mock.go index 43707b8..7694366 100644 --- a/internal/service/mocks/mock.go +++ b/internal/service/mocks/mock.go @@ -25,7 +25,6 @@ import ( type MockUser struct { ctrl *gomock.Controller recorder *MockUserMockRecorder - isgomock struct{} } // MockUserMockRecorder is the mock recorder for MockUser. @@ -61,18 +60,18 @@ func (mr *MockUserMockRecorder) FindOne(username, email any) *gomock.Call { } // Get mocks base method. -func (m *MockUser) Get(userID int) (domain.User, error) { +func (m *MockUser) Get(userID int, isFast bool) (domain.User, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", userID) + ret := m.ctrl.Call(m, "Get", userID, isFast) ret0, _ := ret[0].(domain.User) ret1, _ := ret[1].(error) return ret0, ret1 } // Get indicates an expected call of Get. -func (mr *MockUserMockRecorder) Get(userID any) *gomock.Call { +func (mr *MockUserMockRecorder) Get(userID, isFast any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockUser)(nil).Get), userID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockUser)(nil).Get), userID, isFast) } // GetAll mocks base method. @@ -90,21 +89,6 @@ func (mr *MockUserMockRecorder) GetAll(username any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockUser)(nil).GetAll), username) } -// GetCachedUser mocks base method. -func (m *MockUser) GetCachedUser(userID int) (domain.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCachedUser", userID) - ret0, _ := ret[0].(domain.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetCachedUser indicates an expected call of GetCachedUser. -func (mr *MockUserMockRecorder) GetCachedUser(userID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCachedUser", reflect.TypeOf((*MockUser)(nil).GetCachedUser), userID) -} - // GetVerificated mocks base method. func (m *MockUser) GetVerificated(userID int) (domain.User, error) { m.ctrl.T.Helper() @@ -135,23 +119,10 @@ func (mr *MockUserMockRecorder) Register(userData any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockUser)(nil).Register), userData) } -// SetCachedUser mocks base method. -func (m *MockUser) SetCachedUser(u domain.User) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetCachedUser", u) -} - -// SetCachedUser indicates an expected call of SetCachedUser. -func (mr *MockUserMockRecorder) SetCachedUser(u any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCachedUser", reflect.TypeOf((*MockUser)(nil).SetCachedUser), u) -} - // MockAuth is a mock of Auth interface. type MockAuth struct { ctrl *gomock.Controller recorder *MockAuthMockRecorder - isgomock struct{} } // MockAuthMockRecorder is the mock recorder for MockAuth. @@ -249,7 +220,6 @@ func (mr *MockAuthMockRecorder) VerifyHashedPassword(p, hp any) *gomock.Call { type MockChat struct { ctrl *gomock.Controller recorder *MockChatMockRecorder - isgomock struct{} } // MockChatMockRecorder is the mock recorder for MockChat. @@ -273,7 +243,6 @@ func (m *MockChat) EXPECT() *MockChatMockRecorder { type MockMessages struct { ctrl *gomock.Controller recorder *MockMessagesMockRecorder - isgomock struct{} } // MockMessagesMockRecorder is the mock recorder for MockMessages. @@ -294,7 +263,7 @@ func (m *MockMessages) EXPECT() *MockMessagesMockRecorder { } // AddAvatarImageAndUsernameToMessage mocks base method. -func (m *MockMessages) AddAvatarImageAndUsernameToMessage(msgRaw domain.MessageRaw) (domain.Message, error) { +func (m *MockMessages) AddAvatarImageAndUsernameToMessage(msgRaw domain.RawMessage) (domain.Message, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddAvatarImageAndUsernameToMessage", msgRaw) ret0, _ := ret[0].(domain.Message) @@ -309,7 +278,7 @@ func (mr *MockMessagesMockRecorder) AddAvatarImageAndUsernameToMessage(msgRaw an } // AddAvatarImageAndUsernameToMessages mocks base method. -func (m *MockMessages) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.MessageRaw) []domain.Message { +func (m *MockMessages) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.RawMessage) []domain.Message { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddAvatarImageAndUsernameToMessages", msgsRaw) ret0, _ := ret[0].([]domain.Message) @@ -381,10 +350,10 @@ func (mr *MockMessagesMockRecorder) GetPinned(chatID any) *gomock.Call { } // GetRaw mocks base method. -func (m *MockMessages) GetRaw(ID domain.UUID) (domain.MessageRaw, error) { +func (m *MockMessages) GetRaw(ID domain.UUID) (domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRaw", ID) - ret0, _ := ret[0].(domain.MessageRaw) + ret0, _ := ret[0].(domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -396,18 +365,18 @@ func (mr *MockMessagesMockRecorder) GetRaw(ID any) *gomock.Call { } // GetSome mocks base method. -func (m *MockMessages) GetSome(chatID, messageNumberFrom, messagesToGet int) ([]domain.Message, error) { +func (m *MockMessages) GetSome(chatID, offset, limit int) ([]domain.Message, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSome", chatID, messageNumberFrom, messagesToGet) + ret := m.ctrl.Call(m, "GetSome", chatID, offset, limit) ret0, _ := ret[0].([]domain.Message) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSome indicates an expected call of GetSome. -func (mr *MockMessagesMockRecorder) GetSome(chatID, messageNumberFrom, messagesToGet any) *gomock.Call { +func (mr *MockMessagesMockRecorder) GetSome(chatID, offset, limit any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSome", reflect.TypeOf((*MockMessages)(nil).GetSome), chatID, messageNumberFrom, messagesToGet) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSome", reflect.TypeOf((*MockMessages)(nil).GetSome), chatID, offset, limit) } // Pin mocks base method. @@ -458,7 +427,6 @@ func (mr *MockMessagesMockRecorder) Unpin(chatID, messageID any) *gomock.Call { type MockCache struct { ctrl *gomock.Controller recorder *MockCacheMockRecorder - isgomock struct{} } // MockCacheMockRecorder is the mock recorder for MockCache. @@ -479,31 +447,31 @@ func (m *MockCache) EXPECT() *MockCacheMockRecorder { } // Get mocks base method. -func (m *MockCache) Get(key string, v encoding.BinaryUnmarshaler) error { +func (m *MockCache) Get(key string, v encoding.BinaryUnmarshaler, isFast bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", key, v) + ret := m.ctrl.Call(m, "Get", key, v, isFast) ret0, _ := ret[0].(error) return ret0 } // Get indicates an expected call of Get. -func (mr *MockCacheMockRecorder) Get(key, v any) *gomock.Call { +func (mr *MockCacheMockRecorder) Get(key, v, isFast any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCache)(nil).Get), key, v) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCache)(nil).Get), key, v, isFast) } // GetCtx mocks base method. -func (m *MockCache) GetCtx(ctx context.Context, key string, v encoding.BinaryUnmarshaler) error { +func (m *MockCache) GetCtx(ctx context.Context, key string, v encoding.BinaryUnmarshaler, isFast bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCtx", ctx, key, v) + ret := m.ctrl.Call(m, "GetCtx", ctx, key, v, isFast) ret0, _ := ret[0].(error) return ret0 } // GetCtx indicates an expected call of GetCtx. -func (mr *MockCacheMockRecorder) GetCtx(ctx, key, v any) *gomock.Call { +func (mr *MockCacheMockRecorder) GetCtx(ctx, key, v, isFast any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCtx", reflect.TypeOf((*MockCache)(nil).GetCtx), ctx, key, v) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCtx", reflect.TypeOf((*MockCache)(nil).GetCtx), ctx, key, v, isFast) } // Set mocks base method. @@ -538,7 +506,6 @@ func (mr *MockCacheMockRecorder) SetCtx(ctx, key, v, ex any) *gomock.Call { type MockParser struct { ctrl *gomock.Controller recorder *MockParserMockRecorder - isgomock struct{} } // MockParserMockRecorder is the mock recorder for MockParser. @@ -623,7 +590,6 @@ func (mr *MockParserMockRecorder) ParseWithClaims(tokenString, claims, keyFunc a type MockClaims struct { ctrl *gomock.Controller recorder *MockClaimsMockRecorder - isgomock struct{} } // MockClaimsMockRecorder is the mock recorder for MockClaims. @@ -737,7 +703,6 @@ func (mr *MockClaimsMockRecorder) GetSubject() *gomock.Call { type MockServ struct { ctrl *gomock.Controller recorder *MockServMockRecorder - isgomock struct{} } // MockServMockRecorder is the mock recorder for MockServ. @@ -758,7 +723,7 @@ func (m *MockServ) EXPECT() *MockServMockRecorder { } // AddAvatarImageAndUsernameToMessage mocks base method. -func (m *MockServ) AddAvatarImageAndUsernameToMessage(msgRaw domain.MessageRaw) (domain.Message, error) { +func (m *MockServ) AddAvatarImageAndUsernameToMessage(msgRaw domain.RawMessage) (domain.Message, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddAvatarImageAndUsernameToMessage", msgRaw) ret0, _ := ret[0].(domain.Message) @@ -773,7 +738,7 @@ func (mr *MockServMockRecorder) AddAvatarImageAndUsernameToMessage(msgRaw any) * } // AddAvatarImageAndUsernameToMessages mocks base method. -func (m *MockServ) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.MessageRaw) []domain.Message { +func (m *MockServ) AddAvatarImageAndUsernameToMessages(msgsRaw []domain.RawMessage) []domain.Message { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddAvatarImageAndUsernameToMessages", msgsRaw) ret0, _ := ret[0].([]domain.Message) @@ -875,18 +840,18 @@ func (mr *MockServMockRecorder) FindOne(username, email any) *gomock.Call { } // Get mocks base method. -func (m *MockServ) Get(userID int) (domain.User, error) { +func (m *MockServ) Get(userID int, isFast bool) (domain.User, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", userID) + ret := m.ctrl.Call(m, "Get", userID, isFast) ret0, _ := ret[0].(domain.User) ret1, _ := ret[1].(error) return ret0, ret1 } // Get indicates an expected call of Get. -func (mr *MockServMockRecorder) Get(userID any) *gomock.Call { +func (mr *MockServMockRecorder) Get(userID, isFast any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockServ)(nil).Get), userID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockServ)(nil).Get), userID, isFast) } // GetAll mocks base method. @@ -904,21 +869,6 @@ func (mr *MockServMockRecorder) GetAll(username any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockServ)(nil).GetAll), username) } -// GetCachedUser mocks base method. -func (m *MockServ) GetCachedUser(userID int) (domain.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCachedUser", userID) - ret0, _ := ret[0].(domain.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetCachedUser indicates an expected call of GetCachedUser. -func (mr *MockServMockRecorder) GetCachedUser(userID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCachedUser", reflect.TypeOf((*MockServ)(nil).GetCachedUser), userID) -} - // GetMessage mocks base method. func (m *MockServ) GetMessage(ID domain.UUID) (domain.Message, error) { m.ctrl.T.Helper() @@ -950,10 +900,10 @@ func (mr *MockServMockRecorder) GetPinned(chatID any) *gomock.Call { } // GetRaw mocks base method. -func (m *MockServ) GetRaw(ID domain.UUID) (domain.MessageRaw, error) { +func (m *MockServ) GetRaw(ID domain.UUID) (domain.RawMessage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRaw", ID) - ret0, _ := ret[0].(domain.MessageRaw) + ret0, _ := ret[0].(domain.RawMessage) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -965,18 +915,18 @@ func (mr *MockServMockRecorder) GetRaw(ID any) *gomock.Call { } // GetSome mocks base method. -func (m *MockServ) GetSome(chatID, messageNumberFrom, messagesToGet int) ([]domain.Message, error) { +func (m *MockServ) GetSome(chatID, offset, limit int) ([]domain.Message, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSome", chatID, messageNumberFrom, messagesToGet) + ret := m.ctrl.Call(m, "GetSome", chatID, offset, limit) ret0, _ := ret[0].([]domain.Message) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSome indicates an expected call of GetSome. -func (mr *MockServMockRecorder) GetSome(chatID, messageNumberFrom, messagesToGet any) *gomock.Call { +func (mr *MockServMockRecorder) GetSome(chatID, offset, limit any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSome", reflect.TypeOf((*MockServ)(nil).GetSome), chatID, messageNumberFrom, messagesToGet) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSome", reflect.TypeOf((*MockServ)(nil).GetSome), chatID, offset, limit) } // GetVerificated mocks base method. @@ -1054,18 +1004,6 @@ func (mr *MockServMockRecorder) SendMessage(userID, chatID, message any) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockServ)(nil).SendMessage), userID, chatID, message) } -// SetCachedUser mocks base method. -func (m *MockServ) SetCachedUser(u domain.User) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetCachedUser", u) -} - -// SetCachedUser indicates an expected call of SetCachedUser. -func (mr *MockServMockRecorder) SetCachedUser(u any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCachedUser", reflect.TypeOf((*MockServ)(nil).SetCachedUser), u) -} - // Unpin mocks base method. func (m *MockServ) Unpin(chatID int, messageID domain.UUID) error { m.ctrl.T.Helper() diff --git a/internal/service/service.go b/internal/service/service.go index 6afe751..9533f1f 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -17,9 +17,7 @@ import ( //go:generate mockgen -source=service.go -destination=mocks/mock.go type User interface { - GetCachedUser(userID int) (domain.User, error) - SetCachedUser(u domain.User) - Get(userID int) (domain.User, error) + Get(userID int, isFast bool) (domain.User, error) GetVerificated(userID int) (domain.User, error) GetAll(username string) ([]domain.User, error) FindOne(username, email string) (domain.User, error) @@ -37,12 +35,12 @@ type Auth interface { type Chat interface{} type Messages interface { - AddAvatarImageAndUsernameToMessage(msgRaw domain.MessageRaw) (domain.Message, error) - AddAvatarImageAndUsernameToMessages(msgsRaw []domain.MessageRaw) []domain.Message + AddAvatarImageAndUsernameToMessage(msgRaw domain.RawMessage) (domain.Message, error) + AddAvatarImageAndUsernameToMessages(msgsRaw []domain.RawMessage) []domain.Message + GetRaw(ID domain.UUID) (domain.RawMessage, error) GetMessage(ID domain.UUID) (domain.Message, error) - GetRaw(ID domain.UUID) (domain.MessageRaw, error) GetPinned(chatID int) ([]domain.Message, error) - GetSome(chatID, messageNumberFrom, messagesToGet int) ([]domain.Message, error) + GetSome(chatID, offset, limit int) ([]domain.Message, error) SendMessage(userID, chatID int, message domain.SendMessage) (domain.Message, error) Delete(ID domain.UUID) error Edit(ID domain.UUID, newMessage, newImageUrl string) error @@ -51,8 +49,8 @@ type Messages interface { } type Cache interface { - Get(key string, v encoding.BinaryUnmarshaler) error - GetCtx(ctx context.Context, key string, v encoding.BinaryUnmarshaler) error + Get(key string, v encoding.BinaryUnmarshaler, isFast bool) error + GetCtx(ctx context.Context, key string, v encoding.BinaryUnmarshaler, isFast bool) error Set(key string, v encoding.BinaryMarshaler, ex time.Duration) error SetCtx(ctx context.Context, key string, v encoding.BinaryMarshaler, ex time.Duration) error } diff --git a/internal/service/service_test.go b/internal/service/service_test.go index aa67e0a..b3b33f9 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -23,9 +23,9 @@ func Test_NewService(t *testing.T) { rdb := &redis.Client{} serv := NewService(repo, cfg, log, rdb) - cache := &cacheService{l: log, r: rdb} + cache := &cacheService{l: log, r: rdb, storage: &sync.Map{}} auth := &authService{cfg: cfg.JWT, parser: jwt.NewParser(), l: log} - user := &userService{ur: repo, auth: auth, l: log, cache: cache, m: &sync.Map{}} + user := &userService{ur: repo, auth: auth, l: log, cache: cache} expectedServ := &Service{ User: user, Auth: auth, diff --git a/internal/service/users.go b/internal/service/users.go index d7f3220..c037811 100644 --- a/internal/service/users.go +++ b/internal/service/users.go @@ -3,7 +3,6 @@ package service import ( "errors" "fmt" - "sync" "time" "git.urec56.ru/urec/chat_back_go/internal/domain" @@ -13,58 +12,30 @@ import ( type userService struct { cache Cache - m *sync.Map ur repository.User auth Auth l *logger.Logger } -type userCache struct { - u domain.User - expiresAt time.Time -} - func newUserService(user repository.User, auth Auth, l *logger.Logger, cache Cache) *userService { - return &userService{ur: user, auth: auth, l: l, cache: cache, m: &sync.Map{}} + return &userService{ur: user, auth: auth, l: l, cache: cache} } -func (s *userService) GetCachedUser(userID int) (domain.User, error) { - item, ok := s.m.Load(userID) - if ok { - if userData, ok := item.(userCache); ok && userData.expiresAt.After(time.Now()) { - return userData.u, nil - } - } - - u, err := s.Get(userID) - if err != nil { - s.l.Infof("error getting user by id: %s", err.Error()) - return domain.User{}, domain.UserNotFoundError - } - - s.SetCachedUser(u) - return u, nil -} - -func (s *userService) SetCachedUser(u domain.User) { - s.m.Store(u.ID, userCache{u: u, expiresAt: time.Now().Add(time.Minute)}) -} - -func (s *userService) Get(userID int) (domain.User, error) { +func (s *userService) Get(userID int, isFast bool) (domain.User, error) { u := domain.User{} - err := s.cache.Get(fmt.Sprintf("user: {'id': %d}", userID), &u) + err := s.cache.Get(fmt.Sprintf("user: {'id': %d}", userID), &u, isFast) if err == nil { return u, nil } - s.l.Infof("error getting user from cache: %s", err.Error()) + s.l.Infof("error getting user from cache: %s", err) u, err = s.ur.GetByID(userID) if err != nil { return u, err } - if err = s.cache.Set(fmt.Sprintf("user: {'id': %d}", userID), u, time.Second*20); err != nil { - s.l.Infof("error setting user to cache: %s", err.Error()) + if err = s.cache.Set(fmt.Sprintf("user: {'id': %d}", userID), &u, time.Second*20); err != nil { + s.l.Infof("error setting user to cache: %s", err) } return u, nil } diff --git a/internal/service/users_test.go b/internal/service/users_test.go index a5d94ed..7bcdc12 100644 --- a/internal/service/users_test.go +++ b/internal/service/users_test.go @@ -5,7 +5,6 @@ import ( "errors" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" - "sync" "testing" "time" @@ -27,7 +26,7 @@ func Test_newUserService(t *testing.T) { serv := newUserService(repo, auth, log, cache) - assert.EqualValues(t, &userService{ur: repo, auth: auth, l: log, cache: cache, m: &sync.Map{}}, serv) + assert.EqualValues(t, &userService{ur: repo, auth: auth, l: log, cache: cache}, serv) } func TestUserService_Get(t *testing.T) { @@ -64,8 +63,10 @@ func TestUserService_Get(t *testing.T) { name: "user_in_cache", repoBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {}, cacheGetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryUnmarshaler, err error) { - c.EXPECT().Get(key, gomock.Any()).DoAndReturn( - func(key string, v encoding.BinaryUnmarshaler) error { return v.UnmarshalBinary([]byte(`{"id": 1}`)) }, + c.EXPECT().Get(key, gomock.Any(), false).DoAndReturn( + func(key string, v encoding.BinaryUnmarshaler, isFast bool) error { + return v.UnmarshalBinary([]byte(`{"id": 1}`)) + }, ) }, cacheSetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryMarshaler, ex time.Duration, err error) {}, @@ -80,7 +81,7 @@ func TestUserService_Get(t *testing.T) { r.EXPECT().GetByID(userID).Return(user, err) }, cacheGetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryUnmarshaler, err error) { - c.EXPECT().Get(key, gomock.Any()).Return(err) + c.EXPECT().Get(key, gomock.Any(), false).Return(err) }, cacheSetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryMarshaler, ex time.Duration, err error) { c.EXPECT().Set(key, v, ex).Return(err) @@ -99,7 +100,7 @@ func TestUserService_Get(t *testing.T) { r.EXPECT().GetByID(userID).Return(user, err) }, cacheGetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryUnmarshaler, err error) { - c.EXPECT().Get(key, gomock.Any()).Return(err) + c.EXPECT().Get(key, gomock.Any(), false).Return(err) }, cacheSetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryMarshaler, ex time.Duration, err error) {}, cacheKey: "user: {'id': 1}", @@ -115,7 +116,7 @@ func TestUserService_Get(t *testing.T) { r.EXPECT().GetByID(userID).Return(user, err) }, cacheGetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryUnmarshaler, err error) { - c.EXPECT().Get(key, gomock.Any()).Return(err) + c.EXPECT().Get(key, gomock.Any(), false).Return(err) }, cacheSetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryMarshaler, ex time.Duration, err error) { c.EXPECT().Set(key, v, ex).Return(err) @@ -134,7 +135,7 @@ func TestUserService_Get(t *testing.T) { r.EXPECT().GetByID(userID).Return(user, err) }, cacheGetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryUnmarshaler, err error) { - c.EXPECT().Get(key, gomock.Any()).Return(err) + c.EXPECT().Get(key, gomock.Any(), false).Return(err) }, cacheSetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryMarshaler, ex time.Duration, err error) { c.EXPECT().Set(key, v, ex).Return(err) @@ -154,7 +155,7 @@ func TestUserService_Get(t *testing.T) { r.EXPECT().GetByID(userID).Return(user, err) }, cacheGetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryUnmarshaler, err error) { - c.EXPECT().Get(key, gomock.Any()).Return(err) + c.EXPECT().Get(key, gomock.Any(), false).Return(err) }, cacheSetBehavior: func(c *mock_service.MockCache, key string, v encoding.BinaryMarshaler, ex time.Duration, err error) {}, cacheKey: "user: {'id': 1}", @@ -178,9 +179,9 @@ func TestUserService_Get(t *testing.T) { tc.repoBehavior(repo, tc.userID, tc.repoUser, tc.repoErr) tc.cacheGetBehavior(cache, tc.cacheKey, tc.cacheGetData.container, tc.cacheGetData.err) - tc.cacheSetBehavior(cache, tc.cacheKey, tc.cacheSetData.value, tc.cacheSetData.duration, tc.cacheSetData.err) + tc.cacheSetBehavior(cache, tc.cacheKey, &tc.cacheSetData.value, tc.cacheSetData.duration, tc.cacheSetData.err) - u, err := serv.Get(tc.userID) + u, err := serv.Get(tc.userID, false) assert.Equal(t, tc.expectedUser, u) assert.ErrorIs(t, err, tc.expectedErr) diff --git a/internal/transport/rest/handler/users.go b/internal/transport/rest/handler/users.go index ae142ca..dfc2a67 100644 --- a/internal/transport/rest/handler/users.go +++ b/internal/transport/rest/handler/users.go @@ -17,7 +17,7 @@ func (h *Handler) GetUsers(w http.ResponseWriter, r *http.Request) { users, err := h.serv.GetAll(username) if err != nil { - h.l.Infof("[%s] getting users: %s", r.URL.Path, err.Error()) + h.l.Infof("[%s] getting users: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) return } @@ -25,7 +25,7 @@ func (h *Handler) GetUsers(w http.ResponseWriter, r *http.Request) { j, _ := json.Marshal(map[string][]domain.User{"users": users}) // no error because []domain.User can`t cause any error here if _, err = w.Write(j); err != nil { - h.l.Errorf("[%s] writing response: %s", r.URL.Path, err.Error()) + h.l.Errorf("[%s] writing response: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) return } @@ -42,14 +42,14 @@ func (h *Handler) CheckExisting(w http.ResponseWriter, r *http.Request) { } if err := domain.V.Struct(userFilter); err != nil { - h.l.Infof("[%s] validation: %s", r.URL.Path, err.Error()) + h.l.Infof("[%s] validation: %s", r.URL.Path, err) w.WriteHeader(http.StatusUnprocessableEntity) return } u, err := h.serv.FindOne(userFilter.Username, userFilter.Email) if err != nil { - h.l.Infof("[%s] serv.FindOne error: %s", r.URL.Path, err.Error()) + h.l.Infof("[%s] serv.FindOne error: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) return } @@ -77,7 +77,7 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) { } if err := domain.V.Struct(userData); err != nil { - h.l.Infof("[%s] validation: %s", r.URL.Path, err.Error()) + h.l.Infof("[%s] validation: %s", r.URL.Path, err) w.WriteHeader(http.StatusUnprocessableEntity) return } @@ -87,7 +87,7 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) { if errors.Is(err, domain.UserAlreadyExistsError) { w.WriteHeader(http.StatusConflict) } else { - h.l.Infof("[%s] serv.Register: %s", r.URL.Path, err.Error()) + h.l.Infof("[%s] serv.Register: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) } return @@ -97,7 +97,7 @@ func (h *Handler) Register(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) if _, err = w.Write(resp); err != nil { - h.l.Errorf("[%s] writing response: %s", r.URL.Path, err.Error()) + h.l.Errorf("[%s] writing response: %s", r.URL.Path, err) return } } @@ -125,7 +125,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { j, _ := json.Marshal(user) // no error because domain.User can`t cause any error here if _, err := w.Write(j); err != nil { - h.l.Errorf("[%s] writing response: %s", r.URL.Path, err.Error()) + h.l.Errorf("[%s] writing response: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) return } diff --git a/internal/transport/rest/middleware/auth.go b/internal/transport/rest/middleware/auth.go index ed32f82..88ec650 100644 --- a/internal/transport/rest/middleware/auth.go +++ b/internal/transport/rest/middleware/auth.go @@ -13,25 +13,25 @@ func (m *Middleware) Auth(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { t, err := m.serv.ExtractAuthToken(r) if err != nil { - m.l.Infof("[%s] error extracting token: %s", r.URL.Path, err.Error()) + m.l.Infof("[%s] error extracting token: %s", r.URL.Path, err) w.WriteHeader(http.StatusUnauthorized) return } userID, err := m.serv.DecodeAuthToken(t) if err != nil { - m.l.Infof("[%s] error decoding token: %s", r.URL.Path, err.Error()) + m.l.Infof("[%s] error decoding token: %s", r.URL.Path, err) w.WriteHeader(http.StatusUnauthorized) return } - user, err := m.serv.Get(userID) + user, err := m.serv.Get(userID, false) if err != nil { if errors.Is(err, domain.UserNotFoundError) { w.WriteHeader(http.StatusNotFound) return } - m.l.Infof("[%s] error resolving user: %s", r.URL.Path, err.Error()) + m.l.Infof("[%s] error resolving user: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) return } @@ -48,14 +48,14 @@ func (m *Middleware) VerificatedAuth(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { t, err := m.serv.ExtractAuthToken(r) if err != nil { - m.l.Infof("[%s] error extracting token: %s", r.URL.Path, err.Error()) + m.l.Infof("[%s] error extracting token: %s", r.URL.Path, err) w.WriteHeader(http.StatusUnauthorized) return } userID, err := m.serv.DecodeAuthToken(t) if err != nil { - m.l.Infof("[%s] error decoding token: %s", r.URL.Path, err.Error()) + m.l.Infof("[%s] error decoding token: %s", r.URL.Path, err) w.WriteHeader(http.StatusUnauthorized) return } @@ -68,7 +68,7 @@ func (m *Middleware) VerificatedAuth(next http.HandlerFunc) http.HandlerFunc { w.WriteHeader(http.StatusConflict) return } - m.l.Infof("[%s] error resolving user: %s", r.URL.Path, err.Error()) + m.l.Infof("[%s] error resolving user: %s", r.URL.Path, err) w.WriteHeader(http.StatusInternalServerError) return } diff --git a/internal/transport/rest/middleware/auth_test.go b/internal/transport/rest/middleware/auth_test.go index 6d90019..6c04b88 100644 --- a/internal/transport/rest/middleware/auth_test.go +++ b/internal/transport/rest/middleware/auth_test.go @@ -44,7 +44,7 @@ func TestMiddleware_Auth(t *testing.T) { s.EXPECT().DecodeAuthToken(token).Return(userID, err) }, getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) { - s.EXPECT().Get(userID).Return(user, err) + s.EXPECT().Get(userID, false).Return(user, err) }, reqToken: "Bearer token", extractToken: "token", @@ -82,7 +82,7 @@ func TestMiddleware_Auth(t *testing.T) { s.EXPECT().DecodeAuthToken(token).Return(userID, err) }, getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) { - s.EXPECT().Get(userID).Return(user, err) + s.EXPECT().Get(userID, false).Return(user, err) }, reqToken: "Bearer token", extractToken: "token", @@ -122,7 +122,7 @@ func TestMiddleware_Auth(t *testing.T) { s.EXPECT().DecodeAuthToken(token).Return(userID, err) }, getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) { - s.EXPECT().Get(userID).Return(user, err) + s.EXPECT().Get(userID, false).Return(user, err) }, getErr: domain.AnyError, logErr: domain.AnyError, diff --git a/internal/transport/rest/server.go b/internal/transport/rest/server.go index fe52a23..8527e88 100644 --- a/internal/transport/rest/server.go +++ b/internal/transport/rest/server.go @@ -3,9 +3,11 @@ package rest import ( "context" "expvar" - "github.com/urec56/pathparams" + "fmt" "net/http" + "github.com/urec56/pathparams" + "git.urec56.ru/urec/chat_back_go/config" "git.urec56.ru/urec/chat_back_go/internal/logger" "git.urec56.ru/urec/chat_back_go/internal/service" @@ -23,11 +25,11 @@ func NewServer(serv service.Serv, l *logger.Logger, cfg config.Config) *Server { return &Server{m: middleware.NewMiddleware(serv, l, cfg.Srv), h: handler.NewHandler(serv, l)} } -func (s *Server) Run(port string) error { +func (s *Server) Run(port int) error { r := s.getRouter() s.httpServer = &http.Server{ - Addr: ":" + port, + Addr: fmt.Sprintf(":%d", port), Handler: r, }