Files
pipu-music/services/api/internal/system/handler_test.go
T
mrleo1nidandClaude Opus 5.5 4fa1add80d
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 48s
ci / api (push) Successful in 51s
ci / recommender (push) Canceled after 2s
ci / ts (packages + web) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Canceled after 0s
ci / mobile (Metro-бандл Android) (push) Canceled after 0s
fix: address project review findings
Security and robustness:
- storage-agent rejects an empty INTERNAL_TOKEN (config notEmpty + middleware)
- api takes the client IP only from X-Real-IP (chi RealIP trusted True-Client-IP)
- /readyz reports ok/fail and logs the cause; checks run in parallel with a timeout
- storage-agent shuts down gracefully; shared httpx.NewServer/Serve with IdleTimeout
- goose migrations take a Postgres advisory lock
- recommender readyz: per-check timeout, redis socket timeouts

Clients:
- Android release allows cleartext HTTP for LAN servers (expo-build-properties)
- checkServer times out after 10s; connect button is disabled while checking

Deploy and CI:
- install.sh hands cache/uploads/updates/models to the non-root service users
- update.sh refuses downgrades unless PIPU_ALLOW_DOWNGRADE=1
- recommender/analyzer get only the env they need; required secrets fail fast
- nginx security headers and CSP; strip True-Client-IP
- dev compose binds ports to 127.0.0.1; postgres/redis images pinned
- version.mjs fails on a malformed explicit tag instead of releasing 0.0.0-dev
- contract check catches untracked generated files
- final releases fail without a release keystore; changelog skips rc tags

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:46:46 +03:00

82 lines
2.5 KiB
Go

package system_test
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"pipu/api/internal/server"
"pipu/api/internal/system"
)
func newServer(checks map[string]system.Checker) http.Handler {
h := system.NewHandler(system.Info{
Version: "1.2.3",
SupportedLocales: []string{"ru", "en"},
MinAndroidVersionCode: 10203,
}, nil, checks)
return server.NewRouter(server.Deps{System: h})
}
func get(t *testing.T, h http.Handler, path string) (*httptest.ResponseRecorder, map[string]any) {
t.Helper()
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil))
var body map[string]any
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body), rec.Body.String())
return rec, body
}
func TestServerInfo(t *testing.T) {
rec, body := get(t, newServer(nil), "/api/v1/server-info")
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "pipu-music", body["name"])
assert.Equal(t, "1.2.3", body["version"])
assert.Equal(t, "v1", body["api_version"])
assert.Equal(t, []any{"ru", "en"}, body["supported_locales"])
assert.EqualValues(t, 10203, body["min_android_version_code"])
}
func TestHealthz(t *testing.T) {
rec, body := get(t, newServer(nil), "/healthz")
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "ok", body["status"])
}
func TestReadyz(t *testing.T) {
ok := system.CheckerFunc(func(context.Context) error { return nil })
down := system.CheckerFunc(func(context.Context) error { return errors.New("connection refused") })
t.Run("all dependencies up", func(t *testing.T) {
rec, body := get(t, newServer(map[string]system.Checker{"postgres": ok, "redis": ok}), "/readyz")
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "ok", body["status"])
assert.Equal(t, map[string]any{"postgres": "ok", "redis": "ok"}, body["checks"])
})
t.Run("one dependency down", func(t *testing.T) {
rec, body := get(t, newServer(map[string]system.Checker{"postgres": ok, "redis": down}), "/readyz")
assert.Equal(t, http.StatusServiceUnavailable, rec.Code)
assert.Equal(t, "degraded", body["status"])
assert.Equal(t, map[string]any{"postgres": "ok", "redis": "fail"}, body["checks"])
})
}
func TestUnknownRouteIsProblemJSON(t *testing.T) {
rec, body := get(t, newServer(nil), "/api/v1/nope")
assert.Equal(t, http.StatusNotFound, rec.Code)
assert.Equal(t, "application/problem+json", rec.Header().Get("Content-Type"))
assert.Equal(t, "common.not_found", body["code"])
}