Написал тесты, отрефакторил
This commit is contained in:
parent
7e14ca2232
commit
217a691264
56 changed files with 6128 additions and 1 deletions
0
.env_template
Normal file
0
.env_template
Normal file
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,2 +1,6 @@
|
|||
.idea
|
||||
vendor
|
||||
.env
|
||||
proto
|
||||
grpc
|
||||
coverage.out
|
38
Makefile
Normal file
38
Makefile
Normal file
|
@ -0,0 +1,38 @@
|
|||
.SILENT:
|
||||
.DEFAULT_GOAL := run
|
||||
|
||||
build:
|
||||
go build -o server cmd/app/main.go
|
||||
|
||||
run:
|
||||
go run cmd/app/main.go
|
||||
|
||||
migrate.up:
|
||||
go run cmd/migrations/up.go
|
||||
|
||||
migrate.down:
|
||||
go run cmd/migrations/down.go
|
||||
|
||||
migrate.create:
|
||||
migrate create -ext sql -dir migrations -seq $(name)
|
||||
|
||||
TEST_PKGS := $(shell go list ./... | grep -v "/cmd/" | grep -v "/mocks")
|
||||
|
||||
test:
|
||||
go test -v -short -count=1 -race -coverprofile=coverage.out $(TEST_PKGS)
|
||||
|
||||
test.many:
|
||||
go test -v -short -count=100 -race -coverprofile=coverage.out $(TEST_PKGS)
|
||||
|
||||
test.full:
|
||||
go test -v $(TEST_PKGS)
|
||||
|
||||
test.race:
|
||||
go test -v -race -count=1 $(TEST_PKGS)
|
||||
|
||||
test.coverage:
|
||||
go tool cover -func=coverage.out | grep "total"
|
||||
go tool cover -html=coverage.out
|
||||
|
||||
test.mock:
|
||||
go generate ./...
|
|
@ -1,5 +1,58 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"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"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/logger"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/repository"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/service"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/transport/rest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
log := logger.NewLogger(cfg)
|
||||
|
||||
db, err := database.InitPostgres(cfg.Psql)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while database initialization: %s", err.Error())
|
||||
}
|
||||
defer func() {
|
||||
if err = db.Close(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
domain.Init(log)
|
||||
repo := repository.NewRepository(db, log)
|
||||
serv := service.NewService(repo, cfg, log)
|
||||
srv := rest.NewServer(serv, log, cfg)
|
||||
|
||||
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.Infof("starting server on port %s\n", cfg.Srv.Port)
|
||||
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-quit
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
41
cmd/migrations/down.go
Normal file
41
cmd/migrations/down.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
|
||||
"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/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
log := logger.NewLogger(cfg)
|
||||
|
||||
db, err := database.InitPostgresSql(cfg.Psql)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while database initialization: %s", err.Error())
|
||||
}
|
||||
|
||||
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while driver initialization: %s", err.Error())
|
||||
}
|
||||
|
||||
m, err := migrate.NewWithDatabaseInstance(
|
||||
"file://migrations",
|
||||
"postgres",
|
||||
driver,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while migration initialization: %s", err.Error())
|
||||
}
|
||||
|
||||
if err = m.Down(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
41
cmd/migrations/up.go
Normal file
41
cmd/migrations/up.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
|
||||
"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/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
log := logger.NewLogger(cfg)
|
||||
|
||||
db, err := database.InitPostgresSql(cfg.Psql)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while database initialization: %s", err.Error())
|
||||
}
|
||||
|
||||
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while driver initialization: %s", err.Error())
|
||||
}
|
||||
|
||||
m, err := migrate.NewWithDatabaseInstance(
|
||||
"file://migrations",
|
||||
"postgres",
|
||||
driver,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while migration initialization: %s", err.Error())
|
||||
}
|
||||
|
||||
if err = m.Up(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
6
config.yml
Normal file
6
config.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
migrations:
|
||||
folder: "migrations"
|
||||
|
||||
server:
|
||||
port: 8000
|
||||
request_id_header: "X-Request-Id"
|
49
config/config.go
Normal file
49
config/config.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/ilyakaznacheev/cleanenv"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Psql Postgres
|
||||
JWT JWT
|
||||
Srv Server `yaml:"server"`
|
||||
Mig Migrations `yaml:"migrations"`
|
||||
Mode string `env:"MODE"`
|
||||
}
|
||||
|
||||
type Postgres struct {
|
||||
Host string `env:"DB_HOST"`
|
||||
Port int `env:"DB_PORT"`
|
||||
User string `env:"DB_USER"`
|
||||
Password string `env:"DB_PASS"`
|
||||
Dbname string `env:"DB_NAME"`
|
||||
Sslmode string `env:"DB_SSLMODE"`
|
||||
}
|
||||
|
||||
type JWT struct {
|
||||
SecretKey []byte `env:"JWT_SECRET_KEY"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Port string `yaml:"port"`
|
||||
RequestIDHeader string `yaml:"request_id_header"`
|
||||
}
|
||||
type Migrations struct {
|
||||
Folder string `yaml:"folder"`
|
||||
}
|
||||
|
||||
func GetConfig() Config {
|
||||
var cfg Config
|
||||
|
||||
if err := cleanenv.ReadConfig(".env", &cfg); err != nil {
|
||||
log.Fatalf("error occurred while reading config: %s", err)
|
||||
}
|
||||
|
||||
if err := cleanenv.ReadConfig("config.yml", &cfg); err != nil {
|
||||
log.Fatalf("error occurred while reading config: %s", err)
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
42
go.mod
42
go.mod
|
@ -1,3 +1,45 @@
|
|||
module git.urec56.ru/urec/chat_back_go
|
||||
|
||||
go 1.22.6
|
||||
|
||||
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-migrate/migrate/v4 v4.18.2
|
||||
github.com/ilyakaznacheev/cleanenv v1.5.0
|
||||
github.com/jackc/pgx/v5 v5.7.2
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/urec56/pathparams v0.0.6
|
||||
go.uber.org/mock v0.5.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 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
|
||||
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
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
|
||||
)
|
||||
|
|
138
go.sum
Normal file
138
go.sum
Normal file
|
@ -0,0 +1,138 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dhui/dktest v0.4.4 h1:+I4s6JRE1yGuqflzwqG+aIaMdgXIorCf5P98JnaAWa8=
|
||||
github.com/dhui/dktest v0.4.4/go.mod h1:4+22R4lgsdAXrDyaH4Nqx2JEz2hLp49MqQmm9HLCQhM=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4=
|
||||
github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
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/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-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=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
|
||||
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/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-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk=
|
||||
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/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
|
||||
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
|
||||
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=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
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/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/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
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=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
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/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/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/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/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=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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/urec56/pathparams v0.0.6 h1:OuCbamKVdfVtprL+arL5QUuy84R909haBHLburAAp1c=
|
||||
github.com/urec56/pathparams v0.0.6/go.mod h1:EymabShlKrvvNckqTZ6zVki6wEYTQmcg41nxFtd7G8Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
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=
|
||||
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/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
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-20220715151400-c0bba94af5f8/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/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
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/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=
|
||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
|
||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=
|
18
internal/database/mock.go
Normal file
18
internal/database/mock.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func GetMockDBx(t *testing.T) (*sqlx.DB, sqlmock.Sqlmock, func()) {
|
||||
db, mock, err := sqlmock.New()
|
||||
assert.NoError(t, err)
|
||||
dbx := sqlx.NewDb(db, "sqlmock")
|
||||
|
||||
sqlx.BindDriver("sqlmock", sqlx.DOLLAR)
|
||||
|
||||
return dbx, mock, func() { _ = dbx.Close() }
|
||||
}
|
52
internal/database/psql.go
Normal file
52
internal/database/psql.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/config"
|
||||
)
|
||||
|
||||
const IntegrityErrorCode = "23505"
|
||||
|
||||
var IntegrityError = &pgconn.PgError{Code: IntegrityErrorCode}
|
||||
|
||||
func InitPostgres(cfg config.Postgres) (*sqlx.DB, error) {
|
||||
db, err := sqlx.Connect(
|
||||
"pgx", fmt.Sprintf(
|
||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||
cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.Dbname, cfg.Sslmode,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func InitPostgresSql(cfg config.Postgres) (*sql.DB, error) {
|
||||
db, err := sql.Open(
|
||||
"pgx", fmt.Sprintf(
|
||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||
cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.Dbname, cfg.Sslmode,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
15
internal/domain/errors.go
Normal file
15
internal/domain/errors.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
UnverifiedUserError = errors.New("user is not verified")
|
||||
UserNotFoundError = errors.New("user not found")
|
||||
InternalServerError = errors.New("internal server error")
|
||||
UserAlreadyExistsError = errors.New("user already exists")
|
||||
TokenError = errors.New("invalid auth token")
|
||||
HashingError = errors.New("error during password hashing")
|
||||
AnyError = errors.New("any error") // for tests
|
||||
)
|
11
internal/domain/log.go
Normal file
11
internal/domain/log.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"git.urec56.ru/urec/chat_back_go/internal/logger"
|
||||
)
|
||||
|
||||
var log logger.Log
|
||||
|
||||
func Init(l logger.Log) {
|
||||
log = l
|
||||
}
|
19
internal/domain/log_test.go
Normal file
19
internal/domain/log_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
)
|
||||
|
||||
func Test_Init(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
l := mock_logger.NewMockLog(c)
|
||||
|
||||
Init(l)
|
||||
|
||||
assert.Equal(t, l, log)
|
||||
}
|
100
internal/domain/user.go
Normal file
100
internal/domain/user.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
UnverifiedUser = 0
|
||||
VerificatedUser = 1
|
||||
AdminUser = 100
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id" db:"id"`
|
||||
Role int `json:"-" db:"role"`
|
||||
Username string `json:"username" db:"username"`
|
||||
Email string `json:"email" db:"email"`
|
||||
HashedPassword string `json:"-" db:"hashed_password"`
|
||||
AvatarImage string `json:"avatar_image" db:"avatar_image"`
|
||||
BlackPhoenix bool `json:"black_phoenix" db:"black_phoenix"`
|
||||
DateOfBirth CustomDate `json:"date_of_birth" db:"date_of_birth"`
|
||||
DateOfRegistration CustomDate `json:"date_of_registration" db:"date_of_registration"`
|
||||
}
|
||||
|
||||
func (u User) MarshalJSON() ([]byte, error) {
|
||||
uMap := make(map[string]any)
|
||||
uMap["id"] = u.ID
|
||||
uMap["username"] = u.Username
|
||||
uMap["email"] = u.Email
|
||||
uMap["avatar_image"] = u.AvatarImage
|
||||
uMap["black_phoenix"] = u.BlackPhoenix
|
||||
uMap["date_of_birth"] = u.DateOfBirth.Format(time.DateOnly)
|
||||
uMap["date_of_registration"] = u.DateOfRegistration.Format(time.DateOnly)
|
||||
|
||||
return json.Marshal(uMap)
|
||||
}
|
||||
|
||||
type UserFilter struct {
|
||||
Username string `json:"username" validate:"omitempty,min=2,max=30"`
|
||||
Email string `json:"email" validate:"omitempty,email"`
|
||||
}
|
||||
|
||||
type UserPassword struct {
|
||||
UserPassword string `json:"user_password" validate:"min=8"`
|
||||
}
|
||||
|
||||
type UserRegister struct {
|
||||
Email string `json:"email" validate:"email"`
|
||||
Username string `json:"username" validate:"min=2,max=30"`
|
||||
Password string `json:"password" validate:"min=8"`
|
||||
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
|
||||
}
|
290
internal/domain/user_test.go
Normal file
290
internal/domain/user_test.go
Normal file
|
@ -0,0 +1,290 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// assertJSONEqual сравнивает JSON, игнорируя пробелы и порядок ключей
|
||||
func assertJSONEqual(t *testing.T, expected string, actual []byte) {
|
||||
var expectedMap, actualMap map[string]any
|
||||
|
||||
if err := json.Unmarshal([]byte(expected), &expectedMap); err != nil {
|
||||
t.Fatalf("Ошибка десериализации ожидаемого JSON: %v", err)
|
||||
}
|
||||
if err := json.Unmarshal(actual, &actualMap); err != nil {
|
||||
t.Fatalf("Ошибка десериализации полученного JSON: %v", err)
|
||||
}
|
||||
|
||||
if len(expectedMap) != len(actualMap) {
|
||||
t.Errorf("Разное количество ключей в JSON: ожидалось %d, получено %d", len(expectedMap), len(actualMap))
|
||||
}
|
||||
|
||||
for key, expectedValue := range expectedMap {
|
||||
actualValue, exists := actualMap[key]
|
||||
if !exists {
|
||||
t.Errorf("Ключ %q отсутствует в JSON", key)
|
||||
} else if actualValue != expectedValue {
|
||||
t.Errorf("Для ключа %q ожидалось %v, получено %v", key, expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserMarshalJSON(t *testing.T) {
|
||||
validDate := CustomDate{Time: time.Date(1990, time.July, 10, 0, 0, 0, 0, time.UTC)}
|
||||
regDate := CustomDate{Time: time.Date(2023, time.March, 15, 0, 0, 0, 0, time.UTC)}
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
user User
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
user: User{
|
||||
ID: 42,
|
||||
Username: "alice",
|
||||
Email: "alice@example.com",
|
||||
AvatarImage: "https://example.com/avatar.png",
|
||||
BlackPhoenix: true,
|
||||
DateOfBirth: validDate,
|
||||
DateOfRegistration: regDate,
|
||||
},
|
||||
expected: `{
|
||||
"id": 42,
|
||||
"username": "alice",
|
||||
"email": "alice@example.com",
|
||||
"avatar_image": "https://example.com/avatar.png",
|
||||
"black_phoenix": true,
|
||||
"date_of_birth": "1990-07-10",
|
||||
"date_of_registration": "2023-03-15"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "zero_values",
|
||||
user: User{},
|
||||
expected: `{
|
||||
"id": 0,
|
||||
"username": "",
|
||||
"email": "",
|
||||
"avatar_image": "",
|
||||
"black_phoenix": false,
|
||||
"date_of_birth": "0001-01-01",
|
||||
"date_of_registration": "0001-01-01"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "special_characters",
|
||||
user: User{
|
||||
ID: 1,
|
||||
Username: `test"user`,
|
||||
Email: "test@example.com\nnewline",
|
||||
AvatarImage: `https://example.com/avatar.png`,
|
||||
},
|
||||
expected: `{
|
||||
"id": 1,
|
||||
"username": "test\"user",
|
||||
"email": "test@example.com\nnewline",
|
||||
"avatar_image": "https://example.com/avatar.png",
|
||||
"black_phoenix": false,
|
||||
"date_of_birth": "0001-01-01",
|
||||
"date_of_registration": "0001-01-01"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result, err := json.Marshal(tc.user)
|
||||
if err != nil {
|
||||
t.Fatalf("Ошибка сериализации: %v", err)
|
||||
}
|
||||
|
||||
assertJSONEqual(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
22
internal/domain/validate.go
Normal file
22
internal/domain/validate.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"time"
|
||||
)
|
||||
|
||||
var V = validator.New(validator.WithRequiredStructEnabled())
|
||||
|
||||
func init() {
|
||||
_ = V.RegisterValidation("date_of_birth", dateOfBirthValidation)
|
||||
}
|
||||
|
||||
func dateOfBirthValidation(fl validator.FieldLevel) bool {
|
||||
cd, _ := fl.Field().Interface().(CustomDate)
|
||||
|
||||
if cd.Time.Before(time.Date(1924, time.January, 1, 0, 0, 0, 0, time.UTC)) || cd.Time.After(time.Now().AddDate(-16, 0, 0)) {
|
||||
return false // дата рождения после 01.01.1924 и юзеру больше 16 лет
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
59
internal/domain/validate_test.go
Normal file
59
internal/domain/validate_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_dateOfBirthValidation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
date struct {
|
||||
Date CustomDate `validate:"date_of_birth"`
|
||||
}
|
||||
expectedErrText string
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
date: struct {
|
||||
Date CustomDate `validate:"date_of_birth"`
|
||||
}{Date: CustomDate{Time: time.Now().AddDate(-18, 0, 0)}},
|
||||
},
|
||||
{
|
||||
name: "exactly_on_boundary",
|
||||
date: struct {
|
||||
Date CustomDate `validate:"date_of_birth"`
|
||||
}{Date: CustomDate{Time: time.Date(1924, time.January, 1, 0, 0, 0, 0, time.UTC)}},
|
||||
},
|
||||
{
|
||||
name: "too_young",
|
||||
date: struct {
|
||||
Date CustomDate `validate:"date_of_birth"`
|
||||
}{Date: CustomDate{Time: time.Now().AddDate(-15, 11, 30)}},
|
||||
expectedErrText: "Key: 'Date' Error:Field validation for 'Date' failed on the 'date_of_birth' tag",
|
||||
},
|
||||
{
|
||||
name: "too_old",
|
||||
date: struct {
|
||||
Date CustomDate `validate:"date_of_birth"`
|
||||
}{Date: CustomDate{Time: time.Date(1923, time.December, 31, 23, 59, 59, 0, time.UTC)}},
|
||||
expectedErrText: "Key: 'Date' Error:Field validation for 'Date' failed on the 'date_of_birth' tag",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
validate := validator.New()
|
||||
_ = validate.RegisterValidation("date_of_birth", dateOfBirthValidation)
|
||||
|
||||
err := validate.Struct(tc.date)
|
||||
if tc.expectedErrText != "" {
|
||||
assert.EqualError(t, err, tc.expectedErrText)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
47
internal/logger/logger.go
Normal file
47
internal/logger/logger.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/config"
|
||||
)
|
||||
|
||||
//go:generate mockgen -source=logger.go -destination=mocks/mock.go
|
||||
|
||||
type Log interface {
|
||||
log.FieldLogger
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
*log.Logger
|
||||
}
|
||||
|
||||
func NewLogger(cfg config.Config) *Logger {
|
||||
logger := &Logger{Logger: log.New()}
|
||||
configureLogger(cfg, logger)
|
||||
return logger
|
||||
}
|
||||
|
||||
func configureLogger(cfg config.Config, logger *Logger) {
|
||||
switch cfg.Mode {
|
||||
case "PROD":
|
||||
logger.SetFormatter(&log.JSONFormatter{})
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(log.WarnLevel)
|
||||
case "STAGE":
|
||||
logger.SetFormatter(&log.TextFormatter{})
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(log.InfoLevel)
|
||||
case "DEV":
|
||||
logger.SetFormatter(&log.TextFormatter{})
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(log.DebugLevel)
|
||||
case "TEST":
|
||||
logger.SetFormatter(&log.TextFormatter{})
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(log.DebugLevel)
|
||||
default:
|
||||
logger.Fatal("incorrect MODE was specified")
|
||||
}
|
||||
}
|
125
internal/logger/logger_test.go
Normal file
125
internal/logger/logger_test.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/config"
|
||||
)
|
||||
|
||||
func Test_NewLogger(t *testing.T) {
|
||||
testTable := []struct {
|
||||
name string
|
||||
mode string
|
||||
expectedFormatter log.Formatter
|
||||
expectedOut io.Writer
|
||||
expectedLevel log.Level
|
||||
}{
|
||||
{
|
||||
name: "prod",
|
||||
mode: "PROD",
|
||||
expectedFormatter: &log.JSONFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.WarnLevel,
|
||||
},
|
||||
{
|
||||
name: "stage",
|
||||
mode: "STAGE",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.InfoLevel,
|
||||
},
|
||||
{
|
||||
name: "dev",
|
||||
mode: "DEV",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.DebugLevel,
|
||||
},
|
||||
{
|
||||
name: "test",
|
||||
mode: "TEST",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.DebugLevel,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
logger := NewLogger(config.Config{Mode: tc.mode})
|
||||
assert.Equal(t, tc.expectedFormatter, logger.Formatter)
|
||||
assert.Equal(t, tc.expectedOut, logger.Out)
|
||||
assert.Equal(t, tc.expectedLevel, logger.Level)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_configureLogger(t *testing.T) {
|
||||
testTable := []struct {
|
||||
name string
|
||||
mode string
|
||||
expectedFormatter log.Formatter
|
||||
expectedOut io.Writer
|
||||
expectedLevel log.Level
|
||||
exitCalled bool
|
||||
}{
|
||||
{
|
||||
name: "prod",
|
||||
mode: "PROD",
|
||||
expectedFormatter: &log.JSONFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.WarnLevel,
|
||||
},
|
||||
{
|
||||
name: "stage",
|
||||
mode: "STAGE",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.InfoLevel,
|
||||
},
|
||||
{
|
||||
name: "dev",
|
||||
mode: "DEV",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.DebugLevel,
|
||||
},
|
||||
{
|
||||
name: "test",
|
||||
mode: "TEST",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stdout,
|
||||
expectedLevel: log.DebugLevel,
|
||||
},
|
||||
{
|
||||
name: "incorrect_mode",
|
||||
expectedFormatter: &log.TextFormatter{},
|
||||
expectedOut: os.Stderr,
|
||||
expectedLevel: log.InfoLevel,
|
||||
exitCalled: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
logger := &Logger{Logger: log.New()}
|
||||
exitCalled := false
|
||||
|
||||
logger.ExitFunc = func(i int) {
|
||||
exitCalled = true
|
||||
}
|
||||
|
||||
configureLogger(config.Config{Mode: tc.mode}, logger)
|
||||
|
||||
assert.EqualExportedValues(t, tc.expectedFormatter, logger.Formatter)
|
||||
assert.EqualExportedValues(t, tc.expectedOut, logger.Out)
|
||||
assert.EqualExportedValues(t, tc.expectedLevel, logger.Level)
|
||||
assert.Equal(t, tc.exitCalled, exitCalled)
|
||||
})
|
||||
}
|
||||
}
|
475
internal/logger/mocks/mock.go
Normal file
475
internal/logger/mocks/mock.go
Normal file
|
@ -0,0 +1,475 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: logger.go
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -source=logger.go -destination=mocks/mock.go
|
||||
//
|
||||
|
||||
// Package mock_logger is a generated GoMock package.
|
||||
package mock_logger
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockLog is a mock of Log interface.
|
||||
type MockLog struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockLogMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockLogMockRecorder is the mock recorder for MockLog.
|
||||
type MockLogMockRecorder struct {
|
||||
mock *MockLog
|
||||
}
|
||||
|
||||
// NewMockLog creates a new mock instance.
|
||||
func NewMockLog(ctrl *gomock.Controller) *MockLog {
|
||||
mock := &MockLog{ctrl: ctrl}
|
||||
mock.recorder = &MockLogMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockLog) EXPECT() *MockLogMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Debug mocks base method.
|
||||
func (m *MockLog) Debug(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Debug", varargs...)
|
||||
}
|
||||
|
||||
// Debug indicates an expected call of Debug.
|
||||
func (mr *MockLogMockRecorder) Debug(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLog)(nil).Debug), args...)
|
||||
}
|
||||
|
||||
// Debugf mocks base method.
|
||||
func (m *MockLog) Debugf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Debugf", varargs...)
|
||||
}
|
||||
|
||||
// Debugf indicates an expected call of Debugf.
|
||||
func (mr *MockLogMockRecorder) Debugf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLog)(nil).Debugf), varargs...)
|
||||
}
|
||||
|
||||
// Debugln mocks base method.
|
||||
func (m *MockLog) Debugln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Debugln", varargs...)
|
||||
}
|
||||
|
||||
// Debugln indicates an expected call of Debugln.
|
||||
func (mr *MockLogMockRecorder) Debugln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugln", reflect.TypeOf((*MockLog)(nil).Debugln), args...)
|
||||
}
|
||||
|
||||
// Error mocks base method.
|
||||
func (m *MockLog) Error(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Error", varargs...)
|
||||
}
|
||||
|
||||
// Error indicates an expected call of Error.
|
||||
func (mr *MockLogMockRecorder) Error(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLog)(nil).Error), args...)
|
||||
}
|
||||
|
||||
// Errorf mocks base method.
|
||||
func (m *MockLog) Errorf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Errorf", varargs...)
|
||||
}
|
||||
|
||||
// Errorf indicates an expected call of Errorf.
|
||||
func (mr *MockLogMockRecorder) Errorf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLog)(nil).Errorf), varargs...)
|
||||
}
|
||||
|
||||
// Errorln mocks base method.
|
||||
func (m *MockLog) Errorln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Errorln", varargs...)
|
||||
}
|
||||
|
||||
// Errorln indicates an expected call of Errorln.
|
||||
func (mr *MockLogMockRecorder) Errorln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorln", reflect.TypeOf((*MockLog)(nil).Errorln), args...)
|
||||
}
|
||||
|
||||
// Fatal mocks base method.
|
||||
func (m *MockLog) Fatal(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Fatal", varargs...)
|
||||
}
|
||||
|
||||
// Fatal indicates an expected call of Fatal.
|
||||
func (mr *MockLogMockRecorder) Fatal(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fatal", reflect.TypeOf((*MockLog)(nil).Fatal), args...)
|
||||
}
|
||||
|
||||
// Fatalf mocks base method.
|
||||
func (m *MockLog) Fatalf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Fatalf", varargs...)
|
||||
}
|
||||
|
||||
// Fatalf indicates an expected call of Fatalf.
|
||||
func (mr *MockLogMockRecorder) Fatalf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fatalf", reflect.TypeOf((*MockLog)(nil).Fatalf), varargs...)
|
||||
}
|
||||
|
||||
// Fatalln mocks base method.
|
||||
func (m *MockLog) Fatalln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Fatalln", varargs...)
|
||||
}
|
||||
|
||||
// Fatalln indicates an expected call of Fatalln.
|
||||
func (mr *MockLogMockRecorder) Fatalln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fatalln", reflect.TypeOf((*MockLog)(nil).Fatalln), args...)
|
||||
}
|
||||
|
||||
// Info mocks base method.
|
||||
func (m *MockLog) Info(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Info", varargs...)
|
||||
}
|
||||
|
||||
// Info indicates an expected call of Info.
|
||||
func (mr *MockLogMockRecorder) Info(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLog)(nil).Info), args...)
|
||||
}
|
||||
|
||||
// Infof mocks base method.
|
||||
func (m *MockLog) Infof(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Infof", varargs...)
|
||||
}
|
||||
|
||||
// Infof indicates an expected call of Infof.
|
||||
func (mr *MockLogMockRecorder) Infof(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infof", reflect.TypeOf((*MockLog)(nil).Infof), varargs...)
|
||||
}
|
||||
|
||||
// Infoln mocks base method.
|
||||
func (m *MockLog) Infoln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Infoln", varargs...)
|
||||
}
|
||||
|
||||
// Infoln indicates an expected call of Infoln.
|
||||
func (mr *MockLogMockRecorder) Infoln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infoln", reflect.TypeOf((*MockLog)(nil).Infoln), args...)
|
||||
}
|
||||
|
||||
// Panic mocks base method.
|
||||
func (m *MockLog) Panic(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Panic", varargs...)
|
||||
}
|
||||
|
||||
// Panic indicates an expected call of Panic.
|
||||
func (mr *MockLogMockRecorder) Panic(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Panic", reflect.TypeOf((*MockLog)(nil).Panic), args...)
|
||||
}
|
||||
|
||||
// Panicf mocks base method.
|
||||
func (m *MockLog) Panicf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Panicf", varargs...)
|
||||
}
|
||||
|
||||
// Panicf indicates an expected call of Panicf.
|
||||
func (mr *MockLogMockRecorder) Panicf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Panicf", reflect.TypeOf((*MockLog)(nil).Panicf), varargs...)
|
||||
}
|
||||
|
||||
// Panicln mocks base method.
|
||||
func (m *MockLog) Panicln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Panicln", varargs...)
|
||||
}
|
||||
|
||||
// Panicln indicates an expected call of Panicln.
|
||||
func (mr *MockLogMockRecorder) Panicln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Panicln", reflect.TypeOf((*MockLog)(nil).Panicln), args...)
|
||||
}
|
||||
|
||||
// Print mocks base method.
|
||||
func (m *MockLog) Print(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Print", varargs...)
|
||||
}
|
||||
|
||||
// Print indicates an expected call of Print.
|
||||
func (mr *MockLogMockRecorder) Print(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Print", reflect.TypeOf((*MockLog)(nil).Print), args...)
|
||||
}
|
||||
|
||||
// Printf mocks base method.
|
||||
func (m *MockLog) Printf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Printf", varargs...)
|
||||
}
|
||||
|
||||
// Printf indicates an expected call of Printf.
|
||||
func (mr *MockLogMockRecorder) Printf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Printf", reflect.TypeOf((*MockLog)(nil).Printf), varargs...)
|
||||
}
|
||||
|
||||
// Println mocks base method.
|
||||
func (m *MockLog) Println(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Println", varargs...)
|
||||
}
|
||||
|
||||
// Println indicates an expected call of Println.
|
||||
func (mr *MockLogMockRecorder) Println(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Println", reflect.TypeOf((*MockLog)(nil).Println), args...)
|
||||
}
|
||||
|
||||
// Warn mocks base method.
|
||||
func (m *MockLog) Warn(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Warn", varargs...)
|
||||
}
|
||||
|
||||
// Warn indicates an expected call of Warn.
|
||||
func (mr *MockLogMockRecorder) Warn(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLog)(nil).Warn), args...)
|
||||
}
|
||||
|
||||
// Warnf mocks base method.
|
||||
func (m *MockLog) Warnf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Warnf", varargs...)
|
||||
}
|
||||
|
||||
// Warnf indicates an expected call of Warnf.
|
||||
func (mr *MockLogMockRecorder) Warnf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockLog)(nil).Warnf), varargs...)
|
||||
}
|
||||
|
||||
// Warning mocks base method.
|
||||
func (m *MockLog) Warning(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Warning", varargs...)
|
||||
}
|
||||
|
||||
// Warning indicates an expected call of Warning.
|
||||
func (mr *MockLogMockRecorder) Warning(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warning", reflect.TypeOf((*MockLog)(nil).Warning), args...)
|
||||
}
|
||||
|
||||
// Warningf mocks base method.
|
||||
func (m *MockLog) Warningf(format string, args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{format}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Warningf", varargs...)
|
||||
}
|
||||
|
||||
// Warningf indicates an expected call of Warningf.
|
||||
func (mr *MockLogMockRecorder) Warningf(format any, args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{format}, args...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warningf", reflect.TypeOf((*MockLog)(nil).Warningf), varargs...)
|
||||
}
|
||||
|
||||
// Warningln mocks base method.
|
||||
func (m *MockLog) Warningln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Warningln", varargs...)
|
||||
}
|
||||
|
||||
// Warningln indicates an expected call of Warningln.
|
||||
func (mr *MockLogMockRecorder) Warningln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warningln", reflect.TypeOf((*MockLog)(nil).Warningln), args...)
|
||||
}
|
||||
|
||||
// Warnln mocks base method.
|
||||
func (m *MockLog) Warnln(args ...any) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range args {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Warnln", varargs...)
|
||||
}
|
||||
|
||||
// Warnln indicates an expected call of Warnln.
|
||||
func (mr *MockLogMockRecorder) Warnln(args ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnln", reflect.TypeOf((*MockLog)(nil).Warnln), args...)
|
||||
}
|
||||
|
||||
// WithError mocks base method.
|
||||
func (m *MockLog) WithError(err error) *logrus.Entry {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "WithError", err)
|
||||
ret0, _ := ret[0].(*logrus.Entry)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// WithError indicates an expected call of WithError.
|
||||
func (mr *MockLogMockRecorder) WithError(err any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithError", reflect.TypeOf((*MockLog)(nil).WithError), err)
|
||||
}
|
||||
|
||||
// WithField mocks base method.
|
||||
func (m *MockLog) WithField(key string, value any) *logrus.Entry {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "WithField", key, value)
|
||||
ret0, _ := ret[0].(*logrus.Entry)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// WithField indicates an expected call of WithField.
|
||||
func (mr *MockLogMockRecorder) WithField(key, value any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithField", reflect.TypeOf((*MockLog)(nil).WithField), key, value)
|
||||
}
|
||||
|
||||
// WithFields mocks base method.
|
||||
func (m *MockLog) WithFields(fields logrus.Fields) *logrus.Entry {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "WithFields", fields)
|
||||
ret0, _ := ret[0].(*logrus.Entry)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// WithFields indicates an expected call of WithFields.
|
||||
func (mr *MockLogMockRecorder) WithFields(fields any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithFields", reflect.TypeOf((*MockLog)(nil).WithFields), fields)
|
||||
}
|
210
internal/repository/mocks/mock.go
Normal file
210
internal/repository/mocks/mock.go
Normal file
|
@ -0,0 +1,210 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: repository.go
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -source=repository.go -destination=mocks/mock.go
|
||||
//
|
||||
|
||||
// Package mock_repository is a generated GoMock package.
|
||||
package mock_repository
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
time "time"
|
||||
|
||||
domain "git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockUser is a mock of User interface.
|
||||
type MockUser struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockUserMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockUserMockRecorder is the mock recorder for MockUser.
|
||||
type MockUserMockRecorder struct {
|
||||
mock *MockUser
|
||||
}
|
||||
|
||||
// NewMockUser creates a new mock instance.
|
||||
func NewMockUser(ctrl *gomock.Controller) *MockUser {
|
||||
mock := &MockUser{ctrl: ctrl}
|
||||
mock.recorder = &MockUserMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockUser) EXPECT() *MockUserMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// FindOne mocks base method.
|
||||
func (m *MockUser) FindOne(username, email string) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindOne", username, email)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindOne indicates an expected call of FindOne.
|
||||
func (mr *MockUserMockRecorder) FindOne(username, email any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockUser)(nil).FindOne), username, email)
|
||||
}
|
||||
|
||||
// GetAll mocks base method.
|
||||
func (m *MockUser) GetAll(username string) ([]domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAll", username)
|
||||
ret0, _ := ret[0].([]domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAll indicates an expected call of GetAll.
|
||||
func (mr *MockUserMockRecorder) GetAll(username any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockUser)(nil).GetAll), username)
|
||||
}
|
||||
|
||||
// GetByID mocks base method.
|
||||
func (m *MockUser) GetByID(userID int) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetByID", userID)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetByID indicates an expected call of GetByID.
|
||||
func (mr *MockUserMockRecorder) GetByID(userID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockUser)(nil).GetByID), userID)
|
||||
}
|
||||
|
||||
// Register mocks base method.
|
||||
func (m *MockUser) Register(email, hashedPassword, username string, dateOfBirth time.Time) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Register", email, hashedPassword, username, dateOfBirth)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Register indicates an expected call of Register.
|
||||
func (mr *MockUserMockRecorder) Register(email, hashedPassword, username, dateOfBirth any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockUser)(nil).Register), email, hashedPassword, username, dateOfBirth)
|
||||
}
|
||||
|
||||
// MockChat is a mock of Chat interface.
|
||||
type MockChat struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockChatMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockChatMockRecorder is the mock recorder for MockChat.
|
||||
type MockChatMockRecorder struct {
|
||||
mock *MockChat
|
||||
}
|
||||
|
||||
// NewMockChat creates a new mock instance.
|
||||
func NewMockChat(ctrl *gomock.Controller) *MockChat {
|
||||
mock := &MockChat{ctrl: ctrl}
|
||||
mock.recorder = &MockChatMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockChat) EXPECT() *MockChatMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// MockRepo is a mock of Repo interface.
|
||||
type MockRepo struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockRepoMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockRepoMockRecorder is the mock recorder for MockRepo.
|
||||
type MockRepoMockRecorder struct {
|
||||
mock *MockRepo
|
||||
}
|
||||
|
||||
// NewMockRepo creates a new mock instance.
|
||||
func NewMockRepo(ctrl *gomock.Controller) *MockRepo {
|
||||
mock := &MockRepo{ctrl: ctrl}
|
||||
mock.recorder = &MockRepoMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockRepo) EXPECT() *MockRepoMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// FindOne mocks base method.
|
||||
func (m *MockRepo) FindOne(username, email string) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindOne", username, email)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindOne indicates an expected call of FindOne.
|
||||
func (mr *MockRepoMockRecorder) FindOne(username, email any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockRepo)(nil).FindOne), username, email)
|
||||
}
|
||||
|
||||
// GetAll mocks base method.
|
||||
func (m *MockRepo) GetAll(username string) ([]domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAll", username)
|
||||
ret0, _ := ret[0].([]domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAll indicates an expected call of GetAll.
|
||||
func (mr *MockRepoMockRecorder) GetAll(username any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockRepo)(nil).GetAll), username)
|
||||
}
|
||||
|
||||
// GetByID mocks base method.
|
||||
func (m *MockRepo) GetByID(userID int) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetByID", userID)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetByID indicates an expected call of GetByID.
|
||||
func (mr *MockRepoMockRecorder) GetByID(userID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockRepo)(nil).GetByID), userID)
|
||||
}
|
||||
|
||||
// Register mocks base method.
|
||||
func (m *MockRepo) Register(email, hashedPassword, username string, dateOfBirth time.Time) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Register", email, hashedPassword, username, dateOfBirth)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Register indicates an expected call of Register.
|
||||
func (mr *MockRepoMockRecorder) Register(email, hashedPassword, username, dateOfBirth any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockRepo)(nil).Register), email, hashedPassword, username, dateOfBirth)
|
||||
}
|
36
internal/repository/repository.go
Normal file
36
internal/repository/repository.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"time"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/logger"
|
||||
)
|
||||
|
||||
//go:generate mockgen -source=repository.go -destination=mocks/mock.go
|
||||
|
||||
type User interface {
|
||||
GetByID(userID int) (domain.User, error)
|
||||
GetAll(username string) ([]domain.User, error)
|
||||
FindOne(username, email string) (domain.User, error)
|
||||
Register(email, hashedPassword, username string, dateOfBirth time.Time) (domain.User, error)
|
||||
}
|
||||
|
||||
type Chat interface{}
|
||||
|
||||
type Repo interface {
|
||||
User
|
||||
Chat
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
User
|
||||
Chat
|
||||
}
|
||||
|
||||
func NewRepository(db *sqlx.DB, l logger.Log) *Repository {
|
||||
return &Repository{
|
||||
User: newUser(db, l),
|
||||
}
|
||||
}
|
23
internal/repository/repository_test.go
Normal file
23
internal/repository/repository_test.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/database"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
)
|
||||
|
||||
func Test_NewRepository(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
db, _, dbClose := database.GetMockDBx(t)
|
||||
defer dbClose()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
repo := NewRepository(db, log)
|
||||
|
||||
assert.Equal(t, &Repository{User: &userRepository{db: db, l: log}}, repo)
|
||||
}
|
120
internal/repository/users.go
Normal file
120
internal/repository/users.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/database"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/logger"
|
||||
)
|
||||
|
||||
type userRepository struct {
|
||||
db *sqlx.DB
|
||||
l logger.Log
|
||||
}
|
||||
|
||||
func newUser(db *sqlx.DB, l logger.Log) *userRepository {
|
||||
return &userRepository{db: db, l: l}
|
||||
}
|
||||
|
||||
func (r *userRepository) GetByID(userID int) (domain.User, error) {
|
||||
var user domain.User
|
||||
query := `SELECT * FROM users WHERE id = $1`
|
||||
if err := r.db.Get(&user, query, userID); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.User{}, domain.UserNotFoundError
|
||||
}
|
||||
return domain.User{}, domain.InternalServerError
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) GetAll(username string) ([]domain.User, error) {
|
||||
var users []domain.User
|
||||
username = fmt.Sprint("%", username, "%")
|
||||
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())
|
||||
return nil, domain.InternalServerError
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindOne(username, email string) (domain.User, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
var user domain.User
|
||||
query := `SELECT * FROM users`
|
||||
|
||||
if username != "" {
|
||||
conditions = append(conditions, `username = ?`)
|
||||
args = append(args, username)
|
||||
}
|
||||
if email != "" {
|
||||
conditions = append(conditions, `email = ?`)
|
||||
args = append(args, email)
|
||||
}
|
||||
|
||||
if len(conditions) > 0 {
|
||||
query += ` WHERE ` + strings.Join(conditions, ` AND `)
|
||||
}
|
||||
|
||||
query += ` LIMIT 1`
|
||||
|
||||
query = r.db.Rebind(query)
|
||||
err := r.db.Get(&user, query, args...)
|
||||
if err != nil {
|
||||
return domain.User{}, domain.InternalServerError
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) Register(email, hashedPassword, username string, dateOfBirth time.Time) (domain.User, error) {
|
||||
var user domain.User
|
||||
uQuery := `INSERT INTO users (email, hashed_password, username, date_of_birth) VALUES ($1, $2, $3, $4) RETURNING *`
|
||||
aQuery := `INSERT INTO user_avatar (user_id, avatar_image) VALUES ($1, $2)`
|
||||
|
||||
tx, err := r.db.Beginx()
|
||||
if err != nil {
|
||||
r.l.Errorf("user registration: tx begin: %s", err.Error())
|
||||
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())
|
||||
}
|
||||
|
||||
if err = tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
|
||||
r.l.Errorf("user registration: tx rollback: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = tx.Get(&user, uQuery, email, hashedPassword, username, dateOfBirth); err != nil {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) && pgErr.SQLState() == database.IntegrityErrorCode {
|
||||
return domain.User{}, domain.UserAlreadyExistsError
|
||||
}
|
||||
return domain.User{}, domain.InternalServerError
|
||||
}
|
||||
|
||||
if _, err = tx.Exec(aQuery, user.ID, user.AvatarImage); err != nil {
|
||||
return domain.User{}, domain.InternalServerError
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return domain.User{}, domain.InternalServerError
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
695
internal/repository/users_test.go
Normal file
695
internal/repository/users_test.go
Normal file
|
@ -0,0 +1,695 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/database"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
)
|
||||
|
||||
func Test_newUser(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
db, _, dbClose := database.GetMockDBx(t)
|
||||
defer dbClose()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
repo := newUser(db, log)
|
||||
|
||||
assert.Equal(t, &userRepository{db: db, l: log}, repo)
|
||||
}
|
||||
|
||||
func TestUserRepository_GetByID(t *testing.T) {
|
||||
type mockBehavior func(mock sqlmock.Sqlmock, userID int, rows *sqlmock.Rows, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
userID int
|
||||
mockBehavior mockBehavior
|
||||
dbRows *sqlmock.Rows
|
||||
dbErr error
|
||||
ud domain.User
|
||||
expectedUser domain.User
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
userID: 1,
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, userID int, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users WHERE id = \$1`).WithArgs(userID).WillReturnRows(rows)
|
||||
},
|
||||
dbRows: sqlmock.NewRows(
|
||||
[]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
},
|
||||
),
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "",
|
||||
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)},
|
||||
},
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "",
|
||||
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)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user_not_found",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, userID int, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users WHERE id = \$1`).WithArgs(userID).WillReturnError(err)
|
||||
},
|
||||
dbRows: sqlmock.NewRows(
|
||||
[]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
},
|
||||
),
|
||||
dbErr: sql.ErrNoRows,
|
||||
expectedErr: domain.UserNotFoundError,
|
||||
},
|
||||
{
|
||||
name: "internal_error",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, userID int, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users WHERE id = \$1`).WithArgs(userID).WillReturnError(err)
|
||||
},
|
||||
dbRows: sqlmock.NewRows(
|
||||
[]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
},
|
||||
),
|
||||
dbErr: errors.New("some error"),
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
db, mock, dbClose := database.GetMockDBx(t)
|
||||
defer dbClose()
|
||||
|
||||
tc.mockBehavior(mock, tc.userID, tc.dbRows.AddRow(
|
||||
tc.ud.ID,
|
||||
tc.ud.Role,
|
||||
tc.ud.Username,
|
||||
tc.ud.Email,
|
||||
tc.ud.HashedPassword,
|
||||
tc.ud.AvatarImage,
|
||||
tc.ud.BlackPhoenix,
|
||||
tc.ud.DateOfBirth,
|
||||
tc.ud.DateOfRegistration,
|
||||
), tc.dbErr)
|
||||
|
||||
repo := &userRepository{db: db}
|
||||
|
||||
u, err := repo.GetByID(tc.userID)
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepository_GetAll(t *testing.T) {
|
||||
type mockBehavior func(mock sqlmock.Sqlmock, username string, rows *sqlmock.Rows, err error)
|
||||
type logBehavior func(l *mock_logger.MockLog, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
username string
|
||||
mockBehavior mockBehavior
|
||||
logBehavior logBehavior
|
||||
dbRows *sqlmock.Rows
|
||||
dbErr error
|
||||
ud domain.User
|
||||
expectedUsers []domain.User
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(fmt.Sprintf(`SELECT \* FROM users WHERE username ILIKE \$1 AND role != %d`, domain.AdminUser)).WithArgs(username).WillReturnRows(rows)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, err error) {},
|
||||
dbRows: sqlmock.NewRows(
|
||||
[]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
},
|
||||
),
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "",
|
||||
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)},
|
||||
},
|
||||
expectedUsers: []domain.User{
|
||||
{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "",
|
||||
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)},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "any_error",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(fmt.Sprintf(`SELECT \* FROM users WHERE username ILIKE \$1 AND role != %d`, domain.AdminUser)).WithArgs(username).WillReturnError(err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, err error) {
|
||||
l.EXPECT().Errorf("getting users: %s", err.Error())
|
||||
},
|
||||
dbRows: sqlmock.NewRows(
|
||||
[]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
},
|
||||
),
|
||||
dbErr: errors.New("some error"),
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
db, mock, dbClose := database.GetMockDBx(t)
|
||||
defer dbClose()
|
||||
log := mock_logger.NewMockLog(c)
|
||||
|
||||
username := fmt.Sprint("%", tc.username, "%")
|
||||
|
||||
tc.mockBehavior(mock, username, tc.dbRows.AddRow(
|
||||
tc.ud.ID,
|
||||
tc.ud.Role,
|
||||
tc.ud.Username,
|
||||
tc.ud.Email,
|
||||
tc.ud.HashedPassword,
|
||||
tc.ud.AvatarImage,
|
||||
tc.ud.BlackPhoenix,
|
||||
tc.ud.DateOfBirth,
|
||||
tc.ud.DateOfRegistration,
|
||||
), tc.dbErr)
|
||||
tc.logBehavior(log, tc.dbErr)
|
||||
|
||||
repo := &userRepository{db: db, l: log}
|
||||
|
||||
users, err := repo.GetAll(tc.username)
|
||||
|
||||
assert.Equal(t, tc.expectedUsers, users)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepository_FindOne(t *testing.T) {
|
||||
type mockBehavior func(mock sqlmock.Sqlmock, username, email string, rows *sqlmock.Rows, err error)
|
||||
|
||||
dbRows := sqlmock.NewRows([]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
})
|
||||
okUd := domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "",
|
||||
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)},
|
||||
}
|
||||
okExpectedUser := domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "",
|
||||
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)},
|
||||
}
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
username string
|
||||
email string
|
||||
mockBehavior mockBehavior
|
||||
dbRows *sqlmock.Rows
|
||||
dbErr error
|
||||
ud domain.User
|
||||
expectedUser domain.User
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok_1",
|
||||
username: "urec",
|
||||
email: "mail@mail.ru",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username, email string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users WHERE username = \$1 AND email = \$2 LIMIT 1`).WithArgs(username, email).WillReturnRows(rows)
|
||||
},
|
||||
dbRows: dbRows,
|
||||
ud: okUd,
|
||||
expectedUser: okExpectedUser,
|
||||
},
|
||||
{
|
||||
name: "ok_2",
|
||||
username: "urec",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username, email string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users WHERE username = \$1 LIMIT 1`).WithArgs(username).WillReturnRows(rows)
|
||||
},
|
||||
dbRows: dbRows,
|
||||
ud: okUd,
|
||||
expectedUser: okExpectedUser,
|
||||
},
|
||||
{
|
||||
name: "ok_3",
|
||||
email: "mail@mail.ru",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username, email string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users WHERE email = \$1 LIMIT 1`).WithArgs(email).WillReturnRows(rows)
|
||||
},
|
||||
dbRows: dbRows,
|
||||
ud: okUd,
|
||||
expectedUser: okExpectedUser,
|
||||
},
|
||||
{
|
||||
name: "ok_4",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username, email string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users LIMIT 1`).WillReturnRows(rows)
|
||||
},
|
||||
dbRows: dbRows,
|
||||
ud: okUd,
|
||||
expectedUser: okExpectedUser,
|
||||
},
|
||||
{
|
||||
name: "any_error",
|
||||
mockBehavior: func(mock sqlmock.Sqlmock, username, email string, rows *sqlmock.Rows, err error) {
|
||||
mock.ExpectQuery(`SELECT \* FROM users LIMIT 1`).WillReturnError(err)
|
||||
},
|
||||
dbRows: dbRows,
|
||||
dbErr: errors.New("some error"),
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
db, mock, dbClose := database.GetMockDBx(t)
|
||||
defer dbClose()
|
||||
|
||||
tc.mockBehavior(mock, tc.username, tc.email, tc.dbRows.AddRow(
|
||||
tc.ud.ID,
|
||||
tc.ud.Role,
|
||||
tc.ud.Username,
|
||||
tc.ud.Email,
|
||||
tc.ud.HashedPassword,
|
||||
tc.ud.AvatarImage,
|
||||
tc.ud.BlackPhoenix,
|
||||
tc.ud.DateOfBirth,
|
||||
tc.ud.DateOfRegistration,
|
||||
), tc.dbErr)
|
||||
|
||||
repo := &userRepository{db: db}
|
||||
|
||||
u, err := repo.FindOne(tc.username, tc.email)
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRepository_Register(t *testing.T) {
|
||||
type mockParams struct {
|
||||
mock sqlmock.Sqlmock
|
||||
email, hashedPassword, username, avatarImage string
|
||||
dateOfBirth time.Time
|
||||
userID int
|
||||
rows *sqlmock.Rows
|
||||
err error
|
||||
}
|
||||
type mockBehavior func(p mockParams)
|
||||
type logBehavior func(log *mock_logger.MockLog, err error)
|
||||
|
||||
dbRows := sqlmock.NewRows([]string{
|
||||
"id",
|
||||
"role",
|
||||
"username",
|
||||
"email",
|
||||
"hashed_password",
|
||||
"avatar_image",
|
||||
"black_phoenix",
|
||||
"date_of_birth",
|
||||
"date_of_registration",
|
||||
})
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
mockParams mockParams
|
||||
mockBehavior mockBehavior
|
||||
logBehavior logBehavior
|
||||
ud domain.User
|
||||
expectedUser domain.User
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin()
|
||||
p.mock.ExpectQuery(
|
||||
`INSERT INTO users \(email, hashed_password, username, date_of_birth\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING \*`,
|
||||
).WithArgs(p.email, p.hashedPassword, p.username, p.dateOfBirth).WillReturnRows(p.rows)
|
||||
p.mock.ExpectExec(
|
||||
`INSERT INTO user_avatar \(user_id, avatar_image\) VALUES \(\$1, \$2\)`,
|
||||
).WithArgs(p.userID, p.avatarImage).WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
p.mock.ExpectCommit()
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user_already_exists",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
err: database.IntegrityError,
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin()
|
||||
p.mock.ExpectQuery(
|
||||
`INSERT INTO users \(email, hashed_password, username, date_of_birth\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING \*`,
|
||||
).WithArgs(p.email, p.hashedPassword, p.username, p.dateOfBirth).WillReturnError(p.err)
|
||||
p.mock.ExpectRollback()
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedErr: domain.UserAlreadyExistsError,
|
||||
},
|
||||
{
|
||||
name: "begin_error",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
err: errors.New("some error"),
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin().WillReturnError(p.err)
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {
|
||||
log.EXPECT().Errorf("user registration: tx begin: %s", err.Error())
|
||||
},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
{
|
||||
name: "get_error",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
err: errors.New("some error"),
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin()
|
||||
p.mock.ExpectQuery(
|
||||
`INSERT INTO users \(email, hashed_password, username, date_of_birth\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING \*`,
|
||||
).WithArgs(p.email, p.hashedPassword, p.username, p.dateOfBirth).WillReturnError(p.err)
|
||||
p.mock.ExpectRollback()
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {
|
||||
log.EXPECT().Errorf("user registration: %s", err.Error())
|
||||
},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
{
|
||||
name: "exec_error",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
err: errors.New("some error"),
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin()
|
||||
p.mock.ExpectQuery(
|
||||
`INSERT INTO users \(email, hashed_password, username, date_of_birth\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING \*`,
|
||||
).WithArgs(p.email, p.hashedPassword, p.username, p.dateOfBirth).WillReturnRows(p.rows)
|
||||
p.mock.ExpectExec(
|
||||
`INSERT INTO user_avatar \(user_id, avatar_image\) VALUES \(\$1, \$2\)`,
|
||||
).WithArgs(p.userID, p.avatarImage).WillReturnError(p.err)
|
||||
p.mock.ExpectRollback()
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {
|
||||
log.EXPECT().Errorf("user registration: %s", err.Error())
|
||||
},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
{
|
||||
name: "commit_error",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
err: errors.New("some error"),
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin()
|
||||
p.mock.ExpectQuery(
|
||||
`INSERT INTO users \(email, hashed_password, username, date_of_birth\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING \*`,
|
||||
).WithArgs(p.email, p.hashedPassword, p.username, p.dateOfBirth).WillReturnRows(p.rows)
|
||||
p.mock.ExpectExec(
|
||||
`INSERT INTO user_avatar \(user_id, avatar_image\) VALUES \(\$1, \$2\)`,
|
||||
).WithArgs(p.userID, p.avatarImage).WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
p.mock.ExpectCommit().WillReturnError(p.err)
|
||||
// p.mock.ExpectRollback().WillReturnError(sql.ErrTxDone) // с ним падает тест тк вызов роллбека не доходит
|
||||
// до драйвера и он его не регистрирует
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {
|
||||
log.EXPECT().Errorf("user registration: %s", err.Error())
|
||||
},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
{
|
||||
name: "rollback_error",
|
||||
mockParams: mockParams{
|
||||
rows: dbRows,
|
||||
err: database.IntegrityError,
|
||||
},
|
||||
mockBehavior: func(p mockParams) {
|
||||
p.mock.ExpectBegin()
|
||||
p.mock.ExpectQuery(
|
||||
`INSERT INTO users \(email, hashed_password, username, date_of_birth\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING \*`,
|
||||
).WithArgs(p.email, p.hashedPassword, p.username, p.dateOfBirth).WillReturnError(p.err)
|
||||
p.mock.ExpectRollback().WillReturnError(p.err)
|
||||
},
|
||||
logBehavior: func(log *mock_logger.MockLog, err error) {
|
||||
log.EXPECT().Errorf("user registration: tx rollback: %s", err.Error())
|
||||
},
|
||||
ud: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
HashedPassword: "hp",
|
||||
AvatarImage: "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)},
|
||||
},
|
||||
expectedErr: domain.UserAlreadyExistsError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
db, mock, dbClose := database.GetMockDBx(t)
|
||||
defer dbClose()
|
||||
log := mock_logger.NewMockLog(c)
|
||||
|
||||
tc.mockParams.mock = mock
|
||||
tc.mockParams.rows.AddRow(
|
||||
tc.ud.ID,
|
||||
tc.ud.Role,
|
||||
tc.ud.Username,
|
||||
tc.ud.Email,
|
||||
tc.ud.HashedPassword,
|
||||
tc.ud.AvatarImage,
|
||||
tc.ud.BlackPhoenix,
|
||||
tc.ud.DateOfBirth,
|
||||
tc.ud.DateOfRegistration,
|
||||
)
|
||||
tc.mockParams.email = tc.ud.Email
|
||||
tc.mockParams.hashedPassword = tc.ud.HashedPassword
|
||||
tc.mockParams.username = tc.ud.Username
|
||||
tc.mockParams.avatarImage = tc.ud.AvatarImage
|
||||
tc.mockParams.dateOfBirth = tc.ud.DateOfBirth.Time
|
||||
tc.mockParams.userID = tc.ud.ID
|
||||
|
||||
tc.mockBehavior(tc.mockParams)
|
||||
tc.logBehavior(log, tc.mockParams.err)
|
||||
|
||||
repo := &userRepository{db: db, l: log}
|
||||
|
||||
u, err := repo.Register(tc.mockParams.email, tc.mockParams.hashedPassword, tc.mockParams.username, tc.mockParams.dateOfBirth)
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
assert.NoError(t, tc.mockParams.mock.ExpectationsWereMet())
|
||||
})
|
||||
}
|
||||
}
|
95
internal/service/auth.go
Normal file
95
internal/service/auth.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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"
|
||||
)
|
||||
|
||||
const maxBcryptPasswordLen = 72
|
||||
|
||||
type authService struct {
|
||||
cfg config.JWT
|
||||
parser Parser
|
||||
l logger.Log
|
||||
}
|
||||
|
||||
func newAuthService(cfg config.JWT, l logger.Log) *authService {
|
||||
return &authService{cfg: cfg, parser: jwt.NewParser(), l: l}
|
||||
}
|
||||
|
||||
func (s *authService) keyFunc(*jwt.Token) (interface{}, error) {
|
||||
return s.cfg.SecretKey, nil
|
||||
}
|
||||
|
||||
func (s *authService) ExtractAuthToken(r *http.Request) (string, error) {
|
||||
token := r.Header.Get("Authorization")
|
||||
|
||||
tokenData := strings.Split(token, " ")
|
||||
if len(tokenData) != 2 || strings.ToLower(tokenData[0]) != "bearer" {
|
||||
return "", domain.TokenError
|
||||
|
||||
}
|
||||
|
||||
return tokenData[1], nil
|
||||
}
|
||||
|
||||
func (s *authService) DecodeAuthToken(token string) (int, error) {
|
||||
val, err := s.parser.Parse(token, s.keyFunc)
|
||||
if err != nil {
|
||||
return 0, domain.TokenError
|
||||
}
|
||||
|
||||
sub, err := val.Claims.GetSubject()
|
||||
if err != nil {
|
||||
return 0, domain.TokenError
|
||||
}
|
||||
|
||||
userID, err := strconv.Atoi(sub)
|
||||
if err != nil {
|
||||
return 0, domain.TokenError
|
||||
}
|
||||
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (s *authService) EncodeAuthToken(userID int) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"sub": fmt.Sprintf("%d", userID),
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour * 24 * 30).Unix(), // срок действия 30 дней
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
tokenString, err := token.SignedString(s.cfg.SecretKey)
|
||||
if err != nil {
|
||||
s.l.Errorf("singing jwt: %v", err)
|
||||
return "", domain.InternalServerError
|
||||
}
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
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())
|
||||
return "", domain.HashingError
|
||||
}
|
||||
return string(hash), nil
|
||||
}
|
||||
|
||||
func (s *authService) VerifyHashedPassword(p, hp string) bool {
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hp), []byte(p)); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
227
internal/service/auth_test.go
Normal file
227
internal/service/auth_test.go
Normal file
|
@ -0,0 +1,227 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks"
|
||||
)
|
||||
|
||||
func Test_newAuthService(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
cfg := config.JWT{}
|
||||
log := mock_logger.NewMockLog(c)
|
||||
|
||||
serv := newAuthService(cfg, log)
|
||||
|
||||
assert.Equal(t, &authService{cfg: cfg, parser: jwt.NewParser(), l: log}, serv)
|
||||
}
|
||||
|
||||
func TestAuthService_keyFunc(t *testing.T) {
|
||||
testTable := []struct {
|
||||
name string
|
||||
token *jwt.Token
|
||||
cfgSecretKey []byte
|
||||
expectedKey any
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok_1",
|
||||
cfgSecretKey: []byte("secretKey"),
|
||||
expectedKey: []byte("secretKey"),
|
||||
},
|
||||
{
|
||||
name: "ok_2",
|
||||
cfgSecretKey: []byte("another_secretKey"),
|
||||
expectedKey: []byte("another_secretKey"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
serv := &authService{cfg: config.JWT{SecretKey: tc.cfgSecretKey}}
|
||||
|
||||
key, err := serv.keyFunc(tc.token)
|
||||
|
||||
assert.Equal(t, tc.expectedKey, key)
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthService_ExtractAuthToken(t *testing.T) {
|
||||
testTable := []struct {
|
||||
name string
|
||||
req *http.Request
|
||||
reqHeader string
|
||||
reqToken string
|
||||
isError bool
|
||||
expectedToken string
|
||||
}{
|
||||
{
|
||||
name: "ok_1",
|
||||
req: &http.Request{Header: http.Header{}},
|
||||
reqHeader: "Authorization",
|
||||
reqToken: "Bearer 123",
|
||||
expectedToken: "123",
|
||||
},
|
||||
{
|
||||
name: "ok_2",
|
||||
req: &http.Request{Header: http.Header{}},
|
||||
reqHeader: "authorization",
|
||||
reqToken: "bearer 123",
|
||||
expectedToken: "123",
|
||||
},
|
||||
{
|
||||
name: "incorrect_header",
|
||||
req: &http.Request{Header: http.Header{}},
|
||||
reqToken: "Bearer 123",
|
||||
isError: true,
|
||||
},
|
||||
{
|
||||
name: "incorrect_token_prefix",
|
||||
req: &http.Request{Header: http.Header{}},
|
||||
reqHeader: "authorization",
|
||||
reqToken: "Beaer 123",
|
||||
isError: true,
|
||||
},
|
||||
{
|
||||
name: "incorrect_token",
|
||||
req: &http.Request{Header: http.Header{}},
|
||||
reqHeader: "authorization",
|
||||
reqToken: "Bearer",
|
||||
isError: true,
|
||||
},
|
||||
{
|
||||
name: "incorrect_token_length",
|
||||
req: &http.Request{Header: http.Header{}},
|
||||
reqHeader: "authorization",
|
||||
reqToken: "123",
|
||||
isError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.req.Header.Set(tc.reqHeader, tc.reqToken)
|
||||
auth := newAuthService(config.JWT{}, &logger.Logger{})
|
||||
|
||||
token, err := auth.ExtractAuthToken(tc.req)
|
||||
assert.Equal(t, tc.expectedToken, token)
|
||||
assert.Equal(t, tc.isError, err != nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthService_DecodeAuthToken(t *testing.T) {
|
||||
type parserBehavior func(p *mock_service.MockParser, token string, keyFunc jwt.Keyfunc, jwtToken *jwt.Token, err error)
|
||||
type jwtTokenConstructor func(claims Claims) *jwt.Token
|
||||
type claimsBehavior func(c *mock_service.MockClaims, subject string, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
parserBehavior parserBehavior
|
||||
claimsBehavior claimsBehavior
|
||||
jwtTokenConstructor jwtTokenConstructor
|
||||
parserToken string
|
||||
parserErr error
|
||||
claimsUserID string
|
||||
claimsErr error
|
||||
secretKey []byte
|
||||
keyFuncErr error
|
||||
expectedUserID int
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
parserBehavior: func(p *mock_service.MockParser, token string, keyFunc jwt.Keyfunc, jwtToken *jwt.Token, err error) {
|
||||
p.EXPECT().Parse(token, gomock.Any()).Return(jwtToken, err)
|
||||
},
|
||||
claimsBehavior: func(c *mock_service.MockClaims, subject string, err error) {
|
||||
c.EXPECT().GetSubject().Return(subject, err)
|
||||
},
|
||||
jwtTokenConstructor: func(claims Claims) *jwt.Token {
|
||||
return &jwt.Token{Claims: claims}
|
||||
},
|
||||
parserToken: "token",
|
||||
claimsUserID: "1",
|
||||
secretKey: []byte("secret_key"),
|
||||
expectedUserID: 1,
|
||||
},
|
||||
{
|
||||
name: "parser_error",
|
||||
parserBehavior: func(p *mock_service.MockParser, token string, keyFunc jwt.Keyfunc, jwtToken *jwt.Token, err error) {
|
||||
p.EXPECT().Parse(token, gomock.Any()).Return(jwtToken, err)
|
||||
},
|
||||
claimsBehavior: func(c *mock_service.MockClaims, subject string, err error) {},
|
||||
jwtTokenConstructor: func(claims Claims) *jwt.Token { return &jwt.Token{} },
|
||||
parserToken: "token",
|
||||
parserErr: domain.AnyError,
|
||||
expectedErr: domain.TokenError,
|
||||
},
|
||||
{
|
||||
name: "claims_error",
|
||||
parserBehavior: func(p *mock_service.MockParser, token string, keyFunc jwt.Keyfunc, jwtToken *jwt.Token, err error) {
|
||||
p.EXPECT().Parse(token, gomock.Any()).Return(jwtToken, err)
|
||||
},
|
||||
claimsBehavior: func(c *mock_service.MockClaims, subject string, err error) {
|
||||
c.EXPECT().GetSubject().Return(subject, err)
|
||||
},
|
||||
jwtTokenConstructor: func(claims Claims) *jwt.Token {
|
||||
return &jwt.Token{Claims: claims}
|
||||
},
|
||||
parserToken: "token",
|
||||
secretKey: []byte("secret_key"),
|
||||
claimsErr: domain.AnyError,
|
||||
expectedErr: domain.TokenError,
|
||||
},
|
||||
{
|
||||
name: "incorrect_id",
|
||||
parserBehavior: func(p *mock_service.MockParser, token string, keyFunc jwt.Keyfunc, jwtToken *jwt.Token, err error) {
|
||||
p.EXPECT().Parse(token, gomock.Any()).Return(jwtToken, err)
|
||||
},
|
||||
claimsBehavior: func(c *mock_service.MockClaims, subject string, err error) {
|
||||
c.EXPECT().GetSubject().Return(subject, err)
|
||||
},
|
||||
jwtTokenConstructor: func(claims Claims) *jwt.Token {
|
||||
return &jwt.Token{Claims: claims}
|
||||
},
|
||||
parserToken: "token",
|
||||
claimsUserID: "not_number",
|
||||
secretKey: []byte("secret_key"),
|
||||
expectedErr: domain.TokenError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
parser := mock_service.NewMockParser(c)
|
||||
claims := mock_service.NewMockClaims(c)
|
||||
|
||||
jwtToken := tc.jwtTokenConstructor(claims)
|
||||
|
||||
tc.claimsBehavior(claims, tc.claimsUserID, tc.claimsErr)
|
||||
|
||||
serv := &authService{cfg: config.JWT{SecretKey: tc.secretKey}, parser: parser}
|
||||
|
||||
tc.parserBehavior(parser, tc.parserToken, serv.keyFunc, jwtToken, tc.parserErr)
|
||||
|
||||
userID, err := serv.DecodeAuthToken(tc.parserToken)
|
||||
|
||||
assert.Equal(t, tc.expectedUserID, userID)
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
})
|
||||
}
|
||||
}
|
15
internal/service/chat.go
Normal file
15
internal/service/chat.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"git.urec56.ru/urec/chat_back_go/internal/logger"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/repository"
|
||||
)
|
||||
|
||||
type chatService struct {
|
||||
repo repository.Chat
|
||||
l logger.Log
|
||||
}
|
||||
|
||||
func newChatService(repo repository.Chat, l logger.Log) *chatService {
|
||||
return &chatService{repo: repo, l: l}
|
||||
}
|
22
internal/service/chat_test.go
Normal file
22
internal/service/chat_test.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_repository "git.urec56.ru/urec/chat_back_go/internal/repository/mocks"
|
||||
)
|
||||
|
||||
func Test_newChatService(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
repo := mock_repository.NewMockChat(c)
|
||||
log := mock_logger.NewMockLog(c)
|
||||
|
||||
serv := newChatService(repo, log)
|
||||
|
||||
assert.Equal(t, &chatService{repo: repo, l: log}, serv)
|
||||
}
|
612
internal/service/mocks/mock.go
Normal file
612
internal/service/mocks/mock.go
Normal file
|
@ -0,0 +1,612 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: service.go
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -source=service.go -destination=mocks/mock.go
|
||||
//
|
||||
|
||||
// Package mock_service is a generated GoMock package.
|
||||
package mock_service
|
||||
|
||||
import (
|
||||
http "net/http"
|
||||
reflect "reflect"
|
||||
|
||||
domain "git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
jwt "github.com/golang-jwt/jwt/v5"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockUser is a mock of User interface.
|
||||
type MockUser struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockUserMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockUserMockRecorder is the mock recorder for MockUser.
|
||||
type MockUserMockRecorder struct {
|
||||
mock *MockUser
|
||||
}
|
||||
|
||||
// NewMockUser creates a new mock instance.
|
||||
func NewMockUser(ctrl *gomock.Controller) *MockUser {
|
||||
mock := &MockUser{ctrl: ctrl}
|
||||
mock.recorder = &MockUserMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockUser) EXPECT() *MockUserMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// FindOne mocks base method.
|
||||
func (m *MockUser) FindOne(username, email string) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindOne", username, email)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindOne indicates an expected call of FindOne.
|
||||
func (mr *MockUserMockRecorder) FindOne(username, email any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockUser)(nil).FindOne), username, email)
|
||||
}
|
||||
|
||||
// Get mocks base method.
|
||||
func (m *MockUser) Get(userID int) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Get", userID)
|
||||
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 {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockUser)(nil).Get), userID)
|
||||
}
|
||||
|
||||
// GetAll mocks base method.
|
||||
func (m *MockUser) GetAll(username string) ([]domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAll", username)
|
||||
ret0, _ := ret[0].([]domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAll indicates an expected call of GetAll.
|
||||
func (mr *MockUserMockRecorder) GetAll(username any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockUser)(nil).GetAll), username)
|
||||
}
|
||||
|
||||
// GetVerificated mocks base method.
|
||||
func (m *MockUser) GetVerificated(userID int) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetVerificated", userID)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetVerificated indicates an expected call of GetVerificated.
|
||||
func (mr *MockUserMockRecorder) GetVerificated(userID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVerificated", reflect.TypeOf((*MockUser)(nil).GetVerificated), userID)
|
||||
}
|
||||
|
||||
// Register mocks base method.
|
||||
func (m *MockUser) Register(userData domain.UserRegister) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Register", userData)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Register indicates an expected call of Register.
|
||||
func (mr *MockUserMockRecorder) Register(userData any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockUser)(nil).Register), userData)
|
||||
}
|
||||
|
||||
// MockAuth is a mock of Auth interface.
|
||||
type MockAuth struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockAuthMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockAuthMockRecorder is the mock recorder for MockAuth.
|
||||
type MockAuthMockRecorder struct {
|
||||
mock *MockAuth
|
||||
}
|
||||
|
||||
// NewMockAuth creates a new mock instance.
|
||||
func NewMockAuth(ctrl *gomock.Controller) *MockAuth {
|
||||
mock := &MockAuth{ctrl: ctrl}
|
||||
mock.recorder = &MockAuthMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockAuth) EXPECT() *MockAuthMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// DecodeAuthToken mocks base method.
|
||||
func (m *MockAuth) DecodeAuthToken(token string) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DecodeAuthToken", token)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DecodeAuthToken indicates an expected call of DecodeAuthToken.
|
||||
func (mr *MockAuthMockRecorder) DecodeAuthToken(token any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeAuthToken", reflect.TypeOf((*MockAuth)(nil).DecodeAuthToken), token)
|
||||
}
|
||||
|
||||
// EncodeAuthToken mocks base method.
|
||||
func (m *MockAuth) EncodeAuthToken(userID int) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "EncodeAuthToken", userID)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// EncodeAuthToken indicates an expected call of EncodeAuthToken.
|
||||
func (mr *MockAuthMockRecorder) EncodeAuthToken(userID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncodeAuthToken", reflect.TypeOf((*MockAuth)(nil).EncodeAuthToken), userID)
|
||||
}
|
||||
|
||||
// ExtractAuthToken mocks base method.
|
||||
func (m *MockAuth) ExtractAuthToken(r *http.Request) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ExtractAuthToken", r)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ExtractAuthToken indicates an expected call of ExtractAuthToken.
|
||||
func (mr *MockAuthMockRecorder) ExtractAuthToken(r any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtractAuthToken", reflect.TypeOf((*MockAuth)(nil).ExtractAuthToken), r)
|
||||
}
|
||||
|
||||
// HashPassword mocks base method.
|
||||
func (m *MockAuth) HashPassword(p string) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "HashPassword", p)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// HashPassword indicates an expected call of HashPassword.
|
||||
func (mr *MockAuthMockRecorder) HashPassword(p any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HashPassword", reflect.TypeOf((*MockAuth)(nil).HashPassword), p)
|
||||
}
|
||||
|
||||
// VerifyHashedPassword mocks base method.
|
||||
func (m *MockAuth) VerifyHashedPassword(p, hp string) bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "VerifyHashedPassword", p, hp)
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// VerifyHashedPassword indicates an expected call of VerifyHashedPassword.
|
||||
func (mr *MockAuthMockRecorder) VerifyHashedPassword(p, hp any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyHashedPassword", reflect.TypeOf((*MockAuth)(nil).VerifyHashedPassword), p, hp)
|
||||
}
|
||||
|
||||
// MockChat is a mock of Chat interface.
|
||||
type MockChat struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockChatMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockChatMockRecorder is the mock recorder for MockChat.
|
||||
type MockChatMockRecorder struct {
|
||||
mock *MockChat
|
||||
}
|
||||
|
||||
// NewMockChat creates a new mock instance.
|
||||
func NewMockChat(ctrl *gomock.Controller) *MockChat {
|
||||
mock := &MockChat{ctrl: ctrl}
|
||||
mock.recorder = &MockChatMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockChat) EXPECT() *MockChatMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// MockParser is a mock of Parser interface.
|
||||
type MockParser struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockParserMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockParserMockRecorder is the mock recorder for MockParser.
|
||||
type MockParserMockRecorder struct {
|
||||
mock *MockParser
|
||||
}
|
||||
|
||||
// NewMockParser creates a new mock instance.
|
||||
func NewMockParser(ctrl *gomock.Controller) *MockParser {
|
||||
mock := &MockParser{ctrl: ctrl}
|
||||
mock.recorder = &MockParserMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockParser) EXPECT() *MockParserMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// DecodeSegment mocks base method.
|
||||
func (m *MockParser) DecodeSegment(seg string) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DecodeSegment", seg)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DecodeSegment indicates an expected call of DecodeSegment.
|
||||
func (mr *MockParserMockRecorder) DecodeSegment(seg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeSegment", reflect.TypeOf((*MockParser)(nil).DecodeSegment), seg)
|
||||
}
|
||||
|
||||
// Parse mocks base method.
|
||||
func (m *MockParser) Parse(tokenString string, keyFunc jwt.Keyfunc) (*jwt.Token, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Parse", tokenString, keyFunc)
|
||||
ret0, _ := ret[0].(*jwt.Token)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Parse indicates an expected call of Parse.
|
||||
func (mr *MockParserMockRecorder) Parse(tokenString, keyFunc any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockParser)(nil).Parse), tokenString, keyFunc)
|
||||
}
|
||||
|
||||
// ParseUnverified mocks base method.
|
||||
func (m *MockParser) ParseUnverified(tokenString string, claims jwt.Claims) (*jwt.Token, []string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ParseUnverified", tokenString, claims)
|
||||
ret0, _ := ret[0].(*jwt.Token)
|
||||
ret1, _ := ret[1].([]string)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// ParseUnverified indicates an expected call of ParseUnverified.
|
||||
func (mr *MockParserMockRecorder) ParseUnverified(tokenString, claims any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParseUnverified", reflect.TypeOf((*MockParser)(nil).ParseUnverified), tokenString, claims)
|
||||
}
|
||||
|
||||
// ParseWithClaims mocks base method.
|
||||
func (m *MockParser) ParseWithClaims(tokenString string, claims jwt.Claims, keyFunc jwt.Keyfunc) (*jwt.Token, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ParseWithClaims", tokenString, claims, keyFunc)
|
||||
ret0, _ := ret[0].(*jwt.Token)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ParseWithClaims indicates an expected call of ParseWithClaims.
|
||||
func (mr *MockParserMockRecorder) ParseWithClaims(tokenString, claims, keyFunc any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParseWithClaims", reflect.TypeOf((*MockParser)(nil).ParseWithClaims), tokenString, claims, keyFunc)
|
||||
}
|
||||
|
||||
// MockClaims is a mock of Claims interface.
|
||||
type MockClaims struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockClaimsMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockClaimsMockRecorder is the mock recorder for MockClaims.
|
||||
type MockClaimsMockRecorder struct {
|
||||
mock *MockClaims
|
||||
}
|
||||
|
||||
// NewMockClaims creates a new mock instance.
|
||||
func NewMockClaims(ctrl *gomock.Controller) *MockClaims {
|
||||
mock := &MockClaims{ctrl: ctrl}
|
||||
mock.recorder = &MockClaimsMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockClaims) EXPECT() *MockClaimsMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetAudience mocks base method.
|
||||
func (m *MockClaims) GetAudience() (jwt.ClaimStrings, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAudience")
|
||||
ret0, _ := ret[0].(jwt.ClaimStrings)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAudience indicates an expected call of GetAudience.
|
||||
func (mr *MockClaimsMockRecorder) GetAudience() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAudience", reflect.TypeOf((*MockClaims)(nil).GetAudience))
|
||||
}
|
||||
|
||||
// GetExpirationTime mocks base method.
|
||||
func (m *MockClaims) GetExpirationTime() (*jwt.NumericDate, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetExpirationTime")
|
||||
ret0, _ := ret[0].(*jwt.NumericDate)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetExpirationTime indicates an expected call of GetExpirationTime.
|
||||
func (mr *MockClaimsMockRecorder) GetExpirationTime() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpirationTime", reflect.TypeOf((*MockClaims)(nil).GetExpirationTime))
|
||||
}
|
||||
|
||||
// GetIssuedAt mocks base method.
|
||||
func (m *MockClaims) GetIssuedAt() (*jwt.NumericDate, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetIssuedAt")
|
||||
ret0, _ := ret[0].(*jwt.NumericDate)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetIssuedAt indicates an expected call of GetIssuedAt.
|
||||
func (mr *MockClaimsMockRecorder) GetIssuedAt() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuedAt", reflect.TypeOf((*MockClaims)(nil).GetIssuedAt))
|
||||
}
|
||||
|
||||
// GetIssuer mocks base method.
|
||||
func (m *MockClaims) GetIssuer() (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetIssuer")
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetIssuer indicates an expected call of GetIssuer.
|
||||
func (mr *MockClaimsMockRecorder) GetIssuer() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssuer", reflect.TypeOf((*MockClaims)(nil).GetIssuer))
|
||||
}
|
||||
|
||||
// GetNotBefore mocks base method.
|
||||
func (m *MockClaims) GetNotBefore() (*jwt.NumericDate, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetNotBefore")
|
||||
ret0, _ := ret[0].(*jwt.NumericDate)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetNotBefore indicates an expected call of GetNotBefore.
|
||||
func (mr *MockClaimsMockRecorder) GetNotBefore() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotBefore", reflect.TypeOf((*MockClaims)(nil).GetNotBefore))
|
||||
}
|
||||
|
||||
// GetSubject mocks base method.
|
||||
func (m *MockClaims) GetSubject() (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSubject")
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSubject indicates an expected call of GetSubject.
|
||||
func (mr *MockClaimsMockRecorder) GetSubject() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubject", reflect.TypeOf((*MockClaims)(nil).GetSubject))
|
||||
}
|
||||
|
||||
// MockServ is a mock of Serv interface.
|
||||
type MockServ struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockServMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockServMockRecorder is the mock recorder for MockServ.
|
||||
type MockServMockRecorder struct {
|
||||
mock *MockServ
|
||||
}
|
||||
|
||||
// NewMockServ creates a new mock instance.
|
||||
func NewMockServ(ctrl *gomock.Controller) *MockServ {
|
||||
mock := &MockServ{ctrl: ctrl}
|
||||
mock.recorder = &MockServMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockServ) EXPECT() *MockServMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// DecodeAuthToken mocks base method.
|
||||
func (m *MockServ) DecodeAuthToken(token string) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DecodeAuthToken", token)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DecodeAuthToken indicates an expected call of DecodeAuthToken.
|
||||
func (mr *MockServMockRecorder) DecodeAuthToken(token any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeAuthToken", reflect.TypeOf((*MockServ)(nil).DecodeAuthToken), token)
|
||||
}
|
||||
|
||||
// EncodeAuthToken mocks base method.
|
||||
func (m *MockServ) EncodeAuthToken(userID int) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "EncodeAuthToken", userID)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// EncodeAuthToken indicates an expected call of EncodeAuthToken.
|
||||
func (mr *MockServMockRecorder) EncodeAuthToken(userID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncodeAuthToken", reflect.TypeOf((*MockServ)(nil).EncodeAuthToken), userID)
|
||||
}
|
||||
|
||||
// ExtractAuthToken mocks base method.
|
||||
func (m *MockServ) ExtractAuthToken(r *http.Request) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ExtractAuthToken", r)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ExtractAuthToken indicates an expected call of ExtractAuthToken.
|
||||
func (mr *MockServMockRecorder) ExtractAuthToken(r any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtractAuthToken", reflect.TypeOf((*MockServ)(nil).ExtractAuthToken), r)
|
||||
}
|
||||
|
||||
// FindOne mocks base method.
|
||||
func (m *MockServ) FindOne(username, email string) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindOne", username, email)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindOne indicates an expected call of FindOne.
|
||||
func (mr *MockServMockRecorder) FindOne(username, email any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockServ)(nil).FindOne), username, email)
|
||||
}
|
||||
|
||||
// Get mocks base method.
|
||||
func (m *MockServ) Get(userID int) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Get", userID)
|
||||
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 {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockServ)(nil).Get), userID)
|
||||
}
|
||||
|
||||
// GetAll mocks base method.
|
||||
func (m *MockServ) GetAll(username string) ([]domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAll", username)
|
||||
ret0, _ := ret[0].([]domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAll indicates an expected call of GetAll.
|
||||
func (mr *MockServMockRecorder) GetAll(username any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockServ)(nil).GetAll), username)
|
||||
}
|
||||
|
||||
// GetVerificated mocks base method.
|
||||
func (m *MockServ) GetVerificated(userID int) (domain.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetVerificated", userID)
|
||||
ret0, _ := ret[0].(domain.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetVerificated indicates an expected call of GetVerificated.
|
||||
func (mr *MockServMockRecorder) GetVerificated(userID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVerificated", reflect.TypeOf((*MockServ)(nil).GetVerificated), userID)
|
||||
}
|
||||
|
||||
// HashPassword mocks base method.
|
||||
func (m *MockServ) HashPassword(p string) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "HashPassword", p)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// HashPassword indicates an expected call of HashPassword.
|
||||
func (mr *MockServMockRecorder) HashPassword(p any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HashPassword", reflect.TypeOf((*MockServ)(nil).HashPassword), p)
|
||||
}
|
||||
|
||||
// Register mocks base method.
|
||||
func (m *MockServ) Register(userData domain.UserRegister) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Register", userData)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Register indicates an expected call of Register.
|
||||
func (mr *MockServMockRecorder) Register(userData any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockServ)(nil).Register), userData)
|
||||
}
|
||||
|
||||
// VerifyHashedPassword mocks base method.
|
||||
func (m *MockServ) VerifyHashedPassword(p, hp string) bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "VerifyHashedPassword", p, hp)
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// VerifyHashedPassword indicates an expected call of VerifyHashedPassword.
|
||||
func (mr *MockServMockRecorder) VerifyHashedPassword(p, hp any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyHashedPassword", reflect.TypeOf((*MockServ)(nil).VerifyHashedPassword), p, hp)
|
||||
}
|
62
internal/service/service.go
Normal file
62
internal/service/service.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
//go:generate mockgen -source=service.go -destination=mocks/mock.go
|
||||
|
||||
type User interface {
|
||||
Get(userID int) (domain.User, error)
|
||||
GetVerificated(userID int) (domain.User, error)
|
||||
GetAll(username string) ([]domain.User, error)
|
||||
FindOne(username, email string) (domain.User, error)
|
||||
Register(userData domain.UserRegister) (string, error)
|
||||
}
|
||||
|
||||
type Auth interface {
|
||||
ExtractAuthToken(r *http.Request) (string, error)
|
||||
DecodeAuthToken(token string) (int, error)
|
||||
EncodeAuthToken(userID int) (string, error)
|
||||
HashPassword(p string) (string, error)
|
||||
VerifyHashedPassword(p, hp string) bool
|
||||
}
|
||||
|
||||
type Chat interface {
|
||||
}
|
||||
|
||||
type Parser interface {
|
||||
Parse(tokenString string, keyFunc jwt.Keyfunc) (*jwt.Token, error)
|
||||
ParseWithClaims(tokenString string, claims jwt.Claims, keyFunc jwt.Keyfunc) (*jwt.Token, error)
|
||||
ParseUnverified(tokenString string, claims jwt.Claims) (token *jwt.Token, parts []string, err error)
|
||||
DecodeSegment(seg string) ([]byte, error)
|
||||
}
|
||||
|
||||
type Claims interface{ jwt.Claims }
|
||||
|
||||
type Service struct {
|
||||
User
|
||||
Auth
|
||||
Chat
|
||||
}
|
||||
|
||||
type Serv interface {
|
||||
User
|
||||
Auth
|
||||
Chat
|
||||
}
|
||||
|
||||
func NewService(repo repository.Repo, cfg config.Config, l logger.Log) *Service {
|
||||
auth := newAuthService(cfg.JWT, l)
|
||||
return &Service{
|
||||
User: newUserService(repo, auth, l),
|
||||
Auth: auth,
|
||||
Chat: newChatService(repo, l),
|
||||
}
|
||||
}
|
31
internal/service/service_test.go
Normal file
31
internal/service/service_test.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/config"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_repository "git.urec56.ru/urec/chat_back_go/internal/repository/mocks"
|
||||
)
|
||||
|
||||
func Test_NewService(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
repo := mock_repository.NewMockRepo(c)
|
||||
log := mock_logger.NewMockLog(c)
|
||||
cfg := config.Config{}
|
||||
|
||||
serv := NewService(repo, cfg, log)
|
||||
auth := &authService{cfg: cfg.JWT, parser: jwt.NewParser(), l: log}
|
||||
expectedServ := &Service{
|
||||
User: &userService{ur: repo, auth: auth, l: log},
|
||||
Auth: auth,
|
||||
Chat: &chatService{repo: repo, l: log},
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedServ, serv)
|
||||
}
|
69
internal/service/users.go
Normal file
69
internal/service/users.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type userService struct {
|
||||
ur repository.User
|
||||
auth Auth
|
||||
l logger.Log
|
||||
}
|
||||
|
||||
func newUserService(user repository.User, auth Auth, l logger.Log) *userService {
|
||||
return &userService{ur: user, auth: auth, l: l}
|
||||
}
|
||||
|
||||
func (s *userService) Get(userID int) (domain.User, error) {
|
||||
u, err := s.ur.GetByID(userID)
|
||||
return u, err
|
||||
}
|
||||
|
||||
func (s *userService) GetVerificated(userID int) (domain.User, error) {
|
||||
u, err := s.ur.GetByID(userID)
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
if u.Role < domain.VerificatedUser {
|
||||
return u, domain.UnverifiedUserError
|
||||
}
|
||||
return u, err
|
||||
}
|
||||
|
||||
func (s *userService) GetAll(username string) ([]domain.User, error) {
|
||||
users, err := s.ur.GetAll(username)
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (s *userService) FindOne(username, email string) (domain.User, error) {
|
||||
user, err := s.ur.FindOne(username, email)
|
||||
if err != nil {
|
||||
return domain.User{}, domain.InternalServerError
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *userService) Register(userData domain.UserRegister) (string, error) {
|
||||
hashedPassword, err := s.auth.HashPassword(userData.Password)
|
||||
if err != nil {
|
||||
return "", domain.InternalServerError
|
||||
}
|
||||
|
||||
user, err := s.ur.Register(userData.Email, hashedPassword, userData.Username, userData.DateOfBirth.Time)
|
||||
if err != nil {
|
||||
if errors.Is(err, domain.UserAlreadyExistsError) {
|
||||
return "", err
|
||||
}
|
||||
return "", domain.InternalServerError
|
||||
}
|
||||
|
||||
authToken, err := s.auth.EncodeAuthToken(user.ID)
|
||||
if err != nil {
|
||||
return "", domain.InternalServerError
|
||||
}
|
||||
return authToken, err
|
||||
}
|
357
internal/service/users_test.go
Normal file
357
internal/service/users_test.go
Normal file
|
@ -0,0 +1,357 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
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_newUserService(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
repo := mock_repository.NewMockUser(c)
|
||||
auth := mock_service.NewMockAuth(c)
|
||||
log := mock_logger.NewMockLog(c)
|
||||
|
||||
serv := newUserService(repo, auth, log)
|
||||
|
||||
assert.Equal(t, &userService{ur: repo, auth: auth, l: log}, serv)
|
||||
}
|
||||
|
||||
func TestUserService_Get(t *testing.T) {
|
||||
type mockBehavior func(r *mock_repository.MockUser, userID int, user domain.User, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
mockBehavior mockBehavior
|
||||
userID int
|
||||
expectedUser domain.User
|
||||
repoErr error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
mockBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {
|
||||
r.EXPECT().GetByID(userID).Return(user, err)
|
||||
},
|
||||
userID: 1,
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user_not_found",
|
||||
mockBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {
|
||||
r.EXPECT().GetByID(userID).Return(user, err)
|
||||
},
|
||||
repoErr: domain.UserNotFoundError,
|
||||
expectedErr: domain.UserNotFoundError,
|
||||
},
|
||||
{
|
||||
name: "internal_error",
|
||||
mockBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {
|
||||
r.EXPECT().GetByID(userID).Return(user, err)
|
||||
},
|
||||
repoErr: domain.InternalServerError,
|
||||
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.NewMockUser(c)
|
||||
serv := userService{ur: repo}
|
||||
|
||||
tc.mockBehavior(repo, tc.userID, tc.expectedUser, tc.repoErr)
|
||||
|
||||
u, err := serv.Get(tc.userID)
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserService_GetVerificated(t *testing.T) {
|
||||
type mockBehavior func(r *mock_repository.MockUser, userID int, user domain.User, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
mockBehavior mockBehavior
|
||||
userID int
|
||||
expectedUser domain.User
|
||||
repoErr error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
mockBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {
|
||||
r.EXPECT().GetByID(userID).Return(user, err)
|
||||
},
|
||||
userID: 1,
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: domain.VerificatedUser,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user_not_found",
|
||||
mockBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {
|
||||
r.EXPECT().GetByID(userID).Return(user, err)
|
||||
},
|
||||
userID: 1,
|
||||
repoErr: domain.UserNotFoundError,
|
||||
expectedErr: domain.UserNotFoundError,
|
||||
},
|
||||
{
|
||||
name: "user_unverified",
|
||||
mockBehavior: func(r *mock_repository.MockUser, userID int, user domain.User, err error) {
|
||||
r.EXPECT().GetByID(userID).Return(user, err)
|
||||
},
|
||||
userID: 1,
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: domain.UnverifiedUser,
|
||||
},
|
||||
expectedErr: domain.UnverifiedUserError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
repo := mock_repository.NewMockUser(c)
|
||||
serv := userService{ur: repo}
|
||||
|
||||
tc.mockBehavior(repo, tc.userID, tc.expectedUser, tc.repoErr)
|
||||
|
||||
u, err := serv.GetVerificated(tc.userID)
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserService_GetAll(t *testing.T) {
|
||||
type mockBehavior func(r *mock_repository.MockUser, username string, users []domain.User, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
mockBehavior mockBehavior
|
||||
username string
|
||||
expectedUsers []domain.User
|
||||
repoErr error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
mockBehavior: func(r *mock_repository.MockUser, username string, users []domain.User, err error) {
|
||||
r.EXPECT().GetAll(username).Return(users, err)
|
||||
},
|
||||
expectedUsers: []domain.User{
|
||||
{ID: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "internal_error",
|
||||
mockBehavior: func(r *mock_repository.MockUser, username string, users []domain.User, err error) {
|
||||
r.EXPECT().GetAll(username).Return(users, err)
|
||||
},
|
||||
repoErr: domain.InternalServerError,
|
||||
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.NewMockUser(c)
|
||||
serv := userService{ur: repo}
|
||||
|
||||
tc.mockBehavior(repo, tc.username, tc.expectedUsers, tc.repoErr)
|
||||
|
||||
u, err := serv.GetAll(tc.username)
|
||||
|
||||
assert.Equal(t, tc.expectedUsers, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserService_FindOne(t *testing.T) {
|
||||
type mockBehavior func(r *mock_repository.MockUser, username, email string, user domain.User, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
mockBehavior mockBehavior
|
||||
username string
|
||||
email string
|
||||
expectedUser domain.User
|
||||
repoErr error
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
mockBehavior: func(r *mock_repository.MockUser, username, email string, user domain.User, err error) {
|
||||
r.EXPECT().FindOne(username, email).Return(user, err)
|
||||
},
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: domain.UnverifiedUser,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "any_error",
|
||||
mockBehavior: func(r *mock_repository.MockUser, username, email string, user domain.User, err error) {
|
||||
r.EXPECT().FindOne(username, email).Return(user, err)
|
||||
},
|
||||
repoErr: errors.New("some error"),
|
||||
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.NewMockUser(c)
|
||||
serv := userService{ur: repo}
|
||||
|
||||
tc.mockBehavior(repo, tc.username, tc.email, tc.expectedUser, tc.repoErr)
|
||||
|
||||
u, err := serv.FindOne(tc.username, tc.email)
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserService_Register(t *testing.T) {
|
||||
type hashMockBehavior func(s *mock_service.MockAuth, p, hp string, err error)
|
||||
type repoMockBehavior func(r *mock_repository.MockUser, email, hp, username string, dateOfBirth time.Time, user domain.User, err error)
|
||||
type encodeMockBehavior func(s *mock_service.MockAuth, userID int, token string, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
hashMockBehavior hashMockBehavior
|
||||
repoMockBehavior repoMockBehavior
|
||||
encodeMockBehavior encodeMockBehavior
|
||||
ud domain.UserRegister
|
||||
hp string
|
||||
repoUser domain.User
|
||||
hashErr error
|
||||
repoErr error
|
||||
encodeErr error
|
||||
expectedToken string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
hashMockBehavior: func(s *mock_service.MockAuth, p, hp string, err error) {
|
||||
s.EXPECT().HashPassword(p).Return(hp, err)
|
||||
},
|
||||
repoMockBehavior: func(r *mock_repository.MockUser, email, hp, username string, dateOfBirth time.Time, user domain.User, err error) {
|
||||
r.EXPECT().Register(email, hp, username, dateOfBirth).Return(user, err)
|
||||
},
|
||||
encodeMockBehavior: func(s *mock_service.MockAuth, userID int, token string, err error) {
|
||||
s.EXPECT().EncodeAuthToken(userID).Return(token, err)
|
||||
},
|
||||
ud: domain.UserRegister{
|
||||
Email: "mail@mail.ru",
|
||||
Username: "username",
|
||||
Password: "pass",
|
||||
},
|
||||
hp: "hp",
|
||||
repoUser: domain.User{ID: 1},
|
||||
expectedToken: "token",
|
||||
},
|
||||
{
|
||||
name: "user_already_exists",
|
||||
hashMockBehavior: func(s *mock_service.MockAuth, p, hp string, err error) {
|
||||
s.EXPECT().HashPassword(p).Return(hp, err)
|
||||
},
|
||||
repoMockBehavior: func(r *mock_repository.MockUser, email, hp, username string, dateOfBirth time.Time, user domain.User, err error) {
|
||||
r.EXPECT().Register(email, hp, username, dateOfBirth).Return(user, err)
|
||||
},
|
||||
encodeMockBehavior: func(s *mock_service.MockAuth, userID int, token string, err error) {},
|
||||
repoErr: domain.UserAlreadyExistsError,
|
||||
expectedErr: domain.UserAlreadyExistsError,
|
||||
},
|
||||
{
|
||||
name: "hash_failed",
|
||||
hashMockBehavior: func(s *mock_service.MockAuth, p, hp string, err error) {
|
||||
s.EXPECT().HashPassword(p).Return(hp, err)
|
||||
},
|
||||
repoMockBehavior: func(r *mock_repository.MockUser, email, hp, username string, dateOfBirth time.Time, user domain.User, err error) {
|
||||
},
|
||||
encodeMockBehavior: func(s *mock_service.MockAuth, userID int, token string, err error) {},
|
||||
hashErr: errors.New("some error"),
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
{
|
||||
name: "db_error",
|
||||
hashMockBehavior: func(s *mock_service.MockAuth, p, hp string, err error) {
|
||||
s.EXPECT().HashPassword(p).Return(hp, err)
|
||||
},
|
||||
repoMockBehavior: func(r *mock_repository.MockUser, email, hp, username string, dateOfBirth time.Time, user domain.User, err error) {
|
||||
r.EXPECT().Register(email, hp, username, dateOfBirth).Return(user, err)
|
||||
},
|
||||
encodeMockBehavior: func(s *mock_service.MockAuth, userID int, token string, err error) {},
|
||||
repoErr: errors.New("some error"),
|
||||
expectedErr: domain.InternalServerError,
|
||||
},
|
||||
{
|
||||
name: "token_encoding_error",
|
||||
hashMockBehavior: func(s *mock_service.MockAuth, p, hp string, err error) {
|
||||
s.EXPECT().HashPassword(p).Return(hp, err)
|
||||
},
|
||||
repoMockBehavior: func(r *mock_repository.MockUser, email, hp, username string, dateOfBirth time.Time, user domain.User, err error) {
|
||||
r.EXPECT().Register(email, hp, username, dateOfBirth).Return(user, err)
|
||||
},
|
||||
encodeMockBehavior: func(s *mock_service.MockAuth, userID int, token string, err error) {
|
||||
s.EXPECT().EncodeAuthToken(userID).Return(token, err)
|
||||
},
|
||||
encodeErr: errors.New("some error"),
|
||||
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.NewMockUser(c)
|
||||
auth := mock_service.NewMockAuth(c)
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := userService{ur: repo, auth: auth, l: log}
|
||||
|
||||
tc.hashMockBehavior(auth, tc.ud.Password, tc.hp, tc.hashErr)
|
||||
tc.repoMockBehavior(repo, tc.ud.Email, tc.hp, tc.ud.Username, tc.ud.DateOfBirth.Time, tc.repoUser, tc.repoErr)
|
||||
tc.encodeMockBehavior(auth, tc.repoUser.ID, tc.expectedToken, tc.encodeErr)
|
||||
|
||||
token, err := serv.Register(tc.ud)
|
||||
|
||||
assert.Equal(t, tc.expectedToken, token)
|
||||
assert.ErrorIs(t, err, tc.expectedErr)
|
||||
})
|
||||
}
|
||||
}
|
64
internal/transport/rest/handler/chat.go
Normal file
64
internal/transport/rest/handler/chat.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetChats args: userID
|
||||
// Middleware stack: middleware.VerificatedAuth
|
||||
func (h *Handler) GetChats(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) ChangeChatData(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) GetLastMessage(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) GetSomeMessages(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) GetMessageByID(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) CreateInvitationLink(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) InviteToChat(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteChat(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteUserFromChat(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) PinChat(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) UnpinnChat(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) GetPinnedChats(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) GetPinnedMessages(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
134
internal/transport/rest/handler/chat_test.go
Normal file
134
internal/transport/rest/handler/chat_test.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandler_GetChats(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.GetChats(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_Create(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.Create(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_ChangeChatData(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.ChangeChatData(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_GetLastMessage(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.GetLastMessage(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_GetSomeMessages(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.GetSomeMessages(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_GetMessageByID(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.GetMessageByID(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_CreateInvitationLink(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.CreateInvitationLink(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_InviteToChat(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.InviteToChat(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_DeleteChat(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.DeleteChat(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_DeleteUserFromChat(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.DeleteUserFromChat(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_PinChat(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.PinChat(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_UnpinnChat(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.UnpinnChat(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_GetPinnedChats(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.GetPinnedChats(w, req)
|
||||
}
|
||||
|
||||
func TestHandler_GetPinnedMessages(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.GetPinnedMessages(w, req)
|
||||
}
|
15
internal/transport/rest/handler/handler.go
Normal file
15
internal/transport/rest/handler/handler.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"git.urec56.ru/urec/chat_back_go/internal/logger"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/service"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
serv service.Serv
|
||||
l logger.Log
|
||||
}
|
||||
|
||||
func NewHandler(serv service.Serv, l logger.Log) *Handler {
|
||||
return &Handler{serv: serv, l: l}
|
||||
}
|
22
internal/transport/rest/handler/handler_test.go
Normal file
22
internal/transport/rest/handler/handler_test.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks"
|
||||
)
|
||||
|
||||
func Test_NewHandler(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
serv := mock_service.NewMockServ(c)
|
||||
log := mock_logger.NewMockLog(c)
|
||||
|
||||
h := NewHandler(serv, log)
|
||||
|
||||
assert.Equal(t, &Handler{serv: serv, l: log}, h)
|
||||
}
|
14
internal/transport/rest/handler/images.go
Normal file
14
internal/transport/rest/handler/images.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (h *Handler) UploadAvatar(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) UploadImage(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
28
internal/transport/rest/handler/images_test.go
Normal file
28
internal/transport/rest/handler/images_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandler_UploadAvatar(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.UploadAvatar(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_UploadImage(t *testing.T) {
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
h.UploadImage(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
21
internal/transport/rest/handler/mocks/mock.go
Normal file
21
internal/transport/rest/handler/mocks/mock.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package mock_http
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
type Recorder struct {
|
||||
*httptest.ResponseRecorder
|
||||
err error
|
||||
}
|
||||
|
||||
func NewRecorder(err error) *Recorder {
|
||||
return &Recorder{ResponseRecorder: httptest.NewRecorder(), err: err}
|
||||
}
|
||||
|
||||
func (r *Recorder) Write(buf []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
return r.ResponseRecorder.Write(buf)
|
||||
}
|
144
internal/transport/rest/handler/users.go
Normal file
144
internal/transport/rest/handler/users.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
)
|
||||
|
||||
// GetUsers args: query: optional(username)
|
||||
func (h *Handler) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
v := r.URL.Query()
|
||||
username := v.Get("username")
|
||||
|
||||
users, err := h.serv.GetAll(username)
|
||||
if err != nil {
|
||||
h.l.Infof("[%s] getting users: %s", r.URL.Path, err.Error())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
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())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CheckExisting args: body: optional(username), optional(email)
|
||||
func (h *Handler) CheckExisting(w http.ResponseWriter, r *http.Request) {
|
||||
var userFilter domain.UserFilter
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&userFilter); err != nil {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
if err := domain.V.Struct(userFilter); err != nil {
|
||||
h.l.Infof("[%s] validation: %s", r.URL.Path, err.Error())
|
||||
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())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if u.ID != 0 { // zero value
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckExistingPassword args: body: password
|
||||
func (h *Handler) CheckExistingPassword(w http.ResponseWriter, _ *http.Request) {
|
||||
randInt := rand.Intn(10)
|
||||
if randInt > 5 {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
|
||||
var userData domain.UserRegister
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&userData); err != nil {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
if err := domain.V.Struct(userData); err != nil {
|
||||
h.l.Infof("[%s] validation: %s", r.URL.Path, err.Error())
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := h.serv.Register(userData)
|
||||
if err != nil {
|
||||
if errors.Is(err, domain.UserAlreadyExistsError) {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
} else {
|
||||
h.l.Infof("[%s] serv.Register: %s", r.URL.Path, err.Error())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resp, _ := json.Marshal(map[string]string{"authorization": fmt.Sprintf("Bearer %s", token)}) // no error here
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
if _, err = w.Write(resp); err != nil {
|
||||
h.l.Errorf("[%s] writing response: %s", r.URL.Path, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) ResendEmailVerification(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) EmailVerification(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := r.Context().Value("user").(domain.User)
|
||||
if !ok {
|
||||
h.l.Errorf("[%s] extracting user from ctx", r.URL.Path)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
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())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) GetAvatarsHistory(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) SendConfirmationCode(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
||||
|
||||
func (h *Handler) ChangeUserData(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("")
|
||||
}
|
639
internal/transport/rest/handler/users_test.go
Normal file
639
internal/transport/rest/handler/users_test.go
Normal file
|
@ -0,0 +1,639 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks"
|
||||
mock_http "git.urec56.ru/urec/chat_back_go/internal/transport/rest/handler/mocks"
|
||||
)
|
||||
|
||||
func TestHandler_GetUsers(t *testing.T) {
|
||||
type servBehavior func(s *mock_service.MockServ, username string, users []domain.User, err error)
|
||||
type logBehavior func(l *mock_logger.MockLog, path string, err error)
|
||||
testTable := []struct {
|
||||
name string
|
||||
username string
|
||||
servBehavior servBehavior
|
||||
servUsers []domain.User
|
||||
servErr error
|
||||
logBehavior logBehavior
|
||||
logErr error
|
||||
writeErr error
|
||||
expectedUsers map[string][]domain.User
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "ok_1",
|
||||
username: "urec",
|
||||
servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) {
|
||||
s.EXPECT().GetAll(username).Return(users, err)
|
||||
},
|
||||
servUsers: []domain.User{
|
||||
{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
expectedUsers: map[string][]domain.User{
|
||||
"users": {
|
||||
{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "ok_2",
|
||||
servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) {
|
||||
s.EXPECT().GetAll(username).Return(users, err)
|
||||
},
|
||||
servUsers: []domain.User{
|
||||
{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
expectedUsers: map[string][]domain.User{
|
||||
"users": {
|
||||
{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "serv_error",
|
||||
servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) {
|
||||
s.EXPECT().GetAll(username).Return(users, err)
|
||||
},
|
||||
servErr: domain.AnyError,
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] getting users: %s", path, err.Error())
|
||||
},
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "response_writing_error",
|
||||
servBehavior: func(s *mock_service.MockServ, username string, users []domain.User, err error) {
|
||||
s.EXPECT().GetAll(username).Return(users, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Errorf("[%s] writing response: %s", path, err.Error())
|
||||
},
|
||||
logErr: domain.AnyError,
|
||||
writeErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
reqPath := "/users"
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
tc.logBehavior(log, reqPath, tc.logErr)
|
||||
tc.servBehavior(serv, tc.username, tc.servUsers, tc.servErr)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, reqPath, nil)
|
||||
w := mock_http.NewRecorder(tc.writeErr)
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Add("username", tc.username)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.GetUsers(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var u map[string][]domain.User
|
||||
|
||||
if tc.expectedStatusCode == http.StatusOK {
|
||||
err = json.Unmarshal(b, &u)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedUsers, u)
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_CheckExisting(t *testing.T) {
|
||||
type servBehavior func(s *mock_service.MockServ, username, email string, user domain.User, err error)
|
||||
type logBehavior func(l *mock_logger.MockLog, path string, err error)
|
||||
testTable := []struct {
|
||||
name string
|
||||
reqBody domain.UserFilter
|
||||
servBehavior servBehavior
|
||||
logBehavior logBehavior
|
||||
servUser domain.User
|
||||
servErr error
|
||||
logErr error
|
||||
isDecodeError bool
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
reqBody: domain.UserFilter{Username: "urecs", Email: "mail@atod.com"},
|
||||
servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {
|
||||
s.EXPECT().FindOne(username, email).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "decoding_body_error",
|
||||
servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
isDecodeError: true,
|
||||
expectedStatusCode: http.StatusUnprocessableEntity,
|
||||
},
|
||||
{
|
||||
name: "validation_error",
|
||||
reqBody: domain.UserFilter{Username: "u"},
|
||||
servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] validation: %s", path, err.Error())
|
||||
},
|
||||
logErr: errors.New("Key: 'UserFilter.Username' Error:Field validation for 'Username' failed on the 'min' tag"),
|
||||
expectedStatusCode: http.StatusUnprocessableEntity,
|
||||
},
|
||||
{
|
||||
name: "serv_error",
|
||||
reqBody: domain.UserFilter{Username: "urec"},
|
||||
servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {
|
||||
s.EXPECT().FindOne(username, email).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] serv.FindOne error: %s", path, err.Error())
|
||||
},
|
||||
servErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "user_already_exists",
|
||||
reqBody: domain.UserFilter{Username: "urec"},
|
||||
servUser: domain.User{ID: 1},
|
||||
servBehavior: func(s *mock_service.MockServ, username, email string, user domain.User, err error) {
|
||||
s.EXPECT().FindOne(username, email).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
expectedStatusCode: http.StatusConflict,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
reqPath := "/check_existing_user"
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
tc.logBehavior(log, reqPath, tc.logErr)
|
||||
tc.servBehavior(serv, tc.reqBody.Username, tc.reqBody.Email, tc.servUser, tc.servErr)
|
||||
|
||||
var body []byte
|
||||
if !tc.isDecodeError {
|
||||
b, err := json.Marshal(tc.reqBody)
|
||||
assert.NoError(t, err)
|
||||
body = b
|
||||
} else {
|
||||
b := make([]byte, 0)
|
||||
body = b
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, reqPath, bytes.NewBuffer(body))
|
||||
w := mock_http.NewRecorder(nil)
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.CheckExisting(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_CheckExistingPassword(t *testing.T) {
|
||||
reqPath := "/check_existing_password"
|
||||
req := httptest.NewRequest(http.MethodPost, reqPath, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
h.CheckExistingPassword(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_Register(t *testing.T) {
|
||||
type servBehavior func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error)
|
||||
type logBehavior func(l *mock_logger.MockLog, path string, err error)
|
||||
testTable := []struct {
|
||||
name string
|
||||
reqBody domain.UserRegister
|
||||
servBehavior servBehavior
|
||||
logBehavior logBehavior
|
||||
servToken string
|
||||
servErr error
|
||||
logErr error
|
||||
isDecodeError bool
|
||||
writeErr error
|
||||
expectedStatusCode int
|
||||
expectedBody map[string]string
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
reqBody: domain.UserRegister{
|
||||
Username: "urecs",
|
||||
Email: "mail@atod.com",
|
||||
Password: "password",
|
||||
Password2: "password",
|
||||
DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {
|
||||
s.EXPECT().Register(userData).Return(token, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
servToken: "token",
|
||||
expectedStatusCode: http.StatusCreated,
|
||||
expectedBody: map[string]string{"authorization": "Bearer token"},
|
||||
},
|
||||
{
|
||||
name: "user_already_exists",
|
||||
reqBody: domain.UserRegister{
|
||||
Username: "urec",
|
||||
Email: "mail@atod.com",
|
||||
Password: "password",
|
||||
Password2: "password",
|
||||
DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {
|
||||
s.EXPECT().Register(userData).Return(token, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
servErr: domain.UserAlreadyExistsError,
|
||||
expectedStatusCode: http.StatusConflict,
|
||||
},
|
||||
{
|
||||
name: "decoding_body_error",
|
||||
servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
isDecodeError: true,
|
||||
expectedStatusCode: http.StatusUnprocessableEntity,
|
||||
},
|
||||
{
|
||||
name: "validation_error",
|
||||
reqBody: domain.UserRegister{
|
||||
Email: "mail@atod.com",
|
||||
Password: "password",
|
||||
Password2: "password",
|
||||
DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] validation: %s", path, err.Error())
|
||||
},
|
||||
logErr: errors.New("Key: 'UserRegister.Username' Error:Field validation for 'Username' failed on the 'min' tag"),
|
||||
expectedStatusCode: http.StatusUnprocessableEntity,
|
||||
},
|
||||
{
|
||||
name: "serv_error",
|
||||
reqBody: domain.UserRegister{
|
||||
Username: "urec",
|
||||
Email: "mail@atod.com",
|
||||
Password: "password",
|
||||
Password2: "password",
|
||||
DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {
|
||||
s.EXPECT().Register(userData).Return(token, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] serv.Register: %s", path, err.Error())
|
||||
},
|
||||
servErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "response_writing_error",
|
||||
reqBody: domain.UserRegister{
|
||||
Username: "urec",
|
||||
Email: "mail@atod.com",
|
||||
Password: "password",
|
||||
Password2: "password",
|
||||
DateOfBirth: domain.CustomDate{Time: time.Date(2002, time.February, 2, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
servBehavior: func(s *mock_service.MockServ, userData domain.UserRegister, token string, err error) {
|
||||
s.EXPECT().Register(userData).Return(token, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Errorf("[%s] writing response: %s", path, err.Error())
|
||||
},
|
||||
writeErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusCreated,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
reqPath := "/register"
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
tc.logBehavior(log, reqPath, tc.logErr)
|
||||
tc.servBehavior(serv, tc.reqBody, tc.servToken, tc.servErr)
|
||||
|
||||
var body []byte
|
||||
if !tc.isDecodeError {
|
||||
b, err := json.Marshal(tc.reqBody)
|
||||
assert.NoError(t, err)
|
||||
body = b
|
||||
} else {
|
||||
b := make([]byte, 0)
|
||||
body = b
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, reqPath, bytes.NewBuffer(body))
|
||||
w := mock_http.NewRecorder(tc.writeErr)
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.Register(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var authToken map[string]string
|
||||
|
||||
if tc.expectedStatusCode == http.StatusCreated && tc.writeErr == nil {
|
||||
err = json.Unmarshal(b, &authToken)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedBody, authToken)
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_ResendEmailVerification(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.ResendEmailVerification(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_EmailVerification(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.EmailVerification(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_Login(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.Login(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_Get(t *testing.T) {
|
||||
type logBehavior func(l *mock_logger.MockLog, path string, err error)
|
||||
testTable := []struct {
|
||||
name string
|
||||
ctxUser domain.User
|
||||
addingCtxUser bool
|
||||
logBehavior logBehavior
|
||||
logErr error
|
||||
writeErr error
|
||||
expectedUser domain.User
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
ctxUser: domain.User{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
addingCtxUser: true,
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "incorrect_user_in_ctx",
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Errorf("[%s] extracting user from ctx", path)
|
||||
},
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "response_writing_error",
|
||||
ctxUser: domain.User{
|
||||
ID: 1,
|
||||
Username: "urec",
|
||||
Email: "mail@mail.ru",
|
||||
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)},
|
||||
},
|
||||
addingCtxUser: true,
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Errorf("[%s] writing response: %s", path, err.Error())
|
||||
},
|
||||
logErr: domain.AnyError,
|
||||
writeErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
reqPath := "/users/me"
|
||||
log := mock_logger.NewMockLog(c)
|
||||
tc.logBehavior(log, reqPath, tc.logErr)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, reqPath, nil)
|
||||
w := mock_http.NewRecorder(tc.writeErr)
|
||||
ctx := req.Context()
|
||||
if tc.addingCtxUser {
|
||||
ctx = context.WithValue(ctx, "user", tc.ctxUser)
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
h := &Handler{l: log}
|
||||
|
||||
h.Get(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var u domain.User
|
||||
|
||||
if tc.expectedStatusCode == http.StatusOK {
|
||||
err = json.Unmarshal(b, &u)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_GetAvatarsHistory(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.GetAvatarsHistory(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_SendConfirmationCode(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.SendConfirmationCode(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
||||
|
||||
func TestHandler_ChangeUserData(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
req := httptest.NewRequest("", "/chats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h := &Handler{serv: serv, l: log}
|
||||
|
||||
h.ChangeUserData(w, req)
|
||||
|
||||
_ = w.Result()
|
||||
}
|
80
internal/transport/rest/middleware/auth.go
Normal file
80
internal/transport/rest/middleware/auth.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
)
|
||||
|
||||
// Auth достаёт jwt токен из хедера `Authorization` по схеме `Bearer` и кидает в контекст юзера
|
||||
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())
|
||||
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())
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := m.serv.Get(userID)
|
||||
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())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "user", user)
|
||||
r = r.WithContext(ctx)
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// VerificatedAuth достаёт jwt токен из хедера `Authorization` по схеме `Bearer` и кидает в контекст верифицированного юзера
|
||||
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())
|
||||
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())
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
user, err := m.serv.GetVerificated(userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, domain.UserNotFoundError) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
} else if errors.Is(err, domain.UnverifiedUserError) {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
return
|
||||
}
|
||||
m.l.Infof("[%s] error resolving user: %s", r.URL.Path, err.Error())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "user", user)
|
||||
r = r.WithContext(ctx)
|
||||
next(w, r)
|
||||
}
|
||||
}
|
359
internal/transport/rest/middleware/auth_test.go
Normal file
359
internal/transport/rest/middleware/auth_test.go
Normal file
|
@ -0,0 +1,359 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/internal/domain"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks"
|
||||
)
|
||||
|
||||
func TestMiddleware_Auth(t *testing.T) {
|
||||
type extractBehavior func(s *mock_service.MockServ, r *http.Request, token string, err error)
|
||||
type decodeBehavior func(s *mock_service.MockServ, token string, userID int, err error)
|
||||
type getBehavior func(s *mock_service.MockServ, userID int, user domain.User, err error)
|
||||
type logBehavior func(l *mock_logger.MockLog, path string, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
extractBehavior extractBehavior
|
||||
decodeBehavior decodeBehavior
|
||||
getBehavior getBehavior
|
||||
logBehavior logBehavior
|
||||
reqToken string
|
||||
extractToken string
|
||||
extractErr error
|
||||
decodeUserID int
|
||||
decodeErr error
|
||||
getUser domain.User
|
||||
getErr error
|
||||
logErr error
|
||||
expectedUser domain.User
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
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)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
reqToken: "Bearer token",
|
||||
extractToken: "token",
|
||||
decodeUserID: 1,
|
||||
getUser: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
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)},
|
||||
},
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
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)},
|
||||
},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "user_not_found",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
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)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
reqToken: "Bearer token",
|
||||
extractToken: "token",
|
||||
getErr: domain.UserNotFoundError,
|
||||
expectedStatusCode: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "extract_error",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] error extracting token: %s", path, err.Error())
|
||||
},
|
||||
extractErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
name: "decode_error",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
s.EXPECT().DecodeAuthToken(token).Return(userID, err)
|
||||
},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] error decoding token: %s", path, err.Error())
|
||||
},
|
||||
decodeErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
name: "get_error",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
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)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] error resolving user: %s", path, err.Error())
|
||||
},
|
||||
getErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
reqPath := "/"
|
||||
req := httptest.NewRequest(http.MethodGet, reqPath, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req.Header.Set("Authorization", tc.reqToken)
|
||||
|
||||
tc.extractBehavior(serv, req, tc.extractToken, tc.extractErr)
|
||||
tc.decodeBehavior(serv, tc.extractToken, tc.decodeUserID, tc.decodeErr)
|
||||
tc.getBehavior(serv, tc.decodeUserID, tc.getUser, tc.getErr)
|
||||
tc.logBehavior(log, reqPath, tc.logErr)
|
||||
|
||||
m := &Middleware{serv: serv, l: log}
|
||||
|
||||
server := m.Auth(func(w http.ResponseWriter, r *http.Request) {
|
||||
u := r.Context().Value("user")
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
})
|
||||
server.ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMiddleware_VerificatedAuth(t *testing.T) {
|
||||
type extractBehavior func(s *mock_service.MockServ, r *http.Request, token string, err error)
|
||||
type decodeBehavior func(s *mock_service.MockServ, token string, userID int, err error)
|
||||
type getBehavior func(s *mock_service.MockServ, userID int, user domain.User, err error)
|
||||
type logBehavior func(l *mock_logger.MockLog, path string, err error)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
extractBehavior extractBehavior
|
||||
decodeBehavior decodeBehavior
|
||||
getBehavior getBehavior
|
||||
logBehavior logBehavior
|
||||
reqToken string
|
||||
extractToken string
|
||||
extractErr error
|
||||
decodeUserID int
|
||||
decodeErr error
|
||||
getUser domain.User
|
||||
getErr error
|
||||
logErr error
|
||||
expectedUser domain.User
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
s.EXPECT().DecodeAuthToken(token).Return(userID, err)
|
||||
},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {
|
||||
s.EXPECT().GetVerificated(userID).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
reqToken: "Bearer token",
|
||||
extractToken: "token",
|
||||
decodeUserID: 1,
|
||||
getUser: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
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)},
|
||||
},
|
||||
expectedUser: domain.User{
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
Username: "urec",
|
||||
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)},
|
||||
},
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "unverified_user",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
s.EXPECT().DecodeAuthToken(token).Return(userID, err)
|
||||
},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {
|
||||
s.EXPECT().GetVerificated(userID).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
reqToken: "Bearer token",
|
||||
extractToken: "token",
|
||||
getErr: domain.UnverifiedUserError,
|
||||
expectedStatusCode: http.StatusConflict,
|
||||
},
|
||||
{
|
||||
name: "user_not_found",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
s.EXPECT().DecodeAuthToken(token).Return(userID, err)
|
||||
},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {
|
||||
s.EXPECT().GetVerificated(userID).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {},
|
||||
reqToken: "Bearer token",
|
||||
extractToken: "token",
|
||||
getErr: domain.UserNotFoundError,
|
||||
expectedStatusCode: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "extract_error",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] error extracting token: %s", path, err.Error())
|
||||
},
|
||||
extractErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
name: "decode_error",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
s.EXPECT().DecodeAuthToken(token).Return(userID, err)
|
||||
},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] error decoding token: %s", path, err.Error())
|
||||
},
|
||||
decodeErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
name: "get_error",
|
||||
extractBehavior: func(s *mock_service.MockServ, r *http.Request, token string, err error) {
|
||||
s.EXPECT().ExtractAuthToken(r).Return(token, err)
|
||||
},
|
||||
decodeBehavior: func(s *mock_service.MockServ, token string, userID int, err error) {
|
||||
s.EXPECT().DecodeAuthToken(token).Return(userID, err)
|
||||
},
|
||||
getBehavior: func(s *mock_service.MockServ, userID int, user domain.User, err error) {
|
||||
s.EXPECT().GetVerificated(userID).Return(user, err)
|
||||
},
|
||||
logBehavior: func(l *mock_logger.MockLog, path string, err error) {
|
||||
l.EXPECT().Infof("[%s] error resolving user: %s", path, err.Error())
|
||||
},
|
||||
getErr: domain.AnyError,
|
||||
logErr: domain.AnyError,
|
||||
expectedStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
serv := mock_service.NewMockServ(c)
|
||||
|
||||
reqPath := "/"
|
||||
req := httptest.NewRequest(http.MethodGet, reqPath, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req.Header.Set("Authorization", tc.reqToken)
|
||||
|
||||
tc.extractBehavior(serv, req, tc.extractToken, tc.extractErr)
|
||||
tc.decodeBehavior(serv, tc.extractToken, tc.decodeUserID, tc.decodeErr)
|
||||
tc.getBehavior(serv, tc.decodeUserID, tc.getUser, tc.getErr)
|
||||
tc.logBehavior(log, reqPath, tc.logErr)
|
||||
|
||||
m := &Middleware{serv: serv, l: log}
|
||||
|
||||
server := m.VerificatedAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||
u := r.Context().Value("user")
|
||||
|
||||
assert.Equal(t, tc.expectedUser, u)
|
||||
})
|
||||
server.ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
12
internal/transport/rest/middleware/log.go
Normal file
12
internal/transport/rest/middleware/log.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (m *Middleware) Log(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
m.l.Infof("[%s] \"%s %s\"", r.Context().Value(RequestIDKey), r.Method, r.URL.Path)
|
||||
next(w, r)
|
||||
}
|
||||
}
|
72
internal/transport/rest/middleware/log_test.go
Normal file
72
internal/transport/rest/middleware/log_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.uber.org/mock/gomock"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
)
|
||||
|
||||
func TestMiddleware_Log(t *testing.T) {
|
||||
type logBehavior func(l *mock_logger.MockLog, requestID any, method, url string)
|
||||
|
||||
testTable := []struct {
|
||||
name string
|
||||
requestMethod string
|
||||
requestUrl string
|
||||
requestID any
|
||||
logBehavior logBehavior
|
||||
}{
|
||||
{
|
||||
name: "ok_1",
|
||||
requestMethod: http.MethodGet,
|
||||
requestUrl: "/users",
|
||||
requestID: 1,
|
||||
logBehavior: func(l *mock_logger.MockLog, requestID any, method, url string) {
|
||||
l.EXPECT().Infof("[%s] \"%s %s\"", requestID, method, url)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok_2",
|
||||
requestMethod: http.MethodPost,
|
||||
requestUrl: "/chat",
|
||||
requestID: 10031,
|
||||
logBehavior: func(l *mock_logger.MockLog, requestID any, method, url string) {
|
||||
l.EXPECT().Infof("[%s] \"%s %s\"", requestID, method, url)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok_3",
|
||||
requestMethod: http.MethodPost,
|
||||
requestUrl: "/chat",
|
||||
logBehavior: func(l *mock_logger.MockLog, requestID any, method, url string) {
|
||||
l.EXPECT().Infof("[%s] \"%s %s\"", requestID, method, url)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
log := mock_logger.NewMockLog(c)
|
||||
tc.logBehavior(log, tc.requestID, tc.requestMethod, tc.requestUrl)
|
||||
|
||||
req := httptest.NewRequest(tc.requestMethod, tc.requestUrl, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ctx := req.Context()
|
||||
ctx = context.WithValue(ctx, RequestIDKey, tc.requestID)
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
m := &Middleware{l: log}
|
||||
|
||||
server := m.Log(func(w http.ResponseWriter, r *http.Request) {})
|
||||
server.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
}
|
18
internal/transport/rest/middleware/middleware.go
Normal file
18
internal/transport/rest/middleware/middleware.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type Middleware struct {
|
||||
serv service.Serv
|
||||
l logger.Log
|
||||
cfg config.Server
|
||||
reqID uint64 // 0
|
||||
}
|
||||
|
||||
func NewMiddleware(serv service.Serv, l logger.Log, cfg config.Server) *Middleware {
|
||||
return &Middleware{serv: serv, l: l, cfg: cfg}
|
||||
}
|
23
internal/transport/rest/middleware/middleware_test.go
Normal file
23
internal/transport/rest/middleware/middleware_test.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"testing"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/config"
|
||||
mock_logger "git.urec56.ru/urec/chat_back_go/internal/logger/mocks"
|
||||
mock_service "git.urec56.ru/urec/chat_back_go/internal/service/mocks"
|
||||
)
|
||||
|
||||
func Test_NewMiddleware(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
serv := mock_service.NewMockServ(c)
|
||||
log := mock_logger.NewMockLog(c)
|
||||
cfg := config.Server{}
|
||||
|
||||
m := NewMiddleware(serv, log, cfg)
|
||||
assert.Equal(t, &Middleware{serv: serv, l: log, cfg: cfg}, m)
|
||||
}
|
26
internal/transport/rest/middleware/request_id.go
Normal file
26
internal/transport/rest/middleware/request_id.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type ctxKeyRequestID int
|
||||
|
||||
const RequestIDKey ctxKeyRequestID = 0
|
||||
|
||||
func (m *Middleware) RequestID(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
requestID := r.Header.Get(m.cfg.RequestIDHeader)
|
||||
if requestID == "" {
|
||||
myID := atomic.AddUint64(&m.reqID, 1)
|
||||
requestID = fmt.Sprintf("%06d", myID)
|
||||
}
|
||||
ctx = context.WithValue(ctx, RequestIDKey, requestID)
|
||||
r = r.WithContext(ctx)
|
||||
next(w, r)
|
||||
}
|
||||
}
|
57
internal/transport/rest/middleware/request_id_test.go
Normal file
57
internal/transport/rest/middleware/request_id_test.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.urec56.ru/urec/chat_back_go/config"
|
||||
)
|
||||
|
||||
func TestMiddleware_RequestID(t *testing.T) {
|
||||
testTable := []struct {
|
||||
name string
|
||||
cfgRequestIDHeader string
|
||||
requestIDHeader string
|
||||
requestID string
|
||||
requestIDInHeader string
|
||||
expectedRequestID string
|
||||
}{
|
||||
{
|
||||
name: "id_in_header",
|
||||
cfgRequestIDHeader: "X-Request-Id",
|
||||
requestIDHeader: "X-Request-Id",
|
||||
requestIDInHeader: "023945",
|
||||
expectedRequestID: "023945",
|
||||
},
|
||||
{
|
||||
name: "no_id_in_request_header",
|
||||
expectedRequestID: "000001",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testTable {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
req.Header.Set(tc.requestIDHeader, tc.requestIDInHeader)
|
||||
|
||||
cfg := config.Server{RequestIDHeader: tc.cfgRequestIDHeader}
|
||||
|
||||
m := &Middleware{cfg: cfg}
|
||||
|
||||
server := m.RequestID(func(w http.ResponseWriter, r *http.Request) {
|
||||
reqID := r.Context().Value(RequestIDKey)
|
||||
|
||||
assert.Equal(t, tc.expectedRequestID, reqID)
|
||||
})
|
||||
server.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
}
|
109
internal/transport/rest/server.go
Normal file
109
internal/transport/rest/server.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"expvar"
|
||||
"github.com/urec56/pathparams"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/transport/rest/handler"
|
||||
"git.urec56.ru/urec/chat_back_go/internal/transport/rest/middleware"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
httpServer *http.Server
|
||||
m *middleware.Middleware
|
||||
h *handler.Handler
|
||||
}
|
||||
|
||||
func NewServer(serv service.Serv, l logger.Log, 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 {
|
||||
r := s.getRouter()
|
||||
|
||||
s.httpServer = &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
return s.httpServer.ListenAndServe()
|
||||
}
|
||||
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
return s.httpServer.Shutdown(ctx)
|
||||
}
|
||||
|
||||
func (s *Server) getRouter() http.Handler {
|
||||
pathparams.SetConfig("", pathparams.DefaultValidationErrorStatusCode)
|
||||
ro := pathparams.NewRouter()
|
||||
r := ro.PathPrefix("/api")
|
||||
|
||||
r.Use(s.m.RequestID)
|
||||
r.Use(s.m.Log)
|
||||
|
||||
s.initUsersRouter(r)
|
||||
s.initChatRouter(r)
|
||||
s.initImagesRouter(r)
|
||||
s.initDebug(r)
|
||||
|
||||
return ro
|
||||
}
|
||||
|
||||
func (s *Server) initUsersRouter(r *pathparams.Router) {
|
||||
ur := r.PathPrefix("/users")
|
||||
aur := r.PathPrefix("/users")
|
||||
|
||||
aur.Use(s.m.Auth)
|
||||
|
||||
ur.Get("", s.h.GetUsers)
|
||||
ur.Post("/check_existing_user", s.h.CheckExisting)
|
||||
ur.Post("/check_existing_password", s.h.CheckExistingPassword)
|
||||
ur.Post("/login", s.h.Login)
|
||||
ur.Post("/register", s.h.Register)
|
||||
|
||||
aur.Post("/resend_email_verification", s.h.ResendEmailVerification)
|
||||
aur.Post("/email_verification", s.h.EmailVerification)
|
||||
aur.Get("/me", s.h.Get)
|
||||
aur.Get("/avatars", s.h.GetAvatarsHistory)
|
||||
aur.Post("/send_confirmation_code", s.h.SendConfirmationCode)
|
||||
aur.Post("/change_data", s.h.ChangeUserData)
|
||||
}
|
||||
|
||||
func (s *Server) initChatRouter(r *pathparams.Router) {
|
||||
chr := r.PathPrefix("/chat")
|
||||
|
||||
chr.Use(s.m.VerificatedAuth)
|
||||
|
||||
chr.Get("", s.h.GetChats)
|
||||
chr.Post("/create_chat", s.h.Create)
|
||||
chr.Post("/change_data", s.h.ChangeChatData)
|
||||
chr.Get("/get_last_message/{chat_id}", s.h.GetLastMessage)
|
||||
chr.Get("/get_some_messages/{chat_id}", s.h.GetSomeMessages)
|
||||
chr.Get("/message/{chat_id}", s.h.GetMessageByID)
|
||||
chr.Get("/create_invitation_link", s.h.CreateInvitationLink)
|
||||
chr.Get("/invite_to_chat/{invitation_token}", s.h.InviteToChat)
|
||||
chr.Delete("/delete_chat/{chat_id}", s.h.DeleteChat)
|
||||
chr.Delete("/delete_user_from_chat/{chat_id}", s.h.DeleteUserFromChat)
|
||||
chr.Post("/pin_chat", s.h.PinChat)
|
||||
chr.Delete("/unpin_chat", s.h.UnpinnChat)
|
||||
chr.Get("/get_pinned_chats", s.h.GetPinnedChats)
|
||||
chr.Get("/pinned_messages/{chat_id}", s.h.GetPinnedMessages)
|
||||
}
|
||||
|
||||
func (s *Server) initImagesRouter(r *pathparams.Router) {
|
||||
ir := r.PathPrefix("/images")
|
||||
|
||||
ir.Use(s.m.VerificatedAuth)
|
||||
|
||||
ir.Post("/upload_avatar", s.h.UploadAvatar)
|
||||
ir.Post("/upload_image", s.h.UploadImage)
|
||||
}
|
||||
|
||||
func (s *Server) initDebug(r *pathparams.Router) {
|
||||
r.Get("/debug/vars", expvar.Handler().(http.HandlerFunc))
|
||||
}
|
7
migrations/000001_database_creation.down.sql
Normal file
7
migrations/000001_database_creation.down.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
DROP TABLE user_chat;
|
||||
DROP TABLE user_avatar;
|
||||
DROP TABLE pinned_message;
|
||||
DROP TABLE pinned_chat;
|
||||
DROP TABLE chat;
|
||||
DROP TABLE users;
|
||||
|
64
migrations/000001_database_creation.up.sql
Normal file
64
migrations/000001_database_creation.up.sql
Normal file
|
@ -0,0 +1,64 @@
|
|||
CREATE TABLE users(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
email VARCHAR NOT NULL UNIQUE,
|
||||
username VARCHAR NOT NULL UNIQUE,
|
||||
hashed_password VARCHAR NOT NULL,
|
||||
role INT NOT NULL DEFAULT 0,
|
||||
black_phoenix BOOL NOT NULL DEFAULT FALSE,
|
||||
avatar_image VARCHAR NOT NULL DEFAULT 'https://images.black-phoenix.ru/static/images/%D1%82%D1%8B%20%D1%83%D0%B6%D0%B5%20%D0%BF%D0%B5%D1%88%D0%BA%D0%B0%20BP.png',
|
||||
date_of_birth DATE NOT NULL,
|
||||
date_of_registration DATE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE chat(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_by BIGINT NOT NULL,
|
||||
chat_for BIGINT NOT NULL,
|
||||
chat_name VARCHAR NOT NULL,
|
||||
avatar_image VARCHAR NOT NULL,
|
||||
visibility BOOL NOT NULL DEFAULT TRUE,
|
||||
FOREIGN KEY (created_by)
|
||||
REFERENCES users (id),
|
||||
FOREIGN KEY (chat_for)
|
||||
REFERENCES users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE pinned_chat(
|
||||
user_id BIGINT,
|
||||
chat_id BIGINT,
|
||||
PRIMARY KEY (user_id, chat_id),
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES users (id),
|
||||
FOREIGN KEY (chat_id)
|
||||
REFERENCES chat (id)
|
||||
);
|
||||
|
||||
CREATE TABLE pinned_message(
|
||||
chat_id BIGINT NOT NULL,
|
||||
message_id UUID,
|
||||
user_id BIGINT,
|
||||
PRIMARY KEY (user_id, message_id),
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES users (id),
|
||||
FOREIGN KEY (chat_id)
|
||||
REFERENCES chat (id)
|
||||
);
|
||||
|
||||
CREATE TABLE user_avatar(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
avatar_image VARCHAR NOT NULL,
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE user_chat(
|
||||
user_id BIGINT,
|
||||
chat_id BIGINT,
|
||||
PRIMARY KEY (user_id, chat_id),
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES users (id),
|
||||
FOREIGN KEY (chat_id)
|
||||
REFERENCES chat (id)
|
||||
);
|
||||
|
Loading…
Add table
Reference in a new issue