приём и чтение записей сведены к одному контракту приложения
- адреса приложения переехали в своё пространство `/app/`, опрос готовности убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи, текст — отдельным адресом названного вида - заведена единая точка отображения доменной ошибки и слой, приводящий к той же форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением - у записи появились имя файла отправителя, длительность и размер своими колонками, а у ленты владельца — свой индекс: без него страница сканировала весь архив сервиса
This commit is contained in:
@@ -260,6 +260,39 @@ func TestУКаждогоРабочегоРубежаЕстьШаг(t *testing.T
|
||||
|
||||
// Обратное направление того же правила: шаг, написанный под рубеж, которого в
|
||||
// дескрипторе нет, недостижим — захват такую запись не выдаст никогда.
|
||||
// Отбор списка — очередной потребитель словаря рубежей, и перечислять их у него
|
||||
// строкой запроса нельзя: рубеж, добавленный конвейером, молча поменял бы состав
|
||||
// всех трёх состояний отбора, а заметить это было бы нечем.
|
||||
//
|
||||
// Правило смотрит, что выборка списка берёт рубежи у дескриптора, а не пишет их
|
||||
// литералом. Инвариант проекта «Рубеж объявляется одним дескриптором» компилятор
|
||||
// не проверяет — проверяет оно.
|
||||
func TestОтборСпискаБерётРубежиУДескриптора(t *testing.T) {
|
||||
const listFile = repoPkg + "/record_list.go"
|
||||
|
||||
body := readFile(t, listFile)
|
||||
|
||||
for _, value := range declaredStateValues(t) {
|
||||
if strings.Contains(body, `"`+value+`"`) {
|
||||
t.Errorf(
|
||||
"отбор списка называет рубеж %q строкой: рубеж, добавленный "+
|
||||
"дескриптором, молча не попадёт ни в одно состояние отбора",
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, fn := range []string{"entity.WorkingStages()", "entity.TerminalStages()"} {
|
||||
if !strings.Contains(body, fn) {
|
||||
t.Errorf(
|
||||
"отбор списка не зовёт %s: перечень рубежей обязан приходить из "+
|
||||
"дескриптора, а не собираться по месту",
|
||||
fn,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestШагиОбъявленыРубежамиДескриптора(t *testing.T) {
|
||||
body := funcBody(t, serviceFile, "func (s *TranscribeService) stepFor(")
|
||||
declared := map[string]bool{}
|
||||
@@ -363,6 +396,25 @@ func stageDescriptor(t *testing.T) (all []string, working []string) {
|
||||
}
|
||||
|
||||
// declaredStates — константы рубежей, объявленные доменом.
|
||||
// declaredStateValues — **значения** рубежей, а не имена их констант: правило
|
||||
// отбора ищет строковый литерал в чужом файле, и сравнивать его надо со
|
||||
// значением.
|
||||
//
|
||||
// declaredStates рядом отдаёт имена констант — им пользуются правила, читающие
|
||||
// код, а не строки.
|
||||
func declaredStateValues(t *testing.T) []string {
|
||||
t.Helper()
|
||||
out := []string{}
|
||||
re := regexp.MustCompile(`(?m)^\tState\w+\s*=\s*"([^"]+)"`)
|
||||
for _, m := range re.FindAllStringSubmatch(readFile(t, stateFile), -1) {
|
||||
out = append(out, m[1])
|
||||
}
|
||||
if len(out) == 0 {
|
||||
t.Fatalf("в %s не объявлено ни одного рубежа: правило потеряло предмет", stateFile)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func declaredStates(t *testing.T) map[string]bool {
|
||||
t.Helper()
|
||||
out := map[string]bool{}
|
||||
|
||||
Reference in New Issue
Block a user