Дозакрыты находки ревью по слиянию сущностей
- Правило покрытия получило второй разряд (условный, как у точек), запрет вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и ряд из null больше не затирают маршрут. Победитель внутри доставки стал функцией множества версий — общим помощником с точками, — а провенанс поднимается и при совпавшем хеше, иначе отложенная доставка возвращала витрину к прежнему содержимому. - Одно поле не того типа больше не уносит сущность, а пропуски видны в учётной записи доставки (миграция 00008, NULL = «не измерялось»); каноническая форма считается один раз и вне транзакции; откат бинаря поверх новой схемы отказывает на старте; текст ошибки разбора не несёт значений из тела. - Ревью кода профилем deep (девять проходов) нашло две регрессии и обе закрыты: безусловный второй разряд запирал законный досчёт навсегда, а выбор победителя был квадратичен по числу присланных версий одного ключа.
This commit is contained in:
@@ -625,3 +625,55 @@ func TestИмяТелаОбязаноБытьКаноническим(t *testing
|
||||
t.Errorf("повторов %d: настоящее тело вытеснено подложенным", rep.Duplicates)
|
||||
}
|
||||
}
|
||||
|
||||
// Число пропущенных сущностей — производное от разбора поле, и пересборка его
|
||||
// не переносит. Пустота у него значит «не измерялось», а перенесённое число
|
||||
// выдавало бы измерение ПРЕЖНЕГО разбора за измерение текущего — притом что по
|
||||
// нему принимается необратимое решение об удалении тела.
|
||||
func TestЧислоПропусковНеПереноситсяВПересобраннуюБазу(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
arch := openArchive(t, filepath.Join(dir, "raw"))
|
||||
src := openStore(t, filepath.Join(dir, "live.db"))
|
||||
ctx := context.Background()
|
||||
|
||||
items := journal(t, "minute.json")
|
||||
live(t, arch, src, items)
|
||||
|
||||
// Проставляем счётчик в исходной базе, как если бы его измерил прежний
|
||||
// разбор, и убираем тело: пересборке будет нечего пересчитывать.
|
||||
n := int64(7)
|
||||
for _, it := range items {
|
||||
err := src.FinishParse(ctx, it.id, store.ParseOutcome{
|
||||
Status: store.ParseDone,
|
||||
SkippedEntities: &n,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("простановка счётчика: %v", err)
|
||||
}
|
||||
}
|
||||
// Тело убираем: доставка становится записью без тела, пересборка её не
|
||||
// сворачивает — и производные поля обязаны начаться пустыми, а не приехать
|
||||
// из журнала.
|
||||
raw := filepath.Join(dir, "raw")
|
||||
if err := os.RemoveAll(raw); err != nil {
|
||||
t.Fatalf("удаление тел: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(raw, 0o755); err != nil {
|
||||
t.Fatalf("пересоздание каталога архива: %v", err)
|
||||
}
|
||||
|
||||
rep, dst := run(t, ctx, arch, src, filepath.Join(dir, "rebuild.db"))
|
||||
if rep.Orphans == 0 {
|
||||
t.Fatal("доставка не стала записью без тела — тест проверяет не то")
|
||||
}
|
||||
|
||||
got, err := dst.LastDelivery(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("чтение доставки: %v", err)
|
||||
}
|
||||
if got.SkippedEntities != nil {
|
||||
t.Errorf("пересборка перенесла счётчик прежнего разбора: %d", *got.SkippedEntities)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user