приём и чтение записей сведены к одному контракту приложения

- адреса приложения переехали в своё пространство `/app/`, опрос готовности
  убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи,
  текст — отдельным адресом названного вида
- заведена единая точка отображения доменной ошибки и слой, приводящий к той же
  форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением
- у записи появились имя файла отправителя, длительность и размер своими
  колонками, а у ленты владельца — свой индекс: без него страница сканировала
  весь архив сервиса
This commit is contained in:
av
2026-08-15 13:51:23 +03:00
parent 79ff12548f
commit 3a2da3004b
55 changed files with 5506 additions and 466 deletions
+69
View File
@@ -0,0 +1,69 @@
package entity
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
// Имя файла приходит извне и содержимым своим приёму не подконтрольно. Правило
// чистки живёт в домене, а не в транспорте: имя доходит до колонки записи одним
// путём, и правил обязано быть одно.
func TestSanitizeOriginalFilename(t *testing.T) {
cases := []struct {
name string
in string
want string
}{
{
name: "обычное имя не трогается",
in: "разговор.mp3",
want: "разговор.mp3",
},
{
name: "управляющие знаки убираются",
in: "разго\x00вор\x07\x1b.mp3",
want: "разговор.mp3",
},
{
name: "перевод строки — тоже управляющий знак",
in: "первая\nвторая.mp3",
want: "перваявторая.mp3",
},
{
name: "пустое остаётся пустым",
in: "",
want: "",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want, SanitizeOriginalFilename(tc.in))
})
}
}
// Режется имя по знакам, а не по байтам: имя русское чаще, чем латинское, и
// обрезка по байтам разрубила бы знак пополам.
func TestSanitizeOriginalFilenameTrimsByRunes(t *testing.T) {
long := strings.Repeat("я", MaxOriginalFilenameLen+50)
got := SanitizeOriginalFilename(long)
assert.Len(t, []rune(got), MaxOriginalFilenameLen, "длина считается знаками")
assert.True(t, strings.HasPrefix(long, got), "обрезано с хвоста, а не переписано")
assert.Equal(t, long[:len(got)], got, "ни один знак не разрублен пополам")
}
// Уборка идёт до резки: иначе потолок съедали бы знаки, которых в сохранённом
// имени всё равно не будет.
func TestSanitizeOriginalFilenameCleansBeforeTrimming(t *testing.T) {
dirty := strings.Repeat("\x00", 100) + strings.Repeat("я", MaxOriginalFilenameLen)
got := SanitizeOriginalFilename(dirty)
assert.Len(t, []rune(got), MaxOriginalFilenameLen,
"сто управляющих знаков не откусили сто знаков имени")
}