- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное вшивается через go:embed и раздаётся корневым маршрутом: разметка на неизвестном пути вне корней сервиса, отказ контракта внутри корня - перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов, а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь - шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка входят в гейт, а в Dockerfile появилась ступень приложения
94 lines
3.3 KiB
Go
94 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
httpcontroller "git.vakhrushev.me/av/transcriber/internal/controller/http"
|
|
)
|
|
|
|
// Перечень адресного пространства для проверок журнала. Обработчиков он здесь
|
|
// не вешает: журналу нужны только границы, а не то, что стоит за ними.
|
|
func journalMounts() []httpcontroller.Mount {
|
|
return []httpcontroller.Mount{
|
|
{Path: httpcontroller.StorageRoot},
|
|
{Path: httpcontroller.PanelRoot},
|
|
{Path: httpcontroller.AppRoot},
|
|
{Path: httpcontroller.AuthRoot},
|
|
{Path: httpcontroller.HealthPath, Exact: true},
|
|
{Path: httpcontroller.MetricsPath, Exact: true},
|
|
}
|
|
}
|
|
|
|
// Имя файла в хранилище в журнал не идёт: оно последняя часть ссылки
|
|
// `/api/files/...`, и строка журнала вместе с идентификатором записи собрала бы
|
|
// ссылку целиком. Инвариант проекта, critical.
|
|
func TestJournalRouteHidesStoredFileName(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
path string
|
|
want string
|
|
}{
|
|
{
|
|
name: "ссылка на файл теряет имя",
|
|
path: "/api/files/files/abc123def456ghi/9f1c-3b2a.mp3",
|
|
want: "/api/files/files/abc123def456ghi/<имя>",
|
|
},
|
|
{
|
|
name: "маршрут остаётся различимым",
|
|
path: "/api/files/files/abc123def456ghi/запись.ogg",
|
|
want: "/api/files/files/abc123def456ghi/<имя>",
|
|
},
|
|
{
|
|
name: "прочие пути не трогаются",
|
|
path: "/app/audiorecords/abc123def456ghi",
|
|
want: "/app/audiorecords/abc123def456ghi",
|
|
},
|
|
{
|
|
name: "приём не трогается",
|
|
path: "/app/audiorecords",
|
|
want: "/app/audiorecords",
|
|
},
|
|
{
|
|
name: "сам префикс без имени не портится",
|
|
path: "/api/files/",
|
|
want: "/api/files/",
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
assert.Equal(t, c.want, journalRoute(c.path, journalMounts()))
|
|
})
|
|
}
|
|
}
|
|
|
|
// Отдельно и прямо: имени в готовой строке нет. Проверка судит результат, а не
|
|
// устройство — переписанная реализация обязана остаться зелёной.
|
|
func TestJournalRouteDropsNameEntirely(t *testing.T) {
|
|
const stored = "0f7b8dd3-d1cc-424c.mp3"
|
|
|
|
route := journalRoute("/api/files/files/rec0000000000000/"+stored, journalMounts())
|
|
|
|
assert.NotContains(t, route, stored, "имя файла в хранилище не доезжает до журнала")
|
|
assert.Contains(t, route, "rec0000000000000", "идентификатор записи остаётся: по нему прослеживается путь")
|
|
}
|
|
|
|
// Путь, не принадлежащий сервису, уходит приложению и в журнал дословно не
|
|
// идёт: множеством его значений распоряжается спрашивающий.
|
|
func TestJournalRouteHidesWebappPath(t *testing.T) {
|
|
cases := []string{
|
|
"/",
|
|
"/records/abc123def456ghi",
|
|
"/" + strings.Repeat("a", 1024),
|
|
}
|
|
|
|
for _, path := range cases {
|
|
route := journalRoute(path, journalMounts())
|
|
|
|
assert.Equal(t, webappRoute, route)
|
|
}
|
|
}
|