httpapi: точки метрики за период отдаются одним запросом

- `GET /api/v1/metrics/{name}?from&to&layer` — ряд точек за период; конверт
  объявляет слой, измеренный род, его применимость к отданному ряду и границу
  окна измерения, а сам ряд собирается из одного слоя, выбранного по охвату
  точек внутри периода
- use-case вынесен в `internal/points`, чтение — одним входом `store.ReadSeries`
  под одной транзакцией; правило выбора слоя остаётся в домене и приходит в
  хранилище колбэком
- `writeJSON` перестал экранировать HTML-символы и перестал глушить отказ
  записи: дословность содержимого точки иначе не удерживается, а оборванное
  тело уходило под видом успешного `200`
This commit is contained in:
av
2026-08-04 18:46:45 +03:00
parent b819b77f62
commit 29ca8d415c
36 changed files with 4721 additions and 58 deletions
+34
View File
@@ -786,3 +786,37 @@ func TestParseЧужаяПричинаОбрезается(t *testing.T) {
t.Errorf("текст ошибки %d Б: литерал из тела доехал до сообщения", len(err.Error()))
}
}
// Словарь слоёв ОДИН, и порядок в нём — от самого мелкого к самому крупному.
//
// Утверждается прямо, потому что из этого словаря выводятся четыре вещи: ранг
// слоя при разборе, перечень слоёв для выборки охватов при чтении, проверка
// параметра запроса и текст отказа клиенту. Разъехавшись, они дали бы пустой
// ряд при непустых данных — и ни компилятор, ни другой тест этого не увидели бы.
func TestСловарьСлоёвУпорядоченОтМелкогоККрупному(t *testing.T) {
want := []hae.Layer{hae.LayerSample, hae.LayerRaw, hae.LayerMinute, hae.LayerHour, hae.LayerDay}
if len(hae.Layers) != len(want) {
t.Fatalf("слоёв в словаре %d, ожидалось %d", len(hae.Layers), len(want))
}
for i, l := range want {
if hae.Layers[i] != l {
t.Errorf("слой %d — %q, ожидался %q", i, hae.Layers[i], l)
}
if hae.Rank(l) != i {
t.Errorf("ранг %q = %d, ожидался %d", l, hae.Rank(l), i)
}
if !hae.Known(l) {
t.Errorf("слой %q словарём не признан", l)
}
}
// Незнакомый слой крупнее любого известного и словарём не признан: иначе он
// выиграл бы предпочтение при равном охвате.
if got := hae.Rank("weekly"); got != len(hae.Layers) {
t.Errorf("ранг незнакомого слоя %d, ожидался %d", got, len(hae.Layers))
}
if hae.Known("weekly") {
t.Error("незнакомый слой признан словарём")
}
}