- рядом с воркером свёртки живёт горутина, раз в минуту разбирающая журнал пассивным чекпойнтом; «журнал не разбирается» видно строкой владельцу, а не только по `df`. Признак — пара чисел, а не флаг занятости: тот молчит под удерживаемым читателем (`busy=0` при 6256 страницах и пяти перенесённых), а при занятой блокировке отдаёт `-1` вместо ответа, и `-1 >= -1` читалось бы как «разобрано целиком» - каталог отвечает `304` на `If-None-Match`, не открывая снимок витрины. Метка собрана из всего, от чего зависит ответ: версии витрины (`data_version` с закреплённого соединения плюс поколение — значение локально для соединения и не переживает переоткрытия), горизонта измерения и области действия ресурса. Версия снимается до и после сборки: снятая после пометила бы устаревший снимок свежим номером - предел и дедлайн ответа отложены в задачу Read API точек вместе с измеренной ценой первого запроса; попутно починен флаки-тест чужой задачи, искавший значение точки в сыром буфере записи лога
73 lines
3.2 KiB
Go
73 lines
3.2 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
// Сравнение меток слабое, звёздочка совпадает с любой существующей меткой, а
|
|
// мусор условия не выполняет — и это не отказ: клиент, приславший кривой
|
|
// заголовок, получает данные, а не `400`.
|
|
func TestУсловиеЗапроса(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const tag = `W/"metrics.gen-7"`
|
|
cases := []struct {
|
|
name string
|
|
header []string
|
|
tag string
|
|
want bool
|
|
}{
|
|
{"заголовка нет", nil, tag, false},
|
|
{"пустая строка", []string{""}, tag, false},
|
|
{"та же метка", []string{tag}, tag, true},
|
|
{"та же метка без W/", []string{`"metrics.gen-7"`}, tag, true},
|
|
{"чужая метка", []string{`W/"metrics.gen-8"`}, tag, false},
|
|
{"метка другого ресурса", []string{`W/"points.gen-7"`}, tag, false},
|
|
{"список, метка вторая", []string{`W/"metrics.gen-1", W/"metrics.gen-7"`}, tag, true},
|
|
{"две строки заголовка", []string{`W/"metrics.gen-1"`, `W/"metrics.gen-7"`}, tag, true},
|
|
{"запятая внутри метки", []string{`W/"points.a,b-7"`}, `W/"points.a,b-7"`, true},
|
|
{"метка без закрывающей кавычки", []string{`W/"metrics.gen-7`}, tag, false},
|
|
{"звёздочка", []string{"*"}, tag, true},
|
|
{"звёздочка без метки", []string{"*"}, "", false},
|
|
{"мусор", []string{"metrics.gen-7"}, tag, false},
|
|
{"мусор со звёздочкой внутри", []string{`"*"`}, tag, false},
|
|
{"метки нет", []string{tag}, "", false},
|
|
}
|
|
for _, c := range cases {
|
|
if got := notModified(c.header, c.tag); got != c.want {
|
|
t.Errorf("%s: %v, ждали %v", c.name, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Пустая версия метки не даёт: подписать ответ нечем, и притворяться нельзя.
|
|
func TestМеткаИзВерсии(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if got := etag("metrics", ""); got != "" {
|
|
t.Errorf("пустая версия дала метку %q", got)
|
|
}
|
|
if got := etag("metrics", "gen-7"); got != `W/"metrics.gen-7"` {
|
|
t.Errorf("метка %q, ждали слабую с областью", got)
|
|
}
|
|
if etag("metrics", "gen-7") == etag("points", "gen-7") {
|
|
t.Error("метки разных ресурсов совпали — 304 отдал бы чужие данные")
|
|
}
|
|
}
|
|
|
|
// Подписать нечем — заголовка нет вовсе. Пустой `ETag:` синтаксически невалиден,
|
|
// и что с ним сделает посредник, не определено ничем.
|
|
func TestОтветБезВерсииНеНесётМетки(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rec := httptest.NewRecorder()
|
|
setReadHeaders(rec, "")
|
|
if _, ok := rec.Header()["Etag"]; ok {
|
|
t.Errorf("ответ без версии несёт метку %q", rec.Header().Get("ETag"))
|
|
}
|
|
if got := rec.Header().Get("Cache-Control"); got != "private, no-cache" {
|
|
t.Errorf("правило кеширования %q — оно не зависит от наличия метки", got)
|
|
}
|
|
}
|