Files
IoT/internal/service/api/handler/namespace_test.go
T

79 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// namespace_test.go — проверяет, что ВСЕ эндпоинты с {namespace} валидируют его
// одинаково (400), а не пропускают «мусор» до хранилища.
//
// Найдено e2e-прогоном 2026-10-11: валидация была только в CreateIoTDevice,
// поэтому GET-список с `bad_ns` отдавал 200, а чтение телеметрии падало уже в
// хранилище (safeNamespaceRe) с 500. Здесь — регрессия на все обработчики.
package handler
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gorilla/mux"
"gitea.services.ngcloud.ru/Nail/IoT/internal/storage/iotpg"
)
// badNamespaces — значения, которые ломают MQTT-топик/username или имя БД
// tenant_<ns>: подчёркивание (коллизия с дефисом), wildcard, слэш, пробел,
// ведущий дефис, юникод, пустой.
var badNamespaces = []string{"bad_ns", "a+b", "a#b", "a/b", "-abc", "abc def", "имя", ""}
func newReq(t *testing.T, ns string) *http.Request {
t.Helper()
req := httptest.NewRequest(http.MethodGet, "/v1/namespaces/x/iot/devices", nil)
return mux.SetURLVars(req, map[string]string{"namespace": ns, "name": "dev1"})
}
func TestHandlersRejectInvalidNamespace(t *testing.T) {
// Пустой IoTPG-стаб: до хранилища дело дойти не должно (валидация раньше),
// поэтому nil-зависимости Devices/IoTPG не используются.
h := &Handler{IoTPG: &iotpg.IoTPostgresStore{}}
handlers := map[string]func(http.ResponseWriter, *http.Request){
"ListIoTDevices": h.ListIoTDevices,
"GetIoTDevice": h.GetIoTDevice,
"DeleteIoTDevice": h.DeleteIoTDevice,
"UpdateIoTDevice": h.UpdateIoTDevice,
"ListIoTTelemetry": h.ListIoTTelemetry,
}
for name, fn := range handlers {
for _, ns := range badNamespaces {
if ns == "" {
continue // пустой namespace = "default" (валидный) — проверяется отдельно
}
t.Run(name+"/"+ns, func(t *testing.T) {
rec := httptest.NewRecorder()
fn(rec, newReq(t, ns))
if rec.Code != http.StatusBadRequest {
t.Fatalf("код %d, ожидался 400 (body=%s)", rec.Code, rec.Body.String())
}
if !strings.Contains(rec.Body.String(), "namespace") {
t.Errorf("в теле нет упоминания namespace: %s", rec.Body.String())
}
})
}
}
}
func TestEmptyNamespaceMeansDefault(t *testing.T) {
// Пустой {namespace} в пути → "default" (валидное значение), поэтому
// проверка должна пройти дальше валидации (здесь упадёт на nil Devices —
// это ожидаемо, ловим панику и убеждаемся, что это НЕ 400).
defer func() {
if r := recover(); r == nil {
t.Log("обработчик дошёл до зависимостей без паники")
}
}()
rec := httptest.NewRecorder()
h := &Handler{}
h.ListIoTDevices(rec, newReq(t, ""))
if rec.Code == http.StatusBadRequest {
t.Fatalf("пустой namespace не должен давать 400 (body=%s)", rec.Body.String())
}
}