- POST /api/v1/ingest: токен, лимит тела, gzip, durable-запись тела в сырой архив, учёт доставки в SQLite - код ответа отражает доставку, а не разбор: битый JSON — 400, непонятое содержимое — 200, данные уже сохранены и доразберутся позже - сохраняется полный набор заголовков запроса с вычисткой секретов по имени и по совпадению значения с токеном
93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
package archive_test
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.vakhrushev.me/av/healthlog/internal/archive"
|
|
)
|
|
|
|
func TestWriteAndOpenRoundTrip(t *testing.T) {
|
|
a := newArchive(t)
|
|
body := []byte(`{"data":{"metrics":[]}}`)
|
|
at := time.Date(2026, 7, 31, 14, 5, 0, 0, time.UTC)
|
|
|
|
rel, err := a.Write("01k1abc", at, body)
|
|
if err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
|
|
if want := filepath.Join("2026", "07", "31", "01k1abc.json.gz"); rel != want {
|
|
t.Errorf("путь = %q, ожидался %q", rel, want)
|
|
}
|
|
|
|
r, err := a.Open(rel)
|
|
if err != nil {
|
|
t.Fatalf("Open: %v", err)
|
|
}
|
|
defer func() { _ = r.Close() }()
|
|
|
|
got, err := io.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatalf("чтение: %v", err)
|
|
}
|
|
if string(got) != string(body) {
|
|
t.Errorf("прочитано %q, записано %q", got, body)
|
|
}
|
|
}
|
|
|
|
// Раскладка по дате приёма — в UTC: иначе один и тот же момент попадал бы в
|
|
// разные каталоги в зависимости от зоны машины.
|
|
func TestWriteUsesUTCDate(t *testing.T) {
|
|
a := newArchive(t)
|
|
// 01:30 по московскому времени — это ещё предыдущие сутки по UTC.
|
|
msk := time.FixedZone("MSK", 3*60*60)
|
|
at := time.Date(2026, 8, 1, 1, 30, 0, 0, msk)
|
|
|
|
rel, err := a.Write("01k1def", at, []byte(`{"data":{}}`))
|
|
if err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
if want := filepath.Join("2026", "07", "31", "01k1def.json.gz"); rel != want {
|
|
t.Errorf("путь = %q, ожидался %q", rel, want)
|
|
}
|
|
}
|
|
|
|
// Временных файлов после успешной записи остаться не должно.
|
|
func TestWriteLeavesNoTempFiles(t *testing.T) {
|
|
root := t.TempDir()
|
|
a, err := archive.New(root)
|
|
if err != nil {
|
|
t.Fatalf("New: %v", err)
|
|
}
|
|
if _, err := a.Write("01k1ghi", time.Date(2026, 7, 31, 0, 0, 0, 0, time.UTC), []byte(`{"data":{}}`)); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
|
|
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.HasSuffix(path, ".tmp") {
|
|
t.Errorf("остался временный файл: %s", path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("обход архива: %v", err)
|
|
}
|
|
}
|
|
|
|
func newArchive(t *testing.T) *archive.Archive {
|
|
t.Helper()
|
|
a, err := archive.New(filepath.Join(t.TempDir(), "raw"))
|
|
if err != nil {
|
|
t.Fatalf("New: %v", err)
|
|
}
|
|
return a
|
|
}
|