From 92a17c06c31c13a232fcd3f1025f5f635f6b8b19 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 10:18:29 +0300 Subject: [PATCH 01/11] sync --- cmd/metricstest/fixtures_test.go | 141 +++++++++++++++++++++++++++++ cmd/metricstest/flags.go | 6 +- cmd/metricstest/iteration1_test.go | 21 +++++ cmd/metricstest/main_test.go | 9 +- go.mod | 1 + go.sum | 2 + 6 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 cmd/metricstest/fixtures_test.go diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go new file mode 100644 index 0000000..d74edaa --- /dev/null +++ b/cmd/metricstest/fixtures_test.go @@ -0,0 +1,141 @@ +package main + +import ( + "fmt" + "net" + "os" + "strconv" + "testing" + "time" + + "github.com/Yandex-Practicum/go-autotests/internal/fork" + "github.com/go-resty/resty/v2" + "github.com/rekby/fixenv" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +const ( + startProcessTimeout = time.Second * 10 + checkPortInterval = time.Millisecond * 100 +) + +type Env struct { + fixenv.EnvT + assert.Assertions + Ctx context.Context + + t testing.TB +} + +func New(t testing.TB) *Env { + ctx, ctxCancel := context.WithCancel(context.Background()) + t.Cleanup(ctxCancel) + + res := Env{ + EnvT: *fixenv.NewEnv(t), + Assertions: *assert.New(t), + t: t, + Ctx: ctx, + } + return &res +} + +func (e *Env) Fatalf(format string, args ...any) { + e.T().Fatalf(format, args...) +} + +func (e *Env) Logf(format string, args ...any) { + e.t.Logf(format, args...) +} + +func + +func ExistPath(e *Env, filePath string) string { + return fixenv.Cache(&e.EnvT, filePath, &fixenv.FixtureOptions{ + Scope: fixenv.ScopePackage, + }, func() (string, error) { + e.Logf("Проверяю наличие файла: %q", filePath) + _, err := os.Stat(filePath) + if err != nil { + return "", err + } + return filePath, nil + }) +} + +func AgentPath(e *Env) string { + return ExistPath(e, flagAgentBinaryPath) +} + +func ServerFilePath(e *Env) string { + return ExistPath(e, flagServerBinaryPath) +} + +func ServerAddress(e *Env) string { + return fmt.Sprintf("http://%v:%v", ServerHost(e), ServerPort(e)) +} + +func ServerHost(e *Env) string { + return flagServerHost +} + +func ServerPort(e *Env) int { + return flagServerPort +} + +func StartProcess(e *Env, name string, command string, args ...string) *fork.BackgroundProcess { + cacheKey := append([]string{name, command}, args...) + return fixenv.CacheWithCleanup(e, cacheKey, nil, func() (*fork.BackgroundProcess, fixenv.FixtureCleanupFunc, error) { + res := fork.NewBackgroundProcess(e.Ctx, command, fork.WithArgs(args...)) + + e.Logf("Запускаю %q: %q %#v", name, command, args) + err := res.Start(e.Ctx) + if err != nil { + return nil, nil, err + } + + cleanup := func() { + e.Logf("Останавливаю %q: %q %#v", name, command, args) + exitCode, err := res.Stop() + if err != nil { + e.Fatalf("Не получилось остановить процесс: %+v", err) + } + if exitCode != 0 { + e.Logf("Ненулевой код возврата: %v", exitCode) + } + } + return res, cleanup, nil + }) +} + +func StartProcessWhichListenPort(e *Env, host string, port int, name string, command string, args ...string) *fork.BackgroundProcess { + cacheKey := append([]string{host, strconv.Itoa(port), name, command}, args...) + return fixenv.Cache[*fork.BackgroundProcess](e, cacheKey, nil, func() (*fork.BackgroundProcess, error) { + process := StartProcess(e, name, command, args...) + ctx, cancel := context.WithTimeout(e.Ctx, startProcessTimeout) + defer cancel() + + address := fmt.Sprintf("%v:%v", host, port) + dialer := net.Dialer{} + for { + time.Sleep(checkPortInterval) + e.Logf("Пробую подключиться на %q...", address) + conn, err := dialer.DialContext(ctx, "tcp", address) + if err == nil { + e.Logf("Закрываю успешное подключение") + err = conn.Close() + return process, err + } + if ctx.Err() != nil { + return nil, err + } + } + }) +} + +func RestyClient(e *Env, host string) *resty.Client { + return fixenv.Cache[*resty.Client](e, host, nil, func() (*resty.Client, error) { + return resty.New().SetHostURL(host).SetRedirectPolicy(resty.NoRedirectPolicy()), nil + }) +} diff --git a/cmd/metricstest/flags.go b/cmd/metricstest/flags.go index 41720da..ec57656 100644 --- a/cmd/metricstest/flags.go +++ b/cmd/metricstest/flags.go @@ -9,7 +9,7 @@ var ( flagServerBinaryPath string flagTargetSourcePath string flagServerHost string - flagServerPort string + flagServerPort int flagServerBaseURL string flagFileStoragePath string flagDatabaseDSN string @@ -20,8 +20,8 @@ func init() { flag.StringVar(&flagAgentBinaryPath, "agent-binary-path", "", "path to target agent binary") flag.StringVar(&flagServerBinaryPath, "binary-path", "", "path to target server binary") flag.StringVar(&flagTargetSourcePath, "source-path", "", "path to target server source") - flag.StringVar(&flagServerHost, "server-host", "", "host of target address") - flag.StringVar(&flagServerPort, "server-port", "", "port of target address") + flag.StringVar(&flagServerHost, "server-host", "localhost", "host of target address") + flag.IntVar(&flagServerPort, "server-port", 8080, "port of target address") flag.StringVar(&flagServerBaseURL, "server-base-url", "", "base URL of target address") flag.StringVar(&flagFileStoragePath, "file-storage-path", "", "path to persistent file storage") flag.StringVar(&flagDatabaseDSN, "database-dsn", "", "connection string to database") diff --git a/cmd/metricstest/iteration1_test.go b/cmd/metricstest/iteration1_test.go index 7f8b6ab..6f2c409 100644 --- a/cmd/metricstest/iteration1_test.go +++ b/cmd/metricstest/iteration1_test.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "syscall" + "testing" "time" "github.com/go-resty/resty/v2" @@ -14,6 +15,26 @@ import ( "github.com/Yandex-Practicum/go-autotests/internal/fork" ) +func TestIteration1(t *testing.T) { + t.Run("Server", func(t *testing.T) { + t.Run("TestCounterHandlers", func(t *testing.T) { + t.Run("update", func(t *testing.T) { + e := New(t) + c := Client1(e) + req := c.R() + resp, err := req.Post("update/gauge/testGauge/100") + e.NoError(err, "Ошибка при выполнении запроса") + + }) + }) + }) +} + +func Client1(e *Env) *resty.Client { + StartProcessWhichListenPort(e, ServerHost(e), ServerPort(e), "metric server", ServerFilePath(e)) + return RestyClient(e, ServerAddress(e)) +} + type Iteration1Suite struct { suite.Suite diff --git a/cmd/metricstest/main_test.go b/cmd/metricstest/main_test.go index d79ade2..b94bbb5 100644 --- a/cmd/metricstest/main_test.go +++ b/cmd/metricstest/main_test.go @@ -3,18 +3,19 @@ package main //go:generate go test -c -o=../../bin/metricstest import ( + "flag" "os" "testing" + "github.com/rekby/fixenv" "github.com/stretchr/testify/suite" ) func TestMain(m *testing.M) { - os.Exit(m.Run()) -} + flag.Parse() -func TestIteration1(t *testing.T) { - suite.Run(t, new(Iteration1Suite)) + fixenv.CreateMainTestEnv(nil) + os.Exit(m.Run()) } func TestIteration2A(t *testing.T) { diff --git a/go.mod b/go.mod index 1f21f8f..14ddfd4 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/lib/pq v1.10.7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rekby/fixenv v0.3.2 // indirect github.com/shopspring/decimal v1.3.1 // indirect golang.org/x/crypto v0.6.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect diff --git a/go.sum b/go.sum index ab858e3..9689c48 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rekby/fixenv v0.3.2 h1:6AOdQ9Boaa/lOQJTY8GDmQRIhg3S3SD0mIEPkuDSkoQ= +github.com/rekby/fixenv v0.3.2/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From ec5da7437d30c711248208ba6a3a4cd1635fe7d8 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 12:31:29 +0300 Subject: [PATCH 02/11] test server for first iteration --- .vscode/launch.json | 21 ++ cmd/metricstest/fixtures_test.go | 70 +++++-- cmd/metricstest/flags.go | 14 +- cmd/metricstest/helpers_test.go | 3 + cmd/metricstest/iteration1_test.go | 314 ++++++++--------------------- 5 files changed, 170 insertions(+), 252 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 cmd/metricstest/helpers_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..aa26e91 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "test server", + "type":"go", + "request": "launch", + "mode": "test", + "program": "/Users/rekby/projects/praktikum/go-autotests/cmd/metricstest/iteration1_test.go", + "args": [ + // "-test.v", + "-test.run=^TestIteration1$", + "--binary-path=/Users/rekby/projects/rekby/mymetrics/cmd/server/server", + "--server-port=8080" + ] + } + ] +} \ No newline at end of file diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index d74edaa..d8f0dc5 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -4,7 +4,9 @@ import ( "fmt" "net" "os" + "path/filepath" "strconv" + "syscall" "testing" "time" @@ -12,18 +14,20 @@ import ( "github.com/go-resty/resty/v2" "github.com/rekby/fixenv" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/net/context" ) const ( startProcessTimeout = time.Second * 10 - checkPortInterval = time.Millisecond * 100 + checkPortInterval = time.Millisecond * 10 ) type Env struct { fixenv.EnvT assert.Assertions - Ctx context.Context + Ctx context.Context + Require require.Assertions t testing.TB } @@ -35,6 +39,7 @@ func New(t testing.TB) *Env { res := Env{ EnvT: *fixenv.NewEnv(t), Assertions: *assert.New(t), + Require: *require.New(t), t: t, Ctx: ctx, } @@ -42,21 +47,26 @@ func New(t testing.TB) *Env { } func (e *Env) Fatalf(format string, args ...any) { + e.t.Helper() e.T().Fatalf(format, args...) } func (e *Env) Logf(format string, args ...any) { + e.t.Helper() e.t.Logf(format, args...) } -func - func ExistPath(e *Env, filePath string) string { return fixenv.Cache(&e.EnvT, filePath, &fixenv.FixtureOptions{ Scope: fixenv.ScopePackage, }, func() (string, error) { - e.Logf("Проверяю наличие файла: %q", filePath) - _, err := os.Stat(filePath) + absFilePath, err := filepath.Abs(filePath) + if err != nil { + return "", err + } + e.Logf("Проверяю наличие файла: %q (%q)", absFilePath, filePath) + + _, err = os.Stat(filePath) if err != nil { return "", err } @@ -81,7 +91,9 @@ func ServerHost(e *Env) string { } func ServerPort(e *Env) int { - return flagServerPort + return fixenv.Cache(e, nil, nil, func() (int, error) { + return strconv.Atoi(flagServerPort) + }) } func StartProcess(e *Env, name string, command string, args ...string) *fork.BackgroundProcess { @@ -97,11 +109,18 @@ func StartProcess(e *Env, name string, command string, args ...string) *fork.Bac cleanup := func() { e.Logf("Останавливаю %q: %q %#v", name, command, args) - exitCode, err := res.Stop() - if err != nil { - e.Fatalf("Не получилось остановить процесс: %+v", err) + exitCode, stopErr := res.Stop(syscall.SIGINT, syscall.SIGKILL) + + stdOut := string(res.Stdout(context.Background())) + stdErr := string(res.Stderr(context.Background())) + + e.Logf("stdout:\n%v", stdOut) + e.Logf("stderr:\n%v", stdErr) + + if stopErr != nil { + e.Fatalf("Не получилось остановить процесс: %+v", stopErr) } - if exitCode != 0 { + if exitCode > 0 { e.Logf("Ненулевой код возврата: %v", exitCode) } } @@ -118,9 +137,10 @@ func StartProcessWhichListenPort(e *Env, host string, port int, name string, com address := fmt.Sprintf("%v:%v", host, port) dialer := net.Dialer{} + e.Logf("Пробую подключиться на %q...", address) + for { time.Sleep(checkPortInterval) - e.Logf("Пробую подключиться на %q...", address) conn, err := dialer.DialContext(ctx, "tcp", address) if err == nil { e.Logf("Закрываю успешное подключение") @@ -128,6 +148,7 @@ func StartProcessWhichListenPort(e *Env, host string, port int, name string, com return process, err } if ctx.Err() != nil { + e.Fatalf("Ошибка подлючения: %+v", err) return nil, err } } @@ -135,7 +156,28 @@ func StartProcessWhichListenPort(e *Env, host string, port int, name string, com } func RestyClient(e *Env, host string) *resty.Client { - return fixenv.Cache[*resty.Client](e, host, nil, func() (*resty.Client, error) { - return resty.New().SetHostURL(host).SetRedirectPolicy(resty.NoRedirectPolicy()), nil + return fixenv.Cache(e, host, nil, func() (*resty.Client, error) { + return resty. + New(). + SetDebug(true). + SetBaseURL(host). + SetRedirectPolicy(resty.NoRedirectPolicy()). + SetLogger(restyLogger{e}), nil }) } + +type restyLogger struct { + e *Env +} + +func (l restyLogger) Errorf(format string, v ...interface{}) { + l.e.Logf("RESTY ERROR: "+format, v...) +} + +func (l restyLogger) Warnf(format string, v ...interface{}) { + l.e.Logf("resty warn: "+format, v...) +} + +func (l restyLogger) Debugf(format string, v ...interface{}) { + l.e.Logf("resty: "+format, v...) +} diff --git a/cmd/metricstest/flags.go b/cmd/metricstest/flags.go index ec57656..9adf824 100644 --- a/cmd/metricstest/flags.go +++ b/cmd/metricstest/flags.go @@ -2,6 +2,12 @@ package main import ( "flag" + "strconv" +) + +const ( + serverDefaultHost = "localhost" + serverDefaultPort = 8080 ) var ( @@ -9,7 +15,7 @@ var ( flagServerBinaryPath string flagTargetSourcePath string flagServerHost string - flagServerPort int + flagServerPort string flagServerBaseURL string flagFileStoragePath string flagDatabaseDSN string @@ -18,10 +24,10 @@ var ( func init() { flag.StringVar(&flagAgentBinaryPath, "agent-binary-path", "", "path to target agent binary") - flag.StringVar(&flagServerBinaryPath, "binary-path", "", "path to target server binary") + flag.StringVar(&flagServerBinaryPath, "binary-path", "cmd/server/server", "path to target server binary") flag.StringVar(&flagTargetSourcePath, "source-path", "", "path to target server source") - flag.StringVar(&flagServerHost, "server-host", "localhost", "host of target address") - flag.IntVar(&flagServerPort, "server-port", 8080, "port of target address") + flag.StringVar(&flagServerHost, "server-host", serverDefaultHost, "host of target address") + flag.StringVar(&flagServerPort, "server-port", strconv.Itoa(serverDefaultPort), "port of target address") flag.StringVar(&flagServerBaseURL, "server-base-url", "", "base URL of target address") flag.StringVar(&flagFileStoragePath, "file-storage-path", "", "path to persistent file storage") flag.StringVar(&flagDatabaseDSN, "database-dsn", "", "connection string to database") diff --git a/cmd/metricstest/helpers_test.go b/cmd/metricstest/helpers_test.go new file mode 100644 index 0000000..b870f19 --- /dev/null +++ b/cmd/metricstest/helpers_test.go @@ -0,0 +1,3 @@ +package main + +type StartedServerHost string diff --git a/cmd/metricstest/iteration1_test.go b/cmd/metricstest/iteration1_test.go index 6f2c409..00b5905 100644 --- a/cmd/metricstest/iteration1_test.go +++ b/cmd/metricstest/iteration1_test.go @@ -1,266 +1,112 @@ package main import ( - "context" - "errors" "net/http" - "os" - "syscall" "testing" - "time" "github.com/go-resty/resty/v2" - "github.com/stretchr/testify/suite" - - "github.com/Yandex-Practicum/go-autotests/internal/fork" ) func TestIteration1(t *testing.T) { - t.Run("Server", func(t *testing.T) { - t.Run("TestCounterHandlers", func(t *testing.T) { - t.Run("update", func(t *testing.T) { - e := New(t) - c := Client1(e) - req := c.R() - resp, err := req.Post("update/gauge/testGauge/100") - e.NoError(err, "Ошибка при выполнении запроса") - - }) + t.Run("TestCounterHandlers", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() + resp, err := req.Post("update/counter/testGauge/100") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен возвращать код 200 (http.StatusOK)") + + req = c.R() + resp, err = req.Post("update/counter/testGauge/101") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusOK, resp.StatusCode(), "При обновлении значения сервер должен возвращать код 200 (http.StatusOK)") }) - }) -} - -func Client1(e *Env) *resty.Client { - StartProcessWhichListenPort(e, ServerHost(e), ServerPort(e), "metric server", ServerFilePath(e)) - return RestyClient(e, ServerAddress(e)) -} - -type Iteration1Suite struct { - suite.Suite - - serverAddress string - serverProcess *fork.BackgroundProcess -} - -func (suite *Iteration1Suite) SetupSuite() { - // check required flags - suite.Require().NotEmpty(flagServerBinaryPath, "-binary-path non-empty flag required") - - suite.serverAddress = "http://localhost:8080" - - envs := append(os.Environ(), []string{ - "RESTORE=false", - }...) - p := fork.NewBackgroundProcess(context.Background(), flagServerBinaryPath, - fork.WithEnv(envs...), - ) - - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - - err := p.Start(ctx) - if err != nil { - suite.T().Errorf("Невозможно запустить процесс командой %s: %s. Переменные окружения: %+v", p, err, envs) - return - } - - port := "8080" - err = p.WaitPort(ctx, "tcp", port) - if err != nil { - suite.T().Errorf("Не удалось дождаться пока порт %s станет доступен для запроса: %s", port, err) - return - } - - suite.serverProcess = p -} - -func (suite *Iteration1Suite) TearDownSuite() { - if suite.serverProcess == nil { - return - } - - exitCode, err := suite.serverProcess.Stop(syscall.SIGINT, syscall.SIGKILL) - if err != nil { - if errors.Is(err, os.ErrProcessDone) { - return - } - suite.T().Logf("Не удалось остановить процесс с помощью сигнала ОС: %s", err) - return - } - - if exitCode > 0 { - suite.T().Logf("Процесс завершился с не нулевым статусом %d", exitCode) - } - - // try to read stdout/stderr - ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) - defer cancel() - - out := suite.serverProcess.Stderr(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDERR лог процесса:\n\n%s", string(out)) - } - out = suite.serverProcess.Stdout(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDOUT лог процесса:\n\n%s", string(out)) - } -} - -// TestHandlers проверяет -// сервер успешно стартует и открывет tcp порт 8080 на 127.0.0.1 -// обработку POST запросов вида: ?id=&value=&type= -// а так же негативкейсы, запросы в которых отсутствуют id, value и задан не корректный type -func (suite *Iteration1Suite) TestGaugeHandlers() { - // create HTTP client without redirects support - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked - }) - - httpc := resty.New(). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) - - suite.Run("update", func() { - req := httpc.R() - resp, err := req.Post("update/gauge/testGauge/100") - - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением gauge") - - validStatus := suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) - - suite.Run("without id", func() { - req := httpc.R() - resp, err := req.Post("update/gauge/") - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением gauge") + t.Run("without-id", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() - validStatus := suite.Assert().Equalf(http.StatusNotFound, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) - - suite.Run("invalid value", func() { - req := httpc.R() - resp, err := req.Post("update/gauge/testGauge/none") - - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением gauge") + resp, err := req.Post("update/counter/testGauge/") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Contains([]int{http.StatusBadRequest, http.StatusNotFound}, resp.StatusCode(), + "При попытке обновления значения без ID - сервер должен вернуть ошибку 400 или 404 (http.StatusBadRequest, http.StatusNotFound).") + }) - validStatus := suite.Assert().Equalf(http.StatusBadRequest, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) + t.Run("bad value", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) -} + resp, err := req.Post("update/gauge/testGauge/bad-value") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusBadRequest, resp.StatusCode(), "При получении неправильного значения сервер должен вернуть ошибку 400 (http.StatusBadRequest)") + }) -func (suite *Iteration1Suite) TestCounterHandlers() { - // create HTTP client without redirects support - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked }) - httpc := resty.New(). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) + t.Run("TestGaugeHandlers", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() + resp, err := req.Post("update/gauge/testGauge/100") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен возвращать код http.StatusOK (200)") + + req = c.R() + resp, err = req.Post("update/gauge/testGauge/101") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusOK, resp.StatusCode(), "При обновлении значения сервер должен возвращать код http.StatusOK (200)") + }) - suite.Run("update", func() { - req := httpc.R() - resp, err := req.Post("update/counter/testCounter/100") + t.Run("without-id", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением counter") + resp, err := req.Post("update/gauge/testGauge/") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Contains([]int{http.StatusBadRequest, http.StatusNotFound}, resp.StatusCode(), + "При попытке обновления значения без ID - сервер должен вернуть ошибку http.StatusBadRequest или http.StatusNotFound (400, 404).") + }) - validStatus := suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) + t.Run("bad value", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } + resp, err := req.Post("update/gauge/testGauge/bad-value") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusBadRequest, resp.StatusCode(), "При получении неправильного значения сервер должен вернуть ошибку http.StatusBadRequest (400)") + }) }) - suite.Run("without id", func() { - req := httpc.R() - resp, err := req.Post("update/counter/") + t.Run("unexpected path", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением counter") - - validStatus := suite.Assert().Equalf(http.StatusNotFound, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) + for _, path := range []string{"unknown-path", "unknown-path/gauge/testGauge/100"} { + resp, err := req.Post(path) + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Equal(http.StatusNotFound, resp.StatusCode(), "При получении запроса к неизвестному пути сервер должен возвращать ошибку http.StatusNotFound (404)") } }) - suite.Run("invalid value", func() { - req := httpc.R() - resp, err := req.Post("update/counter/testCounter/none") - - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением counter") - - validStatus := suite.Assert().Equalf(http.StatusBadRequest, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } + t.Run("unknown-metric-type", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + req := c.R() + resp, err := req.Post("update/unknown/testGauge/100") + e.Require.NoError(err, "Ошибка при выполнении запроса") + e.Contains([]int{http.StatusBadRequest, http.StatusNotFound}, resp.StatusCode(), + "При попытке обновления метрики неизвестного типа сервер должен вернуть ошибку http.StatusBadRequest или http.StatusNotFound (400, 404)") }) } -func (suite *Iteration1Suite) TestUnknownHandlers() { - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked - }) - - httpc := resty.New(). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) - - suite.Run("update invalid type", func() { - req := httpc.R() - resp, err := req.Post("update/unknown/testCounter/100") - - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с не корректным типом метрики") - - validStatus := suite.Assert().Equalf(http.StatusNotImplemented, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) - - suite.Run("update invalid method", func() { - req := httpc.R() - resp, err := req.Post("updater/counter/testCounter/100") - - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с не корректным типом метрики") - - validStatus := suite.Assert().Equalf(http.StatusNotFound, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) +func DefaultServer(e *Env) *resty.Client { + StartProcessWhichListenPort(e, serverDefaultHost, serverDefaultPort, "metric server", ServerFilePath(e)) + return RestyClient(e, ServerAddress(e)) } From ff8f9b70b5c9cd538948a8a755e328759ca51fcb Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 13:26:39 +0300 Subject: [PATCH 03/11] init iter2 --- .vscode/launch.json | 6 +- cmd/metricstest/fixtures_test.go | 13 ++++ cmd/metricstest/flags.go | 9 ++- cmd/metricstest/helper_server_test.go | 108 ++++++++++++++++++++++++++ cmd/metricstest/helpers_test.go | 3 - cmd/metricstest/iteration2a_test.go | 25 ++++++ cmd/metricstest/main_test.go | 7 +- go.mod | 28 ++++++- go.sum | 53 +++++++++++++ 9 files changed, 237 insertions(+), 15 deletions(-) create mode 100644 cmd/metricstest/helper_server_test.go delete mode 100644 cmd/metricstest/helpers_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json index aa26e91..591caf5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,8 +14,10 @@ // "-test.v", "-test.run=^TestIteration1$", "--binary-path=/Users/rekby/projects/rekby/mymetrics/cmd/server/server", - "--server-port=8080" + "--server-port=8080", + "--source-path=/Users/rekby/projects/rekby/mymetrics", + "--agent-binary-path=/Users/rekby/projects/rekby/mymetrics/cmd/agent/agent" ] } ] -} \ No newline at end of file +} diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index d8f0dc5..8bd784d 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -46,6 +46,11 @@ func New(t testing.TB) *Env { return &res } +func (e *Env) Errorf(format string, args ...any) { + e.t.Helper() + e.t.Errorf(format, args...) +} + func (e *Env) Fatalf(format string, args ...any) { e.t.Helper() e.T().Fatalf(format, args...) @@ -155,6 +160,14 @@ func StartProcessWhichListenPort(e *Env, host string, port int, name string, com }) } +func ServerMock(e *Env, port int) *TestServerT { + return fixenv.CacheWithCleanup(e, port, nil, func() (*TestServerT, fixenv.FixtureCleanupFunc, error) { + endpoint := "localhost:" + strconv.Itoa(port) + res := NewTestServerT(e, endpoint) + return res, res.Stop, nil + }) +} + func RestyClient(e *Env, host string) *resty.Client { return fixenv.Cache(e, host, nil, func() (*resty.Client, error) { return resty. diff --git a/cmd/metricstest/flags.go b/cmd/metricstest/flags.go index 9adf824..883ed31 100644 --- a/cmd/metricstest/flags.go +++ b/cmd/metricstest/flags.go @@ -3,11 +3,14 @@ package main import ( "flag" "strconv" + "time" ) const ( - serverDefaultHost = "localhost" - serverDefaultPort = 8080 + serverDefaultHost = "localhost" + serverDefaultPort = 8080 + agentDefaultReportInterval = 10 * time.Second + agentDefaultPollInterval = 2 * time.Second ) var ( @@ -24,7 +27,7 @@ var ( func init() { flag.StringVar(&flagAgentBinaryPath, "agent-binary-path", "", "path to target agent binary") - flag.StringVar(&flagServerBinaryPath, "binary-path", "cmd/server/server", "path to target server binary") + flag.StringVar(&flagServerBinaryPath, "binary-path", "", "path to target server binary") flag.StringVar(&flagTargetSourcePath, "source-path", "", "path to target server source") flag.StringVar(&flagServerHost, "server-host", serverDefaultHost, "host of target address") flag.StringVar(&flagServerPort, "server-port", strconv.Itoa(serverDefaultPort), "port of target address") diff --git a/cmd/metricstest/helper_server_test.go b/cmd/metricstest/helper_server_test.go new file mode 100644 index 0000000..d1dadd4 --- /dev/null +++ b/cmd/metricstest/helper_server_test.go @@ -0,0 +1,108 @@ +package main + +import ( + "context" + "fmt" + "io" + "net/http" + "strconv" + "sync" + + "github.com/gin-gonic/gin" +) + +type TestServerT struct { + e *Env + endpoint string + + httpServer http.Server + gin *gin.Engine + + m sync.Mutex + counters map[string][]int64 + gauges map[string][]float64 +} + +func NewTestServerT(e *Env, endpoint string) *TestServerT { + s := &TestServerT{ + e: e, + endpoint: endpoint, + counters: map[string][]int64{}, + gauges: map[string][]float64{}, + gin: gin.New(), + } + + s.httpServer.Addr = s.endpoint + s.httpServer.Handler = s.gin + + s.gin.RedirectTrailingSlash = false + s.gin.RedirectFixedPath = false + + s.gin.POST("/update/counter/:name/:value", s.storeCounter) + s.gin.POST("/update/gauge/:name/:value", s.storeGauge) + + s.gin.Any("/", s.unexpectedCall) + return s +} + +func (s *TestServerT) Start() error { + return s.httpServer.ListenAndServe() +} + +func (s *TestServerT) Stop() { + _ = s.httpServer.Shutdown(context.Background()) +} + +func (s *TestServerT) storeCounter(c *gin.Context) { + name := c.Param("name") + valS := c.Param("value") + val, err := strconv.ParseInt(valS, 10, 64) + if err != nil { + s.e.Errorf("Пришло значение counter не соответствующее формату. name: %q, value: %q, err: %+v", name, valS, err) + _ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("parse int value error: %w", err)) + return + } + + s.m.Lock() + defer s.m.Unlock() + s.counters[name] = append(s.counters[name], val) +} + +func (s *TestServerT) storeGauge(c *gin.Context) { + name := c.Param("name") + valS := c.Param("value") + + val, err := strconv.ParseFloat(valS, 64) + if err != nil { + s.e.Errorf("Пришло значение gauge не соответствующее формату. name: %q, value: %q, err: %+v", name, valS, err) + _ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("parse float value error: %w", err)) + return + } + + s.m.Lock() + defer s.m.Unlock() + s.gauges[name] = append(s.gauges[name], val) +} + +func (s *TestServerT) unexpectedCall(c *gin.Context) { + c.AbortWithStatus(http.StatusBadRequest) + + s.e.Errorf("Пришёл неожиданный запрос, method: %q, uri: %q", c.Request.Method, c.Request.RequestURI) + content, err := io.ReadAll(c.Request.Body) + if err != nil { + s.e.Errorf("Не могу прочитать тело запроса:") + return + } + _ = c.Request.Body.Close() + s.e.Errorf("Тело запроса:\n%s", content) +} + +func (s *TestServerT) checkReceiveValues(gauges []string, needCount int) { + s.m.Lock() + defer s.m.Unlock() + + for _, name := range gauges { + values := s.gauges[name] + s.e.Len(values, needCount, "Количество значений, полученных по метрике расходится с ожидаемым") + } +} diff --git a/cmd/metricstest/helpers_test.go b/cmd/metricstest/helpers_test.go deleted file mode 100644 index b870f19..0000000 --- a/cmd/metricstest/helpers_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package main - -type StartedServerHost string diff --git a/cmd/metricstest/iteration2a_test.go b/cmd/metricstest/iteration2a_test.go index 6a15744..6f683ec 100644 --- a/cmd/metricstest/iteration2a_test.go +++ b/cmd/metricstest/iteration2a_test.go @@ -5,6 +5,7 @@ import ( "errors" "os" "syscall" + "testing" "time" "github.com/stretchr/testify/suite" @@ -12,6 +13,30 @@ import ( "github.com/Yandex-Practicum/go-autotests/internal/fork" ) +func TestIteration2A(t *testing.T) { + e := New(t) + serverMock := ServerMock(e, serverDefaultPort) + + gauges := []string{ + "Alloc", "BuckHashSys", "Frees", "GCCPUFraction", "GCSys", "HeapAlloc", "HeapIdle", "HeapInuse", "HeapObjects", "HeapReleased", "HeapSys", "LastGC", "Lookups", "MCacheInuse", "MCacheSys", "MSpanInuse", "MSpanSys", "Mallocs", "NextGC", + "NumForcedGC", "NumGC", "OtherSys", "PauseTotalNs", "StackInuse", "StackSys", "Sys", "TotalAlloc", "RandomValue", + } + + StartDefaultAgent(e) + + firstIterationTimeout := agentDefaultPollInterval + agentDefaultReportInterval + firstIterationTimeout += firstIterationTimeout / 10 + + e.Logf("Жду %v", firstIterationTimeout) + time.Sleep(firstIterationTimeout) + + serverMock.checkReceiveValues(gauges, 1) +} + +func StartDefaultAgent(e *Env) { + StartProcess(e, "agent", AgentPath(e)) +} + type Iteration2ASuite struct { suite.Suite diff --git a/cmd/metricstest/main_test.go b/cmd/metricstest/main_test.go index b94bbb5..268a21c 100644 --- a/cmd/metricstest/main_test.go +++ b/cmd/metricstest/main_test.go @@ -7,6 +7,7 @@ import ( "os" "testing" + "github.com/gin-gonic/gin" "github.com/rekby/fixenv" "github.com/stretchr/testify/suite" ) @@ -14,14 +15,12 @@ import ( func TestMain(m *testing.M) { flag.Parse() + gin.SetMode(gin.ReleaseMode) + fixenv.CreateMainTestEnv(nil) os.Exit(m.Run()) } -func TestIteration2A(t *testing.T) { - suite.Run(t, new(Iteration2ASuite)) -} - func TestIteration2B(t *testing.T) { suite.Run(t, new(Iteration2BSuite)) } diff --git a/go.mod b/go.mod index 14ddfd4..1f1080a 100644 --- a/go.mod +++ b/go.mod @@ -8,28 +8,50 @@ require ( github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40 github.com/jackc/pgx v3.6.2+incompatible github.com/jingyugao/rowserrcheck v1.1.1 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 golang.org/x/tools v0.1.12 honnef.co/go/tools v0.3.3 ) +require ( + github.com/bytedance/sonic v1.8.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.11.2 // indirect + github.com/goccy/go-json v0.10.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.9 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + google.golang.org/protobuf v1.28.1 // indirect +) + require ( github.com/BurntSushi/toml v0.4.1 // indirect github.com/cockroachdb/apd v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gin-gonic/gin v1.9.0 github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 // indirect github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect github.com/lib/pq v1.10.7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rekby/fixenv v0.3.2 // indirect + github.com/rekby/fixenv v0.3.2 github.com/shopspring/decimal v1.3.1 // indirect golang.org/x/crypto v0.6.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/net v0.7.0 golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9689c48..36ed824 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,35 @@ github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= +github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +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.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40 h1:ykKxL12NZd3JmWZnyqarJGsF73M9Xhtrik/FEtEeFRE= github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= @@ -19,8 +40,22 @@ github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yI github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -31,12 +66,24 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDHS9/4U9yQo1UcPQM0kOMJHn29EoH/Ro= github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= +github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= @@ -63,6 +110,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -80,7 +128,11 @@ golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -88,3 +140,4 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA= honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From dd905425a59651257f1625edaef0344c2ce63087 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 17:29:05 +0300 Subject: [PATCH 04/11] iteration 2a --- cmd/metricstest/fixtures_test.go | 50 ++++++++++++++++----------- cmd/metricstest/helper_server_test.go | 39 +++++++++++++++++++-- cmd/metricstest/iteration2a_test.go | 17 +++++++-- 3 files changed, 81 insertions(+), 25 deletions(-) diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index 8bd784d..2497b35 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -137,26 +137,8 @@ func StartProcessWhichListenPort(e *Env, host string, port int, name string, com cacheKey := append([]string{host, strconv.Itoa(port), name, command}, args...) return fixenv.Cache[*fork.BackgroundProcess](e, cacheKey, nil, func() (*fork.BackgroundProcess, error) { process := StartProcess(e, name, command, args...) - ctx, cancel := context.WithTimeout(e.Ctx, startProcessTimeout) - defer cancel() - address := fmt.Sprintf("%v:%v", host, port) - dialer := net.Dialer{} - e.Logf("Пробую подключиться на %q...", address) - - for { - time.Sleep(checkPortInterval) - conn, err := dialer.DialContext(ctx, "tcp", address) - if err == nil { - e.Logf("Закрываю успешное подключение") - err = conn.Close() - return process, err - } - if ctx.Err() != nil { - e.Fatalf("Ошибка подлючения: %+v", err) - return nil, err - } - } + return process, waitOpenPort(e, address) }) } @@ -164,7 +146,13 @@ func ServerMock(e *Env, port int) *TestServerT { return fixenv.CacheWithCleanup(e, port, nil, func() (*TestServerT, fixenv.FixtureCleanupFunc, error) { endpoint := "localhost:" + strconv.Itoa(port) res := NewTestServerT(e, endpoint) - return res, res.Stop, nil + + e.Logf("Запускаю мок сервер: %q", endpoint) + go res.Start() + + err := waitOpenPort(e, endpoint) + + return res, res.Stop, err }) } @@ -194,3 +182,25 @@ func (l restyLogger) Warnf(format string, v ...interface{}) { func (l restyLogger) Debugf(format string, v ...interface{}) { l.e.Logf("resty: "+format, v...) } + +func waitOpenPort(e *Env, address string) error { + ctx, cancel := context.WithTimeout(e.Ctx, startProcessTimeout) + defer cancel() + + dialer := net.Dialer{} + e.Logf("Пробую подключиться на %q...", address) + + for { + time.Sleep(checkPortInterval) + conn, err := dialer.DialContext(ctx, "tcp", address) + if err == nil { + e.Logf("Закрываю успешное подключение") + err = conn.Close() + return err + } + if ctx.Err() != nil { + e.Fatalf("Ошибка подлючения: %+v", err) + return err + } + } +} diff --git a/cmd/metricstest/helper_server_test.go b/cmd/metricstest/helper_server_test.go index d1dadd4..9eb1965 100644 --- a/cmd/metricstest/helper_server_test.go +++ b/cmd/metricstest/helper_server_test.go @@ -66,6 +66,7 @@ func (s *TestServerT) storeCounter(c *gin.Context) { s.m.Lock() defer s.m.Unlock() s.counters[name] = append(s.counters[name], val) + s.e.Logf("Получено значение counter %q: %v", name, val) } func (s *TestServerT) storeGauge(c *gin.Context) { @@ -82,6 +83,7 @@ func (s *TestServerT) storeGauge(c *gin.Context) { s.m.Lock() defer s.m.Unlock() s.gauges[name] = append(s.gauges[name], val) + s.e.Logf("Получено значение gauge %q: %f", name, val) } func (s *TestServerT) unexpectedCall(c *gin.Context) { @@ -97,12 +99,45 @@ func (s *TestServerT) unexpectedCall(c *gin.Context) { s.e.Errorf("Тело запроса:\n%s", content) } -func (s *TestServerT) checkReceiveValues(gauges []string, needCount int) { +func (s *TestServerT) CheckReceiveValues(gauges []string, counters []string, min, max int) { s.m.Lock() defer s.m.Unlock() for _, name := range gauges { values := s.gauges[name] - s.e.Len(values, needCount, "Количество значений, полученных по метрике расходится с ожидаемым") + lenValues := len(values) + s.e.True(min <= lenValues && lenValues <= max, "Ожидается количество gauge значений от %v до %v, сейас получено: %v", min, max, lenValues) } + + for _, name := range counters { + values := s.counters[name] + lenValues := len(values) + s.e.True(min <= lenValues && lenValues <= max, "Ожидается количество counter значений от %v до %v, сейас получено: %v", min, max, lenValues) + } +} + +func (s *TestServerT) GetLastCounter(name string) int64 { + s.m.Lock() + defer s.m.Unlock() + + val := s.counters[name] + if len(val) > 0 { + return val[len(val)-1] + } + + s.e.Errorf("Запрошено значение отсутствующего счётчика counter: %q", name) + return 0 +} + +func (s *TestServerT) GetLastGauge(name string) float64 { + s.m.Lock() + defer s.m.Unlock() + + val := s.gauges[name] + if len(val) > 0 { + return val[len(val)-1] + } + + s.e.Errorf("Запрошено значение отсутствующего счётчика counter: %q", name) + return 0 } diff --git a/cmd/metricstest/iteration2a_test.go b/cmd/metricstest/iteration2a_test.go index 6f683ec..a6d2dae 100644 --- a/cmd/metricstest/iteration2a_test.go +++ b/cmd/metricstest/iteration2a_test.go @@ -21,16 +21,27 @@ func TestIteration2A(t *testing.T) { "Alloc", "BuckHashSys", "Frees", "GCCPUFraction", "GCSys", "HeapAlloc", "HeapIdle", "HeapInuse", "HeapObjects", "HeapReleased", "HeapSys", "LastGC", "Lookups", "MCacheInuse", "MCacheSys", "MSpanInuse", "MSpanSys", "Mallocs", "NextGC", "NumForcedGC", "NumGC", "OtherSys", "PauseTotalNs", "StackInuse", "StackSys", "Sys", "TotalAlloc", "RandomValue", } + counters := []string{ + "PollCount", + } StartDefaultAgent(e) - firstIterationTimeout := agentDefaultPollInterval + agentDefaultReportInterval - firstIterationTimeout += firstIterationTimeout / 10 + firstIterationTimeout := agentDefaultReportInterval + agentDefaultReportInterval/2 e.Logf("Жду %v", firstIterationTimeout) time.Sleep(firstIterationTimeout) - serverMock.checkReceiveValues(gauges, 1) + serverMock.CheckReceiveValues(gauges, counters, 1, 2) + firstRandom := serverMock.GetLastGauge("RandomValue") + e.InDelta(int(agentDefaultReportInterval/agentDefaultPollInterval), serverMock.GetLastCounter("PollCount"), 1) + + e.Logf("Жду ещё %v", agentDefaultReportInterval) + time.Sleep(agentDefaultReportInterval) + + serverMock.CheckReceiveValues(gauges, counters, 2, 3) + e.InDelta(agentDefaultReportInterval/agentDefaultPollInterval*2, serverMock.GetLastCounter("PollCount"), 1) + e.NotEqual(firstRandom, serverMock.GetLastGauge("RandomValue")) } func StartDefaultAgent(e *Env) { From e3983e9214c0c9b409f0d5187735e3f39c7d6385 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 17:30:04 +0300 Subject: [PATCH 05/11] remove launch from git --- .vscode/launch.json | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 591caf5..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "test server", - "type":"go", - "request": "launch", - "mode": "test", - "program": "/Users/rekby/projects/praktikum/go-autotests/cmd/metricstest/iteration1_test.go", - "args": [ - // "-test.v", - "-test.run=^TestIteration1$", - "--binary-path=/Users/rekby/projects/rekby/mymetrics/cmd/server/server", - "--server-port=8080", - "--source-path=/Users/rekby/projects/rekby/mymetrics", - "--agent-binary-path=/Users/rekby/projects/rekby/mymetrics/cmd/agent/agent" - ] - } - ] -} From 1c987ece9173677a1b06f0e3168336d47872c5bc Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 17:30:12 +0300 Subject: [PATCH 06/11] add vscode to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 82ec5dd..330b20f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ vendor/ # Ignore IDE .idea/ +.vscode/ # Ignore artifacts cmd/shortenertest/shortenertest From 5f0673404800fcfbc2837b69613163d963a677e9 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 18:12:09 +0300 Subject: [PATCH 07/11] iter 2b --- cmd/metricstest/fixtures_test.go | 45 +++++++++++++- cmd/metricstest/iteration2a_test.go | 2 +- cmd/metricstest/iteration2b_test.go | 92 ++++++++++++++++++++++++----- cmd/metricstest/main_test.go | 4 -- 4 files changed, 121 insertions(+), 22 deletions(-) diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index 2497b35..d8c2a30 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -79,7 +79,7 @@ func ExistPath(e *Env, filePath string) string { }) } -func AgentPath(e *Env) string { +func AgentFilePath(e *Env) string { return ExistPath(e, flagAgentBinaryPath) } @@ -101,6 +101,49 @@ func ServerPort(e *Env) int { }) } +func AgentSourcePath(e *Env) string { + return filepath.Join(TargetSourcePath(e), "cmd/agent") +} + +func ServerSourcePath(e *Env) string { + return filepath.Join(TargetSourcePath(e), "cmd/server") +} + +func TargetSourcePath(e *Env) string { + return fixenv.Cache(e, nil, &fixenv.FixtureOptions{Scope: fixenv.ScopePackage}, func() (string, error) { + // project/cmd/server/server + startPath := flagServerBinaryPath + if startPath == "" { + startPath = flagAgentBinaryPath + } + + absPath, err := filepath.Abs(startPath) + e.NoError(err, "Не могу построить полный путь к начальному файлу %q") + + // project/cmd/server + absPath = filepath.Dir(absPath) + + // project/cmd + absPath = filepath.Dir(absPath) + + // project + absPath = filepath.Dir(absPath) + + e.Logf("Проверяю что %q (%q) - это папка", absPath, flagTargetSourcePath) + stat, err := os.Stat(absPath) + if err != nil { + e.Fatalf("Не могу получить информацию о папке", err) + return "", err + } + + if stat.IsDir() { + return absPath, nil + } + + return "", fmt.Errorf("%q - не папка", absPath) + }) +} + func StartProcess(e *Env, name string, command string, args ...string) *fork.BackgroundProcess { cacheKey := append([]string{name, command}, args...) return fixenv.CacheWithCleanup(e, cacheKey, nil, func() (*fork.BackgroundProcess, fixenv.FixtureCleanupFunc, error) { diff --git a/cmd/metricstest/iteration2a_test.go b/cmd/metricstest/iteration2a_test.go index a6d2dae..7277cf2 100644 --- a/cmd/metricstest/iteration2a_test.go +++ b/cmd/metricstest/iteration2a_test.go @@ -45,7 +45,7 @@ func TestIteration2A(t *testing.T) { } func StartDefaultAgent(e *Env) { - StartProcess(e, "agent", AgentPath(e)) + StartProcess(e, "agent", AgentFilePath(e)) } type Iteration2ASuite struct { diff --git a/cmd/metricstest/iteration2b_test.go b/cmd/metricstest/iteration2b_test.go index ae73252..17f9e2d 100644 --- a/cmd/metricstest/iteration2b_test.go +++ b/cmd/metricstest/iteration2b_test.go @@ -9,11 +9,87 @@ import ( "path/filepath" "regexp" "strings" + "testing" "time" "github.com/stretchr/testify/suite" ) +func TestIteration2B(t *testing.T) { + commonEnv := New(t) + table := []struct { + name, path string + }{ + { + "agent", + AgentSourcePath(commonEnv), + }, + { + "server", + ServerSourcePath(commonEnv), + }, + } + + for _, test := range table { + t.Run(test.name, func(t *testing.T) { + t.Run("TestFilesPresence", func(t *testing.T) { + e := New(t) + + sourcePath := test.path + + hasTestFile := false + err := filepath.WalkDir(sourcePath, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + // skip vendor directory + if d.Name() == "vendor" || d.Name() == ".git" { + return filepath.SkipDir + } + // dive into regular directory + return nil + } + + if strings.HasSuffix(d.Name(), "_test.go") { + hasTestFile = true + return filepath.SkipAll + } + + return nil + }) + + e.NoErrorf(err, "Неожиданная ошибка при поиске тестовых файлов по пути %s: %s", sourcePath, err) + + if !hasTestFile { + e.Errorf("Не найден ни один тестовый файл по пути %s", sourcePath) + } + }) + + t.Run("TestAgentCoverage", func(t *testing.T) { + e := New(t) + sourcePath := test.path + + coverRegex := regexp.MustCompile(`coverage: (\d+.\d)% of statements`) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + cmd := exec.CommandContext(ctx, "go", "test", "-cover", sourcePath) + cmd.Env = os.Environ() // pass parent envs + cmd.Dir = sourcePath + out, err := cmd.CombinedOutput() + e.NoError(err, "Невозможно получить результат выполнения команды: %s. Вывод:\n\n %s", cmd, out) + + matched := coverRegex.Match(out) + e.True(matched, "Отсутствует информация о покрытии кода тестами, команда: %s", cmd) + e.Logf("Вывод команды:\n\n%s", string(out)) + }) + }) + } +} + type Iteration2BSuite struct { suite.Suite @@ -66,20 +142,4 @@ func (suite *Iteration2BSuite) TestFilesPresence() { // TestServerCoverage attempts to obtain and parse coverage report using standard Go tooling func (suite *Iteration2BSuite) TestServerCoverage() { - sourcePath := strings.TrimRight(flagTargetSourcePath, "/") + "/..." - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - cmd := exec.CommandContext(ctx, "go", "test", "-cover", sourcePath) - cmd.Env = os.Environ() // pass parent envs - out, err := cmd.CombinedOutput() - suite.Assert().NoError(err, "Невозможно получить результат выполнения команды: %s. Вывод:\n\n %s", cmd, out) - - matched := suite.coverRegex.Match(out) - found := suite.Assert().True(matched, "Отсутствует информация о покрытии кода тестами, команда: %s", cmd) - - if !found { - suite.T().Logf("Вывод команды:\n\n%s", string(out)) - } } diff --git a/cmd/metricstest/main_test.go b/cmd/metricstest/main_test.go index 268a21c..973564d 100644 --- a/cmd/metricstest/main_test.go +++ b/cmd/metricstest/main_test.go @@ -21,10 +21,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestIteration2B(t *testing.T) { - suite.Run(t, new(Iteration2BSuite)) -} - func TestIteration3A(t *testing.T) { suite.Run(t, new(Iteration3ASuite)) } From 09addd49e3f09cfa72a864b002bb0f05cc6edc6d Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 19:13:06 +0300 Subject: [PATCH 08/11] iter3 --- cmd/metricstest/fixtures_test.go | 16 +- cmd/metricstest/iteration3a_test.go | 206 ++++++++++--------- cmd/metricstest/iteration3b_test.go | 293 ++++++++-------------------- cmd/metricstest/main_test.go | 8 - 4 files changed, 204 insertions(+), 319 deletions(-) diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index d8c2a30..a1c2508 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -102,11 +102,21 @@ func ServerPort(e *Env) int { } func AgentSourcePath(e *Env) string { - return filepath.Join(TargetSourcePath(e), "cmd/agent") + return fixenv.Cache(e, nil, &fixenv.FixtureOptions{Scope: fixenv.ScopePackage}, func() (string, error) { + res := filepath.Join(TargetSourcePath(e), "cmd/agent") + e.Logf("Путь к исходникам агента: %q", res) + e.DirExists(res) + return res, nil + }) } func ServerSourcePath(e *Env) string { - return filepath.Join(TargetSourcePath(e), "cmd/server") + return fixenv.Cache(e, nil, &fixenv.FixtureOptions{Scope: fixenv.ScopePackage}, func() (string, error) { + res := filepath.Join(TargetSourcePath(e), "cmd/server") + e.Logf("Путь к исходникам сервера: %q", res) + e.DirExists(res) + return res, nil + }) } func TargetSourcePath(e *Env) string { @@ -191,7 +201,7 @@ func ServerMock(e *Env, port int) *TestServerT { res := NewTestServerT(e, endpoint) e.Logf("Запускаю мок сервер: %q", endpoint) - go res.Start() + go func() { _ = res.Start() }() err := waitOpenPort(e, endpoint) diff --git a/cmd/metricstest/iteration3a_test.go b/cmd/metricstest/iteration3a_test.go index 9b4459c..922e24d 100644 --- a/cmd/metricstest/iteration3a_test.go +++ b/cmd/metricstest/iteration3a_test.go @@ -1,103 +1,121 @@ package main import ( - "errors" - - "github.com/stretchr/testify/suite" + "net/http" + "strconv" + "testing" ) -type Iteration3ASuite struct { - suite.Suite +func TestIteration3A(t *testing.T) { + t.Run("TestCounter", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) - knownFrameworks []string -} + resp, err := c.R().Post("update/counter/test1/1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен вернуть код 200 (http.StatusOK)") -func (suite *Iteration3ASuite) SetupSuite() { - // check required flags - suite.Require().NotEmpty(flagTargetSourcePath, "-source-path non-empty flag required") - - suite.knownFrameworks = []string{ - "aahframework.org", - "confetti-framework.com", - "github.com/abahmed/gearbox", - "github.com/aerogo/aero", - "github.com/aisk/vox", - "github.com/ant0ine/go-json-rest", - "github.com/aofei/air", - "github.com/appist/appy", - "github.com/astaxie/beego", - "github.com/beatlabs/patron", - "github.com/bnkamalesh/webgo", - "github.com/buaazp/fasthttprouter", - "github.com/claygod/Bxog", - "github.com/claygod/microservice", - "github.com/dimfeld/httptreemux", - "github.com/dinever/golf", - "github.com/fulldump/golax", - "github.com/gernest/alien", - "github.com/gernest/utron", - "github.com/gin-gonic/gin", - "github.com/go-chi/chi", - "github.com/go-goyave/goyave", - "github.com/go-macaron/macaron", - "github.com/go-ozzo/ozzo-routing", - "github.com/go-playground/lars", - "github.com/go-playground/pure", - "github.com/go-zoo/bone", - "github.com/goa-go/goa", - "github.com/goadesign/goa", - "github.com/goanywhere/rex", - "github.com/gocraft/web", - "github.com/gofiber/fiber", - "github.com/goji/goji", - "github.com/gookit/rux", - "github.com/gorilla/mux", - "github.com/goroute/route", - "github.com/gotuna/gotuna", - "github.com/gowww/router", - "github.com/GuilhermeCaruso/bellt", - "github.com/hidevopsio/hiboot", - "github.com/husobee/vestigo", - "github.com/i-love-flamingo/flamingo", - "github.com/i-love-flamingo/flamingo-commerce", - "github.com/ivpusic/neo", - "github.com/julienschmidt/httprouter", - "github.com/labstack/echo", - "github.com/lunny/tango", - "github.com/mustafaakin/gongular", - "github.com/nbari/violetear", - "github.com/nsheremet/banjo", - "github.com/NYTimes/gizmo", - "github.com/paulbellamy/mango", - "github.com/rainycape/gondola", - "github.com/razonyang/fastrouter", - "github.com/rcrowley/go-tigertonic", - "github.com/resoursea/api", - "github.com/revel/revel", - "github.com/rs/xmux", - "github.com/twharmon/goweb", - "github.com/uadmin/uadmin", - "github.com/ungerik/go-rest", - "github.com/vardius/gorouter", - "github.com/VividCortex/siesta", - "github.com/xujiajun/gorouter", - "github.com/xxjwxc/ginrpc", - "github.com/yarf-framework/yarf", - "github.com/zpatrick/fireball", - "gobuffalo.io", - "rest-layer.io", - } -} + resp, err = c.R().Get("value/counter/test1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equalf(http.StatusOK, resp.StatusCode(), "При получении известного значения метрики сервер должен вернуть код 200 (http.StatusOK)") + + val, err := strconv.Atoi(string(resp.Body())) + e.NoErrorf(err, "Ошибка при попытке распарсить значение %q", resp.Body()) + e.Equal(1, val, "Сервер вернул не то значение, которое было отправлено") + + resp, err = c.R().Post("update/counter/test1/2") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен вернуть код 200 (http.StatusOK)") + + resp, err = c.R().Get("value/counter/test1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + val, err = strconv.Atoi(string(resp.Body())) + e.NoErrorf(err, "Ошибка при попытке распарсить обновлённое значение %q", resp.Body()) + e.Equal(3, val, "Сервер должен был вернуть сумму отправленных значений: 1+2") + + // добавляем второе значение + resp, err = c.R().Post("update/counter/test2/4") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен вернуть код 200 (http.StatusOK)") + + resp, err = c.R().Get("value/counter/test2") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equalf(http.StatusOK, resp.StatusCode(), "При получении известного значения метрики сервер должен вернуть код 200 (http.StatusOK)") + + val, err = strconv.Atoi(string(resp.Body())) + e.NoErrorf(err, "Ошибка при попытке распарсить значение %q", resp.Body()) + e.Equal(4, val, "Сервер вернул не то значение, которое было отправлено для второй метрики") + + resp, err = c.R().Get("value/counter/test1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + val, err = strconv.Atoi(string(resp.Body())) + e.NoErrorf(err, "Ошибка при попытке распарсить обновлённое значение %q", resp.Body()) + e.Equal(3, val, "Сервер неправильно вернул значение первого счётчика") + }) + + t.Run("unknown-value", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + resp, err := c.R().Get("value/counter/unknown") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusNotFound, resp.StatusCode(), "При запросе неизвестного значения должен возвращаться код 404 (http.StatusNotFound)") + }) + }) + + t.Run("TestGauge", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + + resp, err := c.R().Post("update/gauge/test1/1.5") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен вернуть код 200 (http.StatusOK)") + + resp, err = c.R().Get("value/gauge/test1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equalf(http.StatusOK, resp.StatusCode(), "При получении известного значения метрики сервер должен вернуть код 200 (http.StatusOK)") + + val, err := strconv.ParseFloat(string(resp.Body()), 64) + e.NoErrorf(err, "Ошибка при попытке распарсить значение %q", resp.Body()) + e.InEpsilon(1.5, val, 0.1, "Сервер вернул не то значение, которое было отправлено") + + resp, err = c.R().Post("update/gauge/test1/2") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен вернуть код 200 (http.StatusOK)") + + resp, err = c.R().Get("value/gauge/test1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + val, err = strconv.ParseFloat(string(resp.Body()), 64) + e.NoErrorf(err, "Ошибка при попытке распарсить обновлённое значение %q", resp.Body()) + e.InEpsilon(2, val, 0.1, "Сервер вернул не то значение, которое было отправлено при обновлении существующего значения") + + // добавляем второе значение + resp, err = c.R().Post("update/gauge/test2/4") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusOK, resp.StatusCode(), "При добавлении нового значения сервер должен вернуть код 200 (http.StatusOK)") + + resp, err = c.R().Get("value/gauge/test2") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equalf(http.StatusOK, resp.StatusCode(), "При получении известного значения метрики сервер должен вернуть код 200 (http.StatusOK)") + + val, err = strconv.ParseFloat(string(resp.Body()), 64) + e.NoErrorf(err, "Ошибка при попытке распарсить значение %q", resp.Body()) + e.InEpsilon(4, val, 0.1, "Сервер вернул не то значение, которое было отправлено для второй метрики") + + resp, err = c.R().Get("value/gauge/test1") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + val, err = strconv.ParseFloat(string(resp.Body()), 64) + e.NoErrorf(err, "Ошибка при попытке распарсить обновлённое значение %q", resp.Body()) + e.InEpsilon(2, val, 0.1, "Сервер неправильно вернул значение первого счётчика") + }) -// TestFrameworkUsage attempts to recursively find usage of known HTTP frameworks in given sources -func (suite *Iteration3ASuite) TestHTTPFrameworkUsage() { - err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownFrameworks) - if errors.Is(err, errUsageFound) { - return - } - if err == nil || errors.Is(err, errUsageNotFound) { - suite.T().Errorf("Не найдено использование хотя бы одного известного HTTP фреймворка по пути %q", flagTargetSourcePath) - return - } - suite.T().Errorf("Неожиданная ошибка при поиске использования фреймворка по пути %q, %v", flagTargetSourcePath, err) + t.Run("unknown-value", func(t *testing.T) { + e := New(t) + c := DefaultServer(e) + resp, err := c.R().Get("value/gauge/unknown") + e.NoError(err, "Ошибка при выполнении запроса к серверу") + e.Equal(http.StatusNotFound, resp.StatusCode(), "При запросе неизвестного значения должен возвращаться код 404 (http.StatusNotFound)") + }) + }) } diff --git a/cmd/metricstest/iteration3b_test.go b/cmd/metricstest/iteration3b_test.go index 640d241..858fcac 100644 --- a/cmd/metricstest/iteration3b_test.go +++ b/cmd/metricstest/iteration3b_test.go @@ -1,228 +1,93 @@ package main import ( - "context" "errors" - "fmt" - "math/rand" - "net/http" - "os" - "strconv" - "strings" - "syscall" - "time" - - "github.com/go-resty/resty/v2" - "github.com/stretchr/testify/suite" - - "github.com/Yandex-Practicum/go-autotests/internal/fork" + "testing" ) -type Iteration3BSuite struct { - suite.Suite - - serverAddress string - serverProcess *fork.BackgroundProcess -} - -func (suite *Iteration3BSuite) SetupSuite() { - // check required flags - suite.Require().NotEmpty(flagServerBinaryPath, "-binary-path non-empty flag required") - - suite.serverAddress = "http://localhost:8080" - - envs := append(os.Environ(), []string{ - "RESTORE=false", - }...) - p := fork.NewBackgroundProcess(context.Background(), flagServerBinaryPath, - fork.WithEnv(envs...), - ) - - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - - err := p.Start(ctx) - if err != nil { - suite.T().Errorf("Невозможно запустить процесс командой %s: %s. Переменные окружения: %+v", p, err, envs) - return - } - - port := "8080" - err = p.WaitPort(ctx, "tcp", port) - if err != nil { - suite.T().Errorf("Не удалось дождаться пока порт %s станет доступен для запроса: %s", port, err) - return +func TestIteration3B(t *testing.T) { + knownFrameworks := []string{ + "aahframework.org", + "confetti-framework.com", + "github.com/abahmed/gearbox", + "github.com/aerogo/aero", + "github.com/aisk/vox", + "github.com/ant0ine/go-json-rest", + "github.com/aofei/air", + "github.com/appist/appy", + "github.com/astaxie/beego", + "github.com/beatlabs/patron", + "github.com/bnkamalesh/webgo", + "github.com/buaazp/fasthttprouter", + "github.com/claygod/Bxog", + "github.com/claygod/microservice", + "github.com/dimfeld/httptreemux", + "github.com/dinever/golf", + "github.com/fulldump/golax", + "github.com/gernest/alien", + "github.com/gernest/utron", + "github.com/gin-gonic/gin", + "github.com/go-chi/chi", + "github.com/go-goyave/goyave", + "github.com/go-macaron/macaron", + "github.com/go-ozzo/ozzo-routing", + "github.com/go-playground/lars", + "github.com/go-playground/pure", + "github.com/go-zoo/bone", + "github.com/goa-go/goa", + "github.com/goadesign/goa", + "github.com/goanywhere/rex", + "github.com/gocraft/web", + "github.com/gofiber/fiber", + "github.com/goji/goji", + "github.com/gookit/rux", + "github.com/gorilla/mux", + "github.com/goroute/route", + "github.com/gotuna/gotuna", + "github.com/gowww/router", + "github.com/GuilhermeCaruso/bellt", + "github.com/hidevopsio/hiboot", + "github.com/husobee/vestigo", + "github.com/i-love-flamingo/flamingo", + "github.com/i-love-flamingo/flamingo-commerce", + "github.com/ivpusic/neo", + "github.com/julienschmidt/httprouter", + "github.com/labstack/echo", + "github.com/lunny/tango", + "github.com/mustafaakin/gongular", + "github.com/nbari/violetear", + "github.com/nsheremet/banjo", + "github.com/NYTimes/gizmo", + "github.com/paulbellamy/mango", + "github.com/rainycape/gondola", + "github.com/razonyang/fastrouter", + "github.com/rcrowley/go-tigertonic", + "github.com/resoursea/api", + "github.com/revel/revel", + "github.com/rs/xmux", + "github.com/twharmon/goweb", + "github.com/uadmin/uadmin", + "github.com/ungerik/go-rest", + "github.com/vardius/gorouter", + "github.com/VividCortex/siesta", + "github.com/xujiajun/gorouter", + "github.com/xxjwxc/ginrpc", + "github.com/yarf-framework/yarf", + "github.com/zpatrick/fireball", + "gobuffalo.io", + "rest-layer.io", } - suite.serverProcess = p -} + e := New(t) + serverSourcePath := ServerSourcePath(e) + err := usesKnownPackage(t, serverSourcePath, knownFrameworks) -func (suite *Iteration3BSuite) TearDownSuite() { - if suite.serverProcess == nil { + if errors.Is(err, errUsageFound) { return } - - exitCode, err := suite.serverProcess.Stop(syscall.SIGINT, syscall.SIGKILL) - if err != nil { - if errors.Is(err, os.ErrProcessDone) { - return - } - suite.T().Logf("Не удалось остановить процесс с помощью сигнала ОС: %s", err) + if err == nil || errors.Is(err, errUsageNotFound) { + e.Errorf("Не найдено использование хотя бы одного известного HTTP фреймворка по пути %q", serverSourcePath) return } - - if exitCode > 0 { - suite.T().Logf("Процесс завершился с не нулевым статусом %d", exitCode) - } - - // try to read stdout/stderr - ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) - defer cancel() - - out := suite.serverProcess.Stderr(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDERR лог процесса:\n\n%s", string(out)) - } - out = suite.serverProcess.Stdout(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDOUT лог процесса:\n\n%s", string(out)) - } -} - -func (suite *Iteration3BSuite) TestGauge() { - // create HTTP client without redirects support - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked - }) - - httpc := resty.NewWithClient(&http.Client{ - Transport: &http.Transport{ - DisableCompression: true, - }, - }). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) - - count := 3 - suite.Run("update sequence", func() { - id := strconv.Itoa(rand.Intn(256)) - req := httpc.R() - for i := 0; i < count; i++ { - v := strings.TrimRight(fmt.Sprintf("%.3f", rand.Float64()*1000000), "0") - resp, err := req.Post("update/gauge/testSetGet" + id + "/" + v) - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением gauge") - - validStatus := suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - - resp, err = req.Get("value/gauge/testSetGet" + id) - noRespErr = suite.Assert().NoError(err, "Ошибка при попытке сделать запрос для получения значения gauge") - validStatus = suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - equality := suite.Assert().Equalf(v, resp.String(), - "Несоответствие отправленного значения gauge (%s) полученному от сервера (%s), '%s %s'", v, resp.String(), req.Method, req.URL) - - if !noRespErr || !validStatus || !equality { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - } - }) - - suite.Run("get unknown", func() { - id := strconv.Itoa(rand.Intn(256)) - req := httpc.R() - resp, err := req.Get("value/gauge/testUnknown" + id) - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос для получения значения gauge") - validStatus := suite.Assert().Equalf(http.StatusNotFound, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) -} - -func (suite *Iteration3BSuite) TestCounter() { - // create HTTP client without redirects support - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked - }) - - httpc := resty.NewWithClient(&http.Client{ - Transport: &http.Transport{ - DisableCompression: true, - }, - }). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) - - count := 3 - - suite.Run("update sequence", func() { - req := httpc.R() - id := strconv.Itoa(rand.Intn(256)) - resp, err := req.Get("value/counter/testSetGet" + id) - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос для получения значения counter") - - if !noRespErr { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - return - } - - a, _ := strconv.ParseInt(resp.String(), 0, 64) - - for i := 0; i < count; i++ { - v := rand.Intn(1024) - a += int64(v) - resp, err = req.Post("update/counter/testSetGet" + id + "/" + strconv.Itoa(v)) - - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос для обновления значения counter") - validStatus := suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - continue - } - - resp, err := req.Get("value/counter/testSetGet" + id) - noRespErr = suite.Assert().NoError(err, "Ошибка при попытке сделать запрос для получения значения counter") - validStatus = suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - equality := suite.Assert().Equalf(fmt.Sprintf("%d", a), resp.String(), - "Несоответствие отправленного значения counter (%d) полученному от сервера (%s), '%s %s'", a, resp.String(), req.Method, req.URL) - - if !noRespErr || !validStatus || !equality { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - } - }) - - suite.Run("get unknown", func() { - id := strconv.Itoa(rand.Intn(256)) - req := httpc.R() - resp, err := req.Get("value/counter/testUnknown" + id) - noRespErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос для получения значения counter") - validStatus := suite.Assert().Equalf(http.StatusNotFound, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере '%s %s'", req.Method, req.URL) - - if !noRespErr || !validStatus { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - } - }) + e.Errorf("Неожиданная ошибка при поиске использования фреймворка по пути %q, %v", serverSourcePath, err) } diff --git a/cmd/metricstest/main_test.go b/cmd/metricstest/main_test.go index 973564d..1da71d7 100644 --- a/cmd/metricstest/main_test.go +++ b/cmd/metricstest/main_test.go @@ -21,14 +21,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestIteration3A(t *testing.T) { - suite.Run(t, new(Iteration3ASuite)) -} - -func TestIteration3B(t *testing.T) { - suite.Run(t, new(Iteration3BSuite)) -} - func TestIteration4(t *testing.T) { suite.Run(t, new(Iteration4Suite)) } From a8ab42bf875834b25ab827f5e106ca273ff805d4 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 19:51:57 +0300 Subject: [PATCH 09/11] Update fixtures_test.go --- cmd/metricstest/fixtures_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index a1c2508..662c78e 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -61,6 +61,16 @@ func (e *Env) Logf(format string, args ...any) { e.t.Logf(format, args...) } +/// +/// В этих тестах используется библиотека fixenv. Она помогает создавать тестовое окружение. +/// Фикстура - это функция, которая может выполнять фоновую работу, возвращает значение и может выполнять очистку за собой после завершения теста. +/// Вызовы фикстуры идемпотентны, т.е. многократный вызов фикстуры с одними и теми же параметрами внутри одного окружения всегда будет возвращать один и тот же результат. +/// при этом фоновая работа выполняется только при первом вызове. +/// +/// Env - тестовое окружение, которое создаётся для каждого теста. +/// Подробнее о билиотеке можно почитать на странице https://github.com/rekby/fixenv +/// + func ExistPath(e *Env, filePath string) string { return fixenv.Cache(&e.EnvT, filePath, &fixenv.FixtureOptions{ Scope: fixenv.ScopePackage, From 369fa3f2cf12ad523e4470a8f8384da443a13215 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 19:52:37 +0300 Subject: [PATCH 10/11] init iter4 --- cmd/metricstest/fixtures_test.go | 29 +++++++++++++++++++++-------- cmd/metricstest/helpers_test.go | 5 +++++ cmd/metricstest/iteration1_test.go | 22 ++++++++++++---------- cmd/metricstest/iteration3a_test.go | 8 ++++---- cmd/metricstest/iteration4_test.go | 13 +++++++++++++ cmd/metricstest/main_test.go | 4 ---- 6 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 cmd/metricstest/helpers_test.go diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index 662c78e..061702f 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/Yandex-Practicum/go-autotests/internal/fork" + "github.com/Yandex-Practicum/go-autotests/internal/random" "github.com/go-resty/resty/v2" "github.com/rekby/fixenv" "github.com/stretchr/testify/assert" @@ -97,17 +98,28 @@ func ServerFilePath(e *Env) string { return ExistPath(e, flagServerBinaryPath) } +func ConnectToServer(e *Env) *resty.Client { + return RestyClient(e, "http://"+ServerAddress(e)) +} + func ServerAddress(e *Env) string { - return fmt.Sprintf("http://%v:%v", ServerHost(e), ServerPort(e)) + return fixenv.Cache(e, "", nil, func() (string, error) { + res := fmt.Sprintf(ServerHost(e), ServerPort(e)) + e.Logf("Адрес сервера: %q", res) + return res, nil + }) } func ServerHost(e *Env) string { - return flagServerHost + return "localhost" } func ServerPort(e *Env) int { - return fixenv.Cache(e, nil, nil, func() (int, error) { - return strconv.Atoi(flagServerPort) + return fixenv.Cache(e, "", nil, func() (int, error) { + port, err := random.UnusedPort() + e.t.Fatal(err, "Ошибка при определении свободного порта для старта сервера") + e.Logf("Для сервера выбран порт: %v", port) + return port, err }) } @@ -198,7 +210,7 @@ func StartProcess(e *Env, name string, command string, args ...string) *fork.Bac func StartProcessWhichListenPort(e *Env, host string, port int, name string, command string, args ...string) *fork.BackgroundProcess { cacheKey := append([]string{host, strconv.Itoa(port), name, command}, args...) - return fixenv.Cache[*fork.BackgroundProcess](e, cacheKey, nil, func() (*fork.BackgroundProcess, error) { + return fixenv.Cache(e, cacheKey, nil, func() (*fork.BackgroundProcess, error) { process := StartProcess(e, name, command, args...) address := fmt.Sprintf("%v:%v", host, port) return process, waitOpenPort(e, address) @@ -219,12 +231,13 @@ func ServerMock(e *Env, port int) *TestServerT { }) } -func RestyClient(e *Env, host string) *resty.Client { - return fixenv.Cache(e, host, nil, func() (*resty.Client, error) { +func RestyClient(e *Env, baseUrl string) *resty.Client { + return fixenv.Cache(e, baseUrl, nil, func() (*resty.Client, error) { + e.Logf("Создаётся клиент для %q", baseUrl) return resty. New(). SetDebug(true). - SetBaseURL(host). + SetBaseURL(baseUrl). SetRedirectPolicy(resty.NoRedirectPolicy()). SetLogger(restyLogger{e}), nil }) diff --git a/cmd/metricstest/helpers_test.go b/cmd/metricstest/helpers_test.go new file mode 100644 index 0000000..8e61193 --- /dev/null +++ b/cmd/metricstest/helpers_test.go @@ -0,0 +1,5 @@ +package main + +func TestServer(e *Env) { + +} diff --git a/cmd/metricstest/iteration1_test.go b/cmd/metricstest/iteration1_test.go index 00b5905..aa9330d 100644 --- a/cmd/metricstest/iteration1_test.go +++ b/cmd/metricstest/iteration1_test.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "net/http" "testing" @@ -11,7 +12,7 @@ func TestIteration1(t *testing.T) { t.Run("TestCounterHandlers", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/counter/testGauge/100") e.Require.NoError(err, "Ошибка при выполнении запроса") @@ -25,7 +26,7 @@ func TestIteration1(t *testing.T) { t.Run("without-id", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/counter/testGauge/") @@ -36,7 +37,7 @@ func TestIteration1(t *testing.T) { t.Run("bad value", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/gauge/testGauge/bad-value") @@ -49,7 +50,7 @@ func TestIteration1(t *testing.T) { t.Run("TestGaugeHandlers", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/gauge/testGauge/100") e.Require.NoError(err, "Ошибка при выполнении запроса") @@ -63,7 +64,7 @@ func TestIteration1(t *testing.T) { t.Run("without-id", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/gauge/testGauge/") @@ -74,7 +75,7 @@ func TestIteration1(t *testing.T) { t.Run("bad value", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/gauge/testGauge/bad-value") @@ -85,7 +86,7 @@ func TestIteration1(t *testing.T) { t.Run("unexpected path", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() for _, path := range []string{"unknown-path", "unknown-path/gauge/testGauge/100"} { @@ -97,7 +98,7 @@ func TestIteration1(t *testing.T) { t.Run("unknown-metric-type", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) req := c.R() resp, err := req.Post("update/unknown/testGauge/100") e.Require.NoError(err, "Ошибка при выполнении запроса") @@ -106,7 +107,8 @@ func TestIteration1(t *testing.T) { }) } -func DefaultServer(e *Env) *resty.Client { +func ClientForDefaultServer(e *Env) *resty.Client { StartProcessWhichListenPort(e, serverDefaultHost, serverDefaultPort, "metric server", ServerFilePath(e)) - return RestyClient(e, ServerAddress(e)) + address := fmt.Sprintf("http://%v:%v", serverDefaultHost, serverDefaultPort) + return RestyClient(e, address) } diff --git a/cmd/metricstest/iteration3a_test.go b/cmd/metricstest/iteration3a_test.go index 922e24d..42362ca 100644 --- a/cmd/metricstest/iteration3a_test.go +++ b/cmd/metricstest/iteration3a_test.go @@ -10,7 +10,7 @@ func TestIteration3A(t *testing.T) { t.Run("TestCounter", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) resp, err := c.R().Post("update/counter/test1/1") e.NoError(err, "Ошибка при выполнении запроса к серверу") @@ -56,7 +56,7 @@ func TestIteration3A(t *testing.T) { t.Run("unknown-value", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) resp, err := c.R().Get("value/counter/unknown") e.NoError(err, "Ошибка при выполнении запроса к серверу") e.Equal(http.StatusNotFound, resp.StatusCode(), "При запросе неизвестного значения должен возвращаться код 404 (http.StatusNotFound)") @@ -66,7 +66,7 @@ func TestIteration3A(t *testing.T) { t.Run("TestGauge", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) resp, err := c.R().Post("update/gauge/test1/1.5") e.NoError(err, "Ошибка при выполнении запроса к серверу") @@ -112,7 +112,7 @@ func TestIteration3A(t *testing.T) { t.Run("unknown-value", func(t *testing.T) { e := New(t) - c := DefaultServer(e) + c := ClientForDefaultServer(e) resp, err := c.R().Get("value/gauge/unknown") e.NoError(err, "Ошибка при выполнении запроса к серверу") e.Equal(http.StatusNotFound, resp.StatusCode(), "При запросе неизвестного значения должен возвращаться код 404 (http.StatusNotFound)") diff --git a/cmd/metricstest/iteration4_test.go b/cmd/metricstest/iteration4_test.go index 28dd113..d8aac4b 100644 --- a/cmd/metricstest/iteration4_test.go +++ b/cmd/metricstest/iteration4_test.go @@ -8,6 +8,7 @@ import ( "os" "strconv" "syscall" + "testing" "time" "github.com/go-resty/resty/v2" @@ -16,6 +17,18 @@ import ( "github.com/Yandex-Practicum/go-autotests/internal/fork" ) +func TestIteration4(t *testing.T) { + t.Run("server", func(t *testing.T) { + e := New(t) + StartProcessWhichListenPort(e, ServerHost(e), ServerPort(e), "server", ServerFilePath(e), + "-a="+ServerAddress(e), + ) + + client := ConnectToServer(e) + + }) +} + type Iteration4Suite struct { suite.Suite diff --git a/cmd/metricstest/main_test.go b/cmd/metricstest/main_test.go index 1da71d7..64975d8 100644 --- a/cmd/metricstest/main_test.go +++ b/cmd/metricstest/main_test.go @@ -21,10 +21,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestIteration4(t *testing.T) { - suite.Run(t, new(Iteration4Suite)) -} - func TestIteration5(t *testing.T) { suite.Run(t, new(Iteration5Suite)) } From b26e5a388d803715b76dc8ef0ceac794b07f245c Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Sun, 9 Apr 2023 21:31:00 +0300 Subject: [PATCH 11/11] iter4, test full func with arg params --- cmd/metricstest/fixtures_test.go | 57 +++- cmd/metricstest/helpers_test.go | 5 - cmd/metricstest/iteration1_test.go | 35 ++- cmd/metricstest/iteration2a_test.go | 121 ++------ cmd/metricstest/iteration3a_test.go | 13 +- cmd/metricstest/iteration4_test.go | 439 ++-------------------------- 6 files changed, 118 insertions(+), 552 deletions(-) delete mode 100644 cmd/metricstest/helpers_test.go diff --git a/cmd/metricstest/fixtures_test.go b/cmd/metricstest/fixtures_test.go index 061702f..7509b37 100644 --- a/cmd/metricstest/fixtures_test.go +++ b/cmd/metricstest/fixtures_test.go @@ -30,10 +30,10 @@ type Env struct { Ctx context.Context Require require.Assertions - t testing.TB + t *testing.T } -func New(t testing.TB) *Env { +func New(t *testing.T) *Env { ctx, ctxCancel := context.WithCancel(context.Background()) t.Cleanup(ctxCancel) @@ -62,6 +62,10 @@ func (e *Env) Logf(format string, args ...any) { e.t.Logf(format, args...) } +func (e *Env) Test() *testing.T { + return e.t +} + /// /// В этих тестах используется библиотека fixenv. Она помогает создавать тестовое окружение. /// Фикстура - это функция, которая может выполнять фоновую работу, возвращает значение и может выполнять очистку за собой после завершения теста. @@ -94,6 +98,32 @@ func AgentFilePath(e *Env) string { return ExistPath(e, flagAgentBinaryPath) } +func AgentPollInterval(e *Env, setInterval ...time.Duration) time.Duration { + return fixenv.Cache(e, "", &fixenv.FixtureOptions{Scope: fixenv.ScopeTestAndSubtests}, func() (time.Duration, error) { + switch len(setInterval) { + case 0: + return time.Second, nil + case 1: + return setInterval[0], nil + default: + return 0, fmt.Errorf("В опциональном параметре можно передать максимум одно значение") + } + }) +} + +func AgentReportInterval(e *Env, setInterval ...time.Duration) time.Duration { + return fixenv.Cache(e, "", &fixenv.FixtureOptions{Scope: fixenv.ScopeTestAndSubtests}, func() (time.Duration, error) { + switch len(setInterval) { + case 0: + return 2 * time.Second, nil + case 1: + return setInterval[0], nil + default: + return 0, fmt.Errorf("В опциональном параметре можно передать максимум одно значение") + } + }) +} + func ServerFilePath(e *Env) string { return ExistPath(e, flagServerBinaryPath) } @@ -104,7 +134,7 @@ func ConnectToServer(e *Env) *resty.Client { func ServerAddress(e *Env) string { return fixenv.Cache(e, "", nil, func() (string, error) { - res := fmt.Sprintf(ServerHost(e), ServerPort(e)) + res := fmt.Sprintf("%v:%v", ServerHost(e), ServerPort(e)) e.Logf("Адрес сервера: %q", res) return res, nil }) @@ -114,10 +144,23 @@ func ServerHost(e *Env) string { return "localhost" } -func ServerPort(e *Env) int { - return fixenv.Cache(e, "", nil, func() (int, error) { - port, err := random.UnusedPort() - e.t.Fatal(err, "Ошибка при определении свободного порта для старта сервера") +func ServerPort(e *Env, setPort ...int) int { + return fixenv.Cache(e, "", &fixenv.FixtureOptions{Scope: fixenv.ScopeTestAndSubtests}, func() (int, error) { + port := 0 + var err error + switch len(setPort) { + case 0: + e.Logf("Автоматический выбор серверного порта") + port, err = random.UnusedPort() + if err != nil { + return 0, err + } + case 1: + port = setPort[0] + e.Logf("Серверный порт задан вручную") + default: + return 0, fmt.Errorf("В опциональном параметре можно передать максимум одно значение") + } e.Logf("Для сервера выбран порт: %v", port) return port, err }) diff --git a/cmd/metricstest/helpers_test.go b/cmd/metricstest/helpers_test.go deleted file mode 100644 index 8e61193..0000000 --- a/cmd/metricstest/helpers_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func TestServer(e *Env) { - -} diff --git a/cmd/metricstest/iteration1_test.go b/cmd/metricstest/iteration1_test.go index aa9330d..569a8fa 100644 --- a/cmd/metricstest/iteration1_test.go +++ b/cmd/metricstest/iteration1_test.go @@ -4,15 +4,24 @@ import ( "fmt" "net/http" "testing" - - "github.com/go-resty/resty/v2" ) func TestIteration1(t *testing.T) { + testServerIncrement1(t, StartDefaultServer) +} + +func StartDefaultServer(e *Env) string { + StartProcessWhichListenPort(e, serverDefaultHost, serverDefaultPort, "metric server", ServerFilePath(e)) + return fmt.Sprintf("http://%v:%v", serverDefaultHost, serverDefaultPort) +} + +// testServerIncrement1 тестирует функционал первого инкремента с кастомной функцией старта сервера. +// нужна, чтобы продолжать тестировать этот функционал в следующих инкрементах с переменой условий - другие порты, другие хранилища +func testServerIncrement1(t *testing.T, startServer func(e *Env) string) { t.Run("TestCounterHandlers", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/counter/testGauge/100") e.Require.NoError(err, "Ошибка при выполнении запроса") @@ -26,7 +35,7 @@ func TestIteration1(t *testing.T) { t.Run("without-id", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/counter/testGauge/") @@ -37,7 +46,7 @@ func TestIteration1(t *testing.T) { t.Run("bad value", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/gauge/testGauge/bad-value") @@ -50,7 +59,7 @@ func TestIteration1(t *testing.T) { t.Run("TestGaugeHandlers", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/gauge/testGauge/100") e.Require.NoError(err, "Ошибка при выполнении запроса") @@ -64,7 +73,7 @@ func TestIteration1(t *testing.T) { t.Run("without-id", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/gauge/testGauge/") @@ -75,7 +84,7 @@ func TestIteration1(t *testing.T) { t.Run("bad value", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/gauge/testGauge/bad-value") @@ -86,7 +95,7 @@ func TestIteration1(t *testing.T) { t.Run("unexpected path", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() for _, path := range []string{"unknown-path", "unknown-path/gauge/testGauge/100"} { @@ -98,7 +107,7 @@ func TestIteration1(t *testing.T) { t.Run("unknown-metric-type", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) req := c.R() resp, err := req.Post("update/unknown/testGauge/100") e.Require.NoError(err, "Ошибка при выполнении запроса") @@ -106,9 +115,3 @@ func TestIteration1(t *testing.T) { "При попытке обновления метрики неизвестного типа сервер должен вернуть ошибку http.StatusBadRequest или http.StatusNotFound (400, 404)") }) } - -func ClientForDefaultServer(e *Env) *resty.Client { - StartProcessWhichListenPort(e, serverDefaultHost, serverDefaultPort, "metric server", ServerFilePath(e)) - address := fmt.Sprintf("http://%v:%v", serverDefaultHost, serverDefaultPort) - return RestyClient(e, address) -} diff --git a/cmd/metricstest/iteration2a_test.go b/cmd/metricstest/iteration2a_test.go index 7277cf2..0250ed5 100644 --- a/cmd/metricstest/iteration2a_test.go +++ b/cmd/metricstest/iteration2a_test.go @@ -1,21 +1,26 @@ package main import ( - "context" - "errors" - "os" - "syscall" "testing" "time" - - "github.com/stretchr/testify/suite" - - "github.com/Yandex-Practicum/go-autotests/internal/fork" ) func TestIteration2A(t *testing.T) { e := New(t) - serverMock := ServerMock(e, serverDefaultPort) + ServerPort(e, serverDefaultPort) + AgentReportInterval(e, agentDefaultReportInterval) + AgentPollInterval(e, agentDefaultPollInterval) + testAgentIncrement2(e, StartDefaultAgent) +} + +func StartDefaultAgent(e *Env) { + StartProcess(e, "agent", AgentFilePath(e)) +} + +func testAgentIncrement2(e *Env, startAgent func(e *Env)) { + e.Logf("Тестирование функционала второго инкремента") + + serverMock := ServerMock(e, ServerPort(e)) gauges := []string{ "Alloc", "BuckHashSys", "Frees", "GCCPUFraction", "GCSys", "HeapAlloc", "HeapIdle", "HeapInuse", "HeapObjects", "HeapReleased", "HeapSys", "LastGC", "Lookups", "MCacheInuse", "MCacheSys", "MSpanInuse", "MSpanSys", "Mallocs", "NextGC", @@ -25,103 +30,23 @@ func TestIteration2A(t *testing.T) { "PollCount", } - StartDefaultAgent(e) + startAgent(e) - firstIterationTimeout := agentDefaultReportInterval + agentDefaultReportInterval/2 + reportInterval := AgentReportInterval(e) + pollInterval := AgentPollInterval(e) + firstIterationTimeout := reportInterval + reportInterval/2 e.Logf("Жду %v", firstIterationTimeout) time.Sleep(firstIterationTimeout) serverMock.CheckReceiveValues(gauges, counters, 1, 2) firstRandom := serverMock.GetLastGauge("RandomValue") - e.InDelta(int(agentDefaultReportInterval/agentDefaultPollInterval), serverMock.GetLastCounter("PollCount"), 1) + e.InDelta(int(reportInterval/pollInterval), serverMock.GetLastCounter("PollCount"), 1) - e.Logf("Жду ещё %v", agentDefaultReportInterval) - time.Sleep(agentDefaultReportInterval) + e.Logf("Жду ещё %v", reportInterval) + time.Sleep(reportInterval) serverMock.CheckReceiveValues(gauges, counters, 2, 3) - e.InDelta(agentDefaultReportInterval/agentDefaultPollInterval*2, serverMock.GetLastCounter("PollCount"), 1) - e.NotEqual(firstRandom, serverMock.GetLastGauge("RandomValue")) -} - -func StartDefaultAgent(e *Env) { - StartProcess(e, "agent", AgentFilePath(e)) -} - -type Iteration2ASuite struct { - suite.Suite - - agentAddress string - agentProcess *fork.BackgroundProcess -} - -func (suite *Iteration2ASuite) SetupSuite() { - // check required flags - suite.Require().NotEmpty(flagAgentBinaryPath, "-agent-binary-path non-empty flag required") - - suite.agentAddress = "http://localhost:8080" - - envs := append(os.Environ(), []string{ - "RESTORE=false", - }...) - p := fork.NewBackgroundProcess(context.Background(), flagAgentBinaryPath, - fork.WithEnv(envs...), - ) - - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - - err := p.Start(ctx) - if err != nil { - suite.T().Errorf("Невозможно запустить процесс командой %s: %s. Переменные окружения: %+v", p, err, envs) - return - } - - port := "8080" - err = p.ListenPort(ctx, "tcp", port) - if err != nil { - suite.T().Errorf("Не удалось дождаться пока на порт %s начнут поступать данные: %s", port, err) - return - } - - suite.agentProcess = p -} - -func (suite *Iteration2ASuite) TearDownSuite() { - if suite.agentProcess == nil { - return - } - - exitCode, err := suite.agentProcess.Stop(syscall.SIGINT, syscall.SIGKILL) - if err != nil { - if errors.Is(err, os.ErrProcessDone) { - return - } - suite.T().Logf("Не удалось остановить процесс с помощью сигнала ОС: %s", err) - return - } - - if exitCode > 0 { - suite.T().Logf("Процесс завершился с не нулевым статусом %d", exitCode) - } - - // try to read stdout/stderr - ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) - defer cancel() - - out := suite.agentProcess.Stderr(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDERR лог процесса:\n\n%s", string(out)) - } - out = suite.agentProcess.Stdout(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDOUT лог процесса:\n\n%s", string(out)) - } -} - -// TestAgent проверяет -// агент успешно стартует и передает какие-то данные по tcp, на 127.0.0.1:8080 -func (suite *Iteration2ASuite) TestAgent() { - suite.Run("receive data from agent", func() { - }) + e.InDelta(reportInterval/pollInterval*2, serverMock.GetLastCounter("PollCount"), 1) + e.NotEqual(firstRandom, serverMock.GetLastGauge("RandomValue"), "Случайное значение не поменялось при повторной отправке") } diff --git a/cmd/metricstest/iteration3a_test.go b/cmd/metricstest/iteration3a_test.go index 42362ca..788b9fe 100644 --- a/cmd/metricstest/iteration3a_test.go +++ b/cmd/metricstest/iteration3a_test.go @@ -7,10 +7,15 @@ import ( ) func TestIteration3A(t *testing.T) { + testServerIncrement3(t, StartDefaultServer) +} + +func testServerIncrement3(t *testing.T, startServer func(e *Env) string) { + t.Log("Тестирование функционала 3го спринта") t.Run("TestCounter", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) resp, err := c.R().Post("update/counter/test1/1") e.NoError(err, "Ошибка при выполнении запроса к серверу") @@ -56,7 +61,7 @@ func TestIteration3A(t *testing.T) { t.Run("unknown-value", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) resp, err := c.R().Get("value/counter/unknown") e.NoError(err, "Ошибка при выполнении запроса к серверу") e.Equal(http.StatusNotFound, resp.StatusCode(), "При запросе неизвестного значения должен возвращаться код 404 (http.StatusNotFound)") @@ -66,7 +71,7 @@ func TestIteration3A(t *testing.T) { t.Run("TestGauge", func(t *testing.T) { t.Run("ok", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) resp, err := c.R().Post("update/gauge/test1/1.5") e.NoError(err, "Ошибка при выполнении запроса к серверу") @@ -112,7 +117,7 @@ func TestIteration3A(t *testing.T) { t.Run("unknown-value", func(t *testing.T) { e := New(t) - c := ClientForDefaultServer(e) + c := RestyClient(e, startServer(e)) resp, err := c.R().Get("value/gauge/unknown") e.NoError(err, "Ошибка при выполнении запроса к серверу") e.Equal(http.StatusNotFound, resp.StatusCode(), "При запросе неизвестного значения должен возвращаться код 404 (http.StatusNotFound)") diff --git a/cmd/metricstest/iteration4_test.go b/cmd/metricstest/iteration4_test.go index d8aac4b..f8b6f50 100644 --- a/cmd/metricstest/iteration4_test.go +++ b/cmd/metricstest/iteration4_test.go @@ -1,438 +1,33 @@ package main import ( - "context" - "errors" - "math/rand" - "net/http" - "os" - "strconv" - "syscall" + "fmt" "testing" "time" - - "github.com/go-resty/resty/v2" - "github.com/stretchr/testify/suite" - - "github.com/Yandex-Practicum/go-autotests/internal/fork" ) func TestIteration4(t *testing.T) { + _ = New(t) t.Run("server", func(t *testing.T) { - e := New(t) - StartProcessWhichListenPort(e, ServerHost(e), ServerPort(e), "server", ServerFilePath(e), - "-a="+ServerAddress(e), - ) - - client := ConnectToServer(e) - + testServerIncrement1(t, StartServerWithArgs) + testServerIncrement3(t, StartServerWithArgs) }) -} - -type Iteration4Suite struct { - suite.Suite - - serverAddress string - serverPort string - serverProcess *fork.BackgroundProcess - serverArgs []string - agentProcess *fork.BackgroundProcess - agentArgs []string - // knownPgLibraries []string - - rnd *rand.Rand - envs []string -} -func (suite *Iteration4Suite) SetupSuite() { - // check required flags - suite.Require().NotEmpty(flagTargetSourcePath, "-source-path non-empty flag required") - suite.Require().NotEmpty(flagServerBinaryPath, "-binary-path non-empty flag required") - suite.Require().NotEmpty(flagAgentBinaryPath, "-agent-binary-path non-empty flag required") - suite.Require().NotEmpty(flagServerPort, "-server-port non-empty flag required") - suite.Require().NotEmpty(flagFileStoragePath, "-file-storage-path non-empty flag required") - suite.Require().NotEmpty(flagDatabaseDSN, "-database-dsn non-empty flag required") - - suite.rnd = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) - // suite.knownPgLibraries = []string{ - // "database/sql", - // "github.com/jackc/pgx", - // "github.com/lib/pq", - // } - suite.serverAddress = "http://localhost:" + flagServerPort - suite.serverPort = flagServerPort - - suite.envs = append(os.Environ(), []string{ - "STORE_INTERVAL=1s", - "RESTORE=true", - "DATABASE_DSN=" + flagDatabaseDSN, - }...) - - suite.agentArgs = []string{ - "-a=localhost:" + flagServerPort, - "-r=2s", - "-p=1s", - } - suite.serverArgs = []string{ - "-a=localhost:" + flagServerPort, - // "-s=5s", - "-r=false", - "-i=5m", - "-f=" + flagFileStoragePath, - } - - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - suite.agentUp(ctx, suite.envs, suite.agentArgs, flagServerPort) - suite.serverUp(ctx, suite.envs, suite.serverArgs, flagServerPort) -} - -func (suite *Iteration4Suite) serverUp(ctx context.Context, envs, args []string, port string) { - p := fork.NewBackgroundProcess(context.Background(), flagServerBinaryPath, - fork.WithEnv(envs...), - fork.WithArgs(args...), - ) - - err := p.Start(ctx) - if err != nil { - suite.T().Errorf("Невозможно запустить процесс командой %q: %s. Переменные окружения: %+v, флаги командной строки: %+v", p, err, envs, args) - return - } - - err = p.WaitPort(ctx, "tcp", port) - if err != nil { - suite.T().Errorf("Не удалось дождаться пока порт %s станет доступен для запроса: %s", port, err) - return - } - suite.serverProcess = p -} - -func (suite *Iteration4Suite) agentUp(ctx context.Context, envs, args []string, port string) { - p := fork.NewBackgroundProcess(context.Background(), flagAgentBinaryPath, - fork.WithEnv(envs...), - fork.WithArgs(args...), - ) - - err := p.Start(ctx) - if err != nil { - suite.T().Errorf("Невозможно запустить процесс командой %q: %s. Переменные окружения: %+v, флаги командной строки: %+v", p, err, envs, args) - return - } - - err = p.ListenPort(ctx, "tcp", port) - if err != nil { - suite.T().Errorf("Не удалось дождаться пока на порт %s начнут поступать данные: %s", port, err) - return - } - suite.agentProcess = p -} - -func (suite *Iteration4Suite) TearDownSuite() { - suite.agentShutdown() - suite.serverShutdown() -} - -func (suite *Iteration4Suite) serverShutdown() { - if suite.serverProcess == nil { - return - } - - exitCode, err := suite.serverProcess.Stop(syscall.SIGINT, syscall.SIGKILL) - if err != nil { - if errors.Is(err, os.ErrProcessDone) { - return - } - suite.T().Logf("Не удалось остановить процесс с помощью сигнала ОС: %s", err) - return - } - - if exitCode > 0 { - suite.T().Logf("Процесс завершился с не нулевым статусом %d", exitCode) - } - - // try to read stdout/stderr - ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) - defer cancel() - - out := suite.serverProcess.Stderr(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDERR лог процесса:\n\n%s", string(out)) - } - out = suite.serverProcess.Stdout(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDOUT лог процесса:\n\n%s", string(out)) - } -} - -func (suite *Iteration4Suite) agentShutdown() { - if suite.agentProcess == nil { - return - } - - exitCode, err := suite.agentProcess.Stop(syscall.SIGINT, syscall.SIGKILL) - if err != nil { - if errors.Is(err, os.ErrProcessDone) { - return - } - suite.T().Logf("Не удалось остановить процесс с помощью сигнала ОС: %s", err) - return - } - - if exitCode > 0 { - suite.T().Logf("Процесс завершился с не нулевым статусом %d", exitCode) - } - - // try to read stdout/stderr - ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) - defer cancel() - - out := suite.agentProcess.Stderr(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDERR лог процесса:\n\n%s", string(out)) - } - out = suite.agentProcess.Stdout(ctx) - if len(out) > 0 { - suite.T().Logf("Получен STDOUT лог процесса:\n\n%s", string(out)) - } -} - -func (suite *Iteration4Suite) TestCounterHandlers() { - // create HTTP client without redirects support - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked - }) - httpc := resty.New(). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) - - id := "GetSet" + strconv.Itoa(suite.rnd.Intn(256)) - var storage int64 - - suite.Run("update", func() { - value1, value2 := int64(suite.rnd.Int31()), int64(suite.rnd.Int31()) - req := httpc.R(). - SetHeader("Content-Type", "application/json") - - // Вдруг на сервере уже есть значение, на всякий случай запросим. - var result Metrics - resp, err := req. - SetBody(&Metrics{ - ID: id, - MType: "counter", - }). - SetResult(&result). - Post("value/") - - dumpErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с получением значения counter") - var value0 int64 - switch resp.StatusCode() { - case http.StatusOK: - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Containsf(resp.Header().Get("Content-Type"), "application/json", - "Заголовок ответа Content-Type содержит несоответствующее значение") - dumpErr = dumpErr && suite.NotNil(result.Delta, - "Получено не инициализированное значение Delta '%q %s'", req.Method, req.URL) - value0 = *result.Delta - case http.StatusNotFound: - default: - dumpErr = false - suite.T().Fatalf("Несоответствие статус кода %d ответа ожидаемому http.StatusNotFound или http.StatusOK в хендлере %q: %q", resp.StatusCode(), req.Method, req.URL) - return - } - - resp, err = req. - SetBody(&Metrics{ - ID: id, - MType: "counter", - Delta: &value1, - }). - Post("update/") - dumpErr = dumpErr && suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением counter") - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - - resp, err = req. - SetBody(&Metrics{ - ID: id, - MType: "counter", - Delta: &value2, - }). - Post("update/") - dumpErr = dumpErr && suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением counter") - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - - resp, err = req. - SetBody(&Metrics{ - ID: id, - MType: "counter", - }). - SetResult(&result). - Post("value/") - - dumpErr = dumpErr && suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с получением значения counter") - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Containsf(resp.Header().Get("Content-Type"), "application/json", - "Заголовок ответа Content-Type содержит несоответствующее значение") - dumpErr = dumpErr && suite.NotNil(result.Delta, - "Несоответствие отправленного значения counter (%d) полученному от сервера (nil), '%q %s'", value0+value1+value2, req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Equalf(value0+value1+value2, *result.Delta, - "Несоответствие отправленного значения counter (%d) полученному от сервера (%d), '%q %s'", value0+value1+value2, *result.Delta, req.Method, req.URL) - - if !dumpErr { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - dump = dumpResponse(resp.RawResponse, true) - suite.T().Logf("Оригинальный ответ:\n\n%s", dump) - } - - storage = value0 + value1 + value2 - }) - - suite.Run("restart server", func() { - time.Sleep(5 * time.Second) // relax time - suite.serverShutdown() - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - suite.serverUp(ctx, suite.envs, suite.serverArgs, suite.serverPort) - }) - - suite.Run("get", func() { - req := httpc.R(). - SetHeader("Content-Type", "application/json") - - // Вдруг на сервере уже есть значение, на всякий случай запросим. - var result Metrics - resp, err := req. - SetBody(&Metrics{ - ID: id, - MType: "counter", - }). - SetResult(&result). - Post("value/") - - dumpErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с получением значения counter") - - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Containsf(resp.Header().Get("Content-Type"), "application/json", - "Заголовок ответа Content-Type содержит несоответствующее значение") - dumpErr = dumpErr && suite.NotNil(result.Delta, - "Получено не инициализированное значение Delta '%q %s'", req.Method, req.URL) - dumpErr = dumpErr && suite.NotNil(result.Delta, - "Несоответствие ожидаемого значения counter (%d) полученному от сервера (nil), '%q %s'", storage, req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Equalf(storage, *result.Delta, - "Несоответствие ожидаемого значения counter (%d) полученному от сервера (%d), '%q %s'", storage, *result.Delta, req.Method, req.URL) - - if !dumpErr { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - dump = dumpResponse(resp.RawResponse, true) - suite.T().Logf("Оригинальный ответ:\n\n%s", dump) - } + t.Run("agent", func(t *testing.T) { + e := New(t) + testAgentIncrement2(e, StartAgentWithArgs) }) } -func (suite *Iteration4Suite) TestGaugeHandlers() { - errRedirectBlocked := errors.New("HTTP redirect blocked") - redirPolicy := resty.RedirectPolicyFunc(func(_ *http.Request, _ []*http.Request) error { - return errRedirectBlocked - }) - httpc := resty.New(). - SetHostURL(suite.serverAddress). - SetRedirectPolicy(redirPolicy) - - id := "GetSet" + strconv.Itoa(suite.rnd.Intn(256)) - var storage float64 - - suite.Run("update", func() { - value := suite.rnd.Float64() * 1e6 - req := httpc.R(). - SetHeader("Content-Type", "application/json") - - resp, err := req. - SetBody(&Metrics{ - ID: id, - MType: "gauge", - Value: &value, - }). - Post("update/") - dumpErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с обновлением gauge") - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - - var result Metrics - resp, err = req. - SetBody(&Metrics{ - ID: id, - MType: "gauge", - }). - SetResult(&result). - Post("value/") - - dumpErr = dumpErr && suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с получением значения gauge") - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Containsf(resp.Header().Get("Content-Type"), "application/json", - "Заголовок ответа Content-Type содержит несоответствующее значение") - dumpErr = dumpErr && suite.Assert().NotEqualf(nil, result.Value, - "Несоответствие отправленного значения gauge (%f) полученному от сервера (nil), '%q %s'", value, req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Equalf(value, *result.Value, - "Несоответствие отправленного значения gauge (%f) полученному от сервера (%f), '%q %s'", value, *result.Value, req.Method, req.URL) - - if !dumpErr { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - dump = dumpResponse(resp.RawResponse, true) - suite.T().Logf("Оригинальный ответ:\n\n%s", dump) - } - if result.Value != nil { - storage = *result.Value - } - }) - - suite.Run("restart server", func() { - time.Sleep(5 * time.Second) // relax time - suite.serverShutdown() - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - suite.serverUp(ctx, suite.envs, suite.serverArgs, suite.serverPort) - }) - - suite.Run("get", func() { - req := httpc.R(). - SetHeader("Content-Type", "application/json") - - // Вдруг на сервере уже есть значение, на всякий случай запросим. - var result Metrics - resp, err := req. - SetBody(&Metrics{ - ID: id, - MType: "gauge", - }). - SetResult(&result). - Post("value/") - - dumpErr := suite.Assert().NoError(err, "Ошибка при попытке сделать запрос с получением значения gauge") - dumpErr = dumpErr && suite.Assert().Equalf(http.StatusOK, resp.StatusCode(), - "Несоответствие статус кода ответа ожидаемому в хендлере %q: %q ", req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Containsf(resp.Header().Get("Content-Type"), "application/json", - "Заголовок ответа Content-Type содержит несоответствующее значение") - dumpErr = dumpErr && suite.Assert().NotEqualf(nil, result.Value, - "Несоответствие ожидаемого значения gauge (%f) полученному от сервера (nil), '%q %s'", storage, req.Method, req.URL) - dumpErr = dumpErr && suite.Assert().Equalf(storage, *result.Value, - "Несоответствие ожидаемого значения gauge (%f) полученному от сервера (%f), '%q %s'", storage, *result.Value, req.Method, req.URL) +func StartServerWithArgs(e *Env) string { + serverArg := "-a=" + ServerAddress(e) + StartProcessWhichListenPort(e, ServerHost(e), ServerPort(e), "server", ServerFilePath(e), serverArg) + return "http://" + ServerAddress(e) +} - if !dumpErr { - dump := dumpRequest(req.RawRequest, true) - suite.T().Logf("Оригинальный запрос:\n\n%s", dump) - dump = dumpResponse(resp.RawResponse, true) - suite.T().Logf("Оригинальный ответ:\n\n%s", dump) - } - }) +func StartAgentWithArgs(e *Env) { + serverArg := "-a=" + ServerAddress(e) + pollIntervalArg := fmt.Sprintf("-p=%v", int(AgentPollInterval(e)/time.Second)) + reportIntervalArg := fmt.Sprintf("-r=%v", int(AgentReportInterval(e)/time.Second)) + StartProcess(e, "agent", AgentFilePath(e), serverArg, pollIntervalArg, reportIntervalArg) }