Files
transcriber/internal/ident/ident_test.go
T
av c9b7765646 хранилище переехало с PocketBase на SQLite со своим каталогом файлов
- база своя: два пула, захват одним UPDATE ... RETURNING, шаги схемы на goose
  под файловым замком, одна миграция начальной схемы вместо семи прежних
- транспорт переписан на net/http: свои слои, свой ограничитель частоты,
  отдача файла с проверкой владельца; панель /_/ и пространство /api/ исчезли
- по находкам ревью: журнал не пишет путь под корнем приложения, ключ бюджета
  читается справа налево, узнавание известного идёт читающим пулом
2026-08-23 08:06:04 +03:00

102 lines
3.3 KiB
Go

package ident
import (
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Идентификатор — ULID в нижнем регистре: постоянная ширина, алфавит Crockford,
// и разбор своего же значения его не меняет.
func TestNewIsCanonical(t *testing.T) {
id := New()
assert.Len(t, id, Len)
assert.Equal(t, strings.ToLower(id), id, "идентификатор выдан не в нижнем регистре")
parsed, ok := Parse(id)
require.True(t, ok, "свой же идентификатор не разобрался")
assert.Equal(t, id, parsed, "разбор изменил канонический вид")
}
// Порядок выдачи и порядок сравнения строк совпадают — в том числе внутри одной
// миллисекунды: колонка времени несёт секунды, и «новые сверху» держит ключ.
func TestNewIsMonotonic(t *testing.T) {
previous := New()
for range 10000 {
next := New()
require.Less(t, previous, next, "идентификатор выдан не по возрастанию")
previous = next
}
}
// Выдача идёт из нескольких потоков разом: два одинаковых ключа означали бы
// отказ вставки на живой базе.
func TestNewIsUniqueUnderConcurrency(t *testing.T) {
const goroutines = 8
const each = 500
var (
mu sync.Mutex
all = map[string]bool{}
)
var wg sync.WaitGroup
for range goroutines {
wg.Add(1)
go func() {
defer wg.Done()
own := make([]string, 0, each)
for range each {
own = append(own, New())
}
mu.Lock()
defer mu.Unlock()
for _, id := range own {
all[id] = true
}
}()
}
wg.Wait()
assert.Len(t, all, goroutines*each, "выданы одинаковые идентификаторы")
}
// Разбор проверяет вид и приводит регистр: значение приходит от спрашивающего, а
// сравнение в базе побайтово.
func TestParse(t *testing.T) {
id := New()
upper, ok := Parse(strings.ToUpper(id))
require.True(t, ok, "верхний регистр не принят: алфавит к нему нечувствителен")
assert.Equal(t, id, upper, "разбор не привёл регистр к каноническому")
for name, value := range map[string]string{
"пусто": "",
"короче": id[:Len-1],
"длиннее": id + "0",
"знак вне алфавита": strings.Repeat("u", Len),
"время за пределом": "8" + id[1:],
"не латиница": strings.Repeat("я", Len),
} {
t.Run(name, func(t *testing.T) {
_, ok := Parse(value)
assert.False(t, ok, "негодное значение принято разбором")
})
}
}
// Время, зашитое в идентификатор, идёт в ту же сторону, что и колонка времени.
func TestTimestampGrowsWithIssue(t *testing.T) {
first, ok := Timestamp(New())
require.True(t, ok)
second, ok := Timestamp(New())
require.True(t, ok)
assert.False(t, second.Before(first), "время в ключе пошло назад")
}