README
¶
qa — кроссплатформенные прогоны QA
Прогон целиком на Go — без sh/cmd/bat (внешние команды — только go,
gofmt, git для коммита в отчёте и собранные бинарники проекта);
работает на Linux, macOS, Windows, железо и окружение определяет сам.
Состав
| Файл | Назначение |
|---|---|
runner/ |
Прогонщик: фазы environment → build → static → consumer → сценарии CLI/REST по ногам → load; структурный отчёт qa-log/1 в qa/reports/*.log; код выхода 0 — все проверки зелёные |
restsrv/ |
Минимальный хост REST /api/v1 для прогонов (модуль серверов не запускает — план 04 §5); поднимается прогонщиком, порт выбирает сам (--addr 127.0.0.1:0) и сообщает фактический адрес строкой готовности |
load_test.go |
Нагрузочная прикидка: генерация NDJSON-датасета синтетических транзисторов JEDEC, импорт боевым путём, измерения поиска/карточки/suggest против ориентиров; запускается прогонщиком (-scale) либо напрямую go test (с -count=1 — измерение не берётся из кэша результатов) |
reports/ |
Авто-отчёты прогонов qa-*.log (формат ниже; коммитятся как свидетельство прогонов) и аналитические отчёты *.md |
Запуск
go run ./qa/runner # SQLite
CATALOG_TEST_POSTGRES_DSN=postgres://… go run ./qa/runner # + PostgreSQL
CATALOG_QA_DIALECT=postgres CATALOG_TEST_POSTGRES_DSN=postgres://… \
go run ./qa/runner # только PostgreSQL
go run ./qa/runner -scale 10000 # + нагрузочная прикидка
go run ./qa/runner -report qa/reports/x.log -keep # свой отчёт, не удалять временный каталог
Ключи: -report (путь файла отчёта; по умолчанию
qa/reports/qa-<дата>-<goos>-<goarch>.log, где <дата> —
ГГГГММДД-ЧЧММСС-наносекунды: коллизии имён повторных прогонов в
пределах секунды исключены), -postgres-dsn
(DSN PostgreSQL; по умолчанию CATALOG_TEST_POSTGRES_DSN, пусто — нога
опущена), -reset-postgres, -dialect (единственная нога прогона:
sqlite | postgres, переносятся сценарии и нагрузочная прикидка; по
умолчанию CATALOG_QA_DIALECT, пусто — все доступные ноги; postgres
без DSN — отказ до прогона), -scale (по умолчанию
CATALOG_QA_SCALE; 0 — фаза опущена, отрицательное значение — отказ
до прогона), -keep.
Сброс PostgreSQL деструктивен (DROP таблиц модуля) и ограждён: база из
env-конвенции CATALOG_TEST_POSTGRES_DSN — одноразовая по регламенту
(internal/testutil чистит её каждым go test) и сбрасывается
автоматически; база из флага -postgres-dsn — произвольная, сбрасывается
только с явным -reset-postgres, без него нога идёт по текущему
состоянию базы (грязная база падает на абсолютных проверках видимым
образом — безопасный отказ вместо разрушения данных). Список таблиц —
storage.TableNames(), выводится из DDL хранилища.
Код выхода: 0 — PASS, 1 — есть отказы проверок, 2 — прогон не начат
(не найден корень модуля, не открыт отчёт, неверные ключи — в том
числе отрицательный -scale), 130 — прогон отменён сигналом
(Ctrl+C/SIGTERM). Отменённый прогон: шаги, прерванные отменой или не
начатые, помечаются заметками «отменён» и не числятся FAIL; в
[summary] добавляется поле cancelled: yes; код 130 приоритетнее
кода 1 (реальные отказы до отмены видны в отчёте). Повторный Ctrl+C
после первого завершает процесс немедленно (умолчание сигнала),
не дожидаясь остановки команд фаз.
CI (.github/workflows/ci.yml): три джобы — Linux на SQLite, Linux со
службой PostgreSQL 16 и Windows на SQLite, каждая с прикидкой 10⁴
(CATALOG_QA_SCALE=10000, CATALOG_QA_DIALECT задаёт единственную ногу
джобы) — кроссплатформенность и обе СУБД проверяются каждым PR и каждым
коммитом без зелёного прогона: на пуше (main или тег v*) джоба
skip-check ищет через Actions API успешный прогон CI на том же коммите
и при находке пропускает все три джобы (тег указывает на коммит из
main, fast-forward-мерж — на голову PR, оба уже покрыты); артефакт
сборки — только отчёт текущего прогона (свежий qa/reports/*.log
копируется в qa-artifact/ по маркеру времени старта шага и выгружается
оттуда; закоммиченные исторические отчёты в артефакт не попадают).
Фазы прогона
- environment — железо и окружение (см. ниже).
- build — сборка
catalogctlиqa/restsrvво временный каталог. - static —
go build ./...,go vet ./...,gofmt -l .(обязан быть пуст),go test ./...— полный контур проверки AGENTS.md. При заданном DSN PostgreSQLgo testидёт с-p 1: одноразовая база одна на все пакеты, параллельные пакеты конфликтуют на DROP/CREATE таблиц (deadlock 40P01, гонка создания системных типов 23505). Отказ фазы не прерывает прогон (итог — в отчёте); фаза выполняется и при провале сборки бинарников — ей они не нужны. - consumer — сборка и запуск примера встраивания
examples/embed(внешний модуль: зависимость на библиотеку подключенаrequire+replaceна локальный путь, импортируется только публичный фасадcomponents): компилируемость у внешнего потребителя — часть контракта фасада, внутримодульные тесты импортируютinternal/и подлинности не доказывают. Бинарник исполняется (одноразовая база sqlite — пример создаёт и удаляет её сам): импорт файла наполнения, поиск, REST черезapp.Handler(), round-trip экспорт. - cli / rest по ногам — SQLite всегда; PostgreSQL при заданном DSN
(одноразовая база очищается прогонщиком перед ногой по ограждённым
правилам — см. «Запуск»; абсолютные проверки
countне зависят от остатков прошлых прогонов);-dialectоставляет единственную указанную ногу. При провале сборки бинарников (фаза build) обе сценарные фазы помечаются пропуском с причиной — зависят отcatalogctl/restsrv. Сценарии CLI: init, импорт выверенной выборкиdata/, идемпотентность, round-trip экспорта (ndjson по классу, yaml полностью), list с фильтрами класса/системы/подстроки, info (карточка, матрица исполнений), find с подсказкой равнозначного по материалу, parse (автодетект, JEDEC, негатив),--dry-run, каскадное удаление, битый файл,catalog list; негативные случаи — префиксError:(канонический en — D9) и код выхода 1. Сценарии REST: kinds/catalog/stats, поиск с фильтрами и параметрический фильтр, ETag → 304, карточки по ключу и по id, POST/PUT/DELETE с кодами 201/409/422/204/404, suggest, неизвестный маршрут; HTTP —net/http, без внешних инструментов. - load (при
-scale/CATALOG_QA_SCALE) — нагрузочная прикидка боевымgo test ./qa -run TestLoad -count=1(кэш результатов отключён — прикидка обязана выполняться, а не реплицироваться); строки измерений (p50/p95, скорость импорта) попадают в отчёт заметками.
Определение железа и окружения
Прогонщик сам определяет платформу и фиксирует её в секции [environment]:
- переносимо (stdlib): ОС (
runtime.GOOS), архитектура, число логических ядер, имя хоста, версия Go, модуль и коммит (build info, при отсутствии штампов vcs —git rev-parse), грязнота рабочего дерева, версии драйверов СУБД, версия библиотеки SQLite, версия сервера PostgreSQL (при заданном DSN); - ядро ОС: Linux —
/proc/sys/kernel/osrelease(uname -r), macOS — XNU (kern.osrelease), Windows — номер сборки ядра NT (CurrentBuildNumber- UBR из реестра);
- Linux: модель CPU (
/proc/cpuinfo), память (/proc/meminfo), издание ОС (/etc/os-release); - macOS: sysctl (
machdep.cpu.brand_string,hw.memsize,kern.osproductversion) черезgolang.org/x/sys/unix; - Windows: реестр (
ProcessorNameString,ProductName/DisplayVersion) иGlobalMemoryStatusEx(kernel32) черезgolang.org/x/sys/windows.
Внешних зависимостей не добавлено: golang.org/x/sys уже был в графе
модуля.
Формат отчёта qa-log/1
Отчёт — текстовый .log с фиксированной грамматикой (парсится
построчно, читается человеком; пишется построчно — прерванный прогон
оставляет частичный отчёт):
# components-catalog QA report
format: qa-log/1
[environment]
ключ: значение # поля окружения и железа
[phase: cli sqlite]
ok cli/init — init: база инициализирована
FAIL cli/count — count: записи выборки
| exit=0 # детали — только у отказов
| 65
[summary]
checks: 50
passed: 50
failed: 0
verdict: PASS
- первая строка — маркер отчёта, поле
format: qa-log/1— версия формата; - секции —
[environment],[phase: <имя>],[summary]; - поля —
ключ: значение, отсутствующее значение ——; - проверки —
ok <id> — <описание>/FAIL <id> — <описание>(<id>— стабильный латинский идентификатор видаcli/init,rest/post-create,static/gofmt); детали отказа — строки с отступом, начинающиеся с|; - заметки — строки
note …(в том числе пропущенные шаги: отменённые сигналом —note <id> — …: отменён— и зависящие от проваленной сборки фазы); - итог —
verdict: PASS|FAIL(FAIL — код выхода 1); у отменённого сигналом прогона в[summary]появляется полеcancelled: yes, отменённые шаги не FAIL, код выхода — 130.
Ориентиры производительности (docs/plan/04 §5)
- поиск с фильтрами по 10⁴–10⁵ устройств ≤ 100 мс;
- карточка ≤ 20 мс.
Прикидка — измерение, а не порог: медиана (p50) и 95-й перцентиль (p95)
печатаются с меткой ok / ПРЕВЫШЕНИЕ (метка — по медиане) и
фиксируются отчётом. Превышение ориентира — материал для
плана Б по риску R2 (денормализация/материализация вынесенных фильтров
без смены публичного API), а не автоматический отказ прогона; отказ —
только строгий режим (CATALOG_QA_STRICT=1, превышение на порядок) либо
ошибки корректности (отказы импорта).
Переменные прикидки: CATALOG_QA_SCALE (число записей),
CATALOG_QA_DIALECT (sqlite по умолчанию; заданная — единственная нога
и прогона целиком), CATALOG_QA_DB (путь файла sqlite; учитывается
только при диалекте sqlite — при CATALOG_QA_DIALECT=postgres
игнорируется, база ноги определяется CATALOG_TEST_POSTGRES_DSN),
CATALOG_QA_STRICT=1. Масштаб 10⁵ — ручной прогон по мере
надобности (в аналитический отчёт). База прикидки по умолчанию —
одноразовый каталог qa/tmp/load-* внутри репозитория (игнорируется
git, удаляется после прогона вместе с WAL/SHM): база обязана лежать на
реальном диске — каталог ОС по умолчанию (/tmp, %TEMP%) на части
систем — tmpfs в памяти, и импорт перестал бы касаться диска, делая
сравнение носителей бессмысленным. Явный CATALOG_QA_DB сохраняет файл
для диагностики; фактическое расположение фиксируется строкой
диалект=… база=… в отчёте. Повторный прогон с сохранённой базой
допустим с тем же или большим масштабом (проверяется итоговое число
записей); масштаб меньше уже находящегося в базе даст честный отказ
количество/переполнение. При прямом запуске go test без прогонщика
добавляйте -count=1 — иначе повторный прогон с тем же масштабом
вернётся из кэша результатов go test за нулевое время со старыми
результатами измерений.
Толкование цифр PostgreSQL: импорт — транзакция на запись (~20
statement'ов), каждый — round-trip до сервера, поэтому скорость импорта
определяется задержкой сети, а не СУБД (docker-прокси опубликованного
порта добавляет ~40% к RTT против прямого адреса; sqlite тот же путь
выполняет in-process). Поиск/карточка на postgres несут 1–3 round-trip
на операцию — разница с sqlite в единицы мс это норма клиент-серверной
СУБД. JIT postgres для соединений модуля отключён на уровне драйвера
(jit=off в DSN, план 02 §4) — иначе компиляция планов параметрического
поиска добавляла сотни мс на каждый запрос.
Documentation
¶
Overview ¶
Package qa — прогоны QA: кроссплатформенный прогонщик qa/runner (сборка, статические проверки, сквозные сценарии CLI и REST на SQLite и PostgreSQL, структурный отчёт qa-log/1) и нагрузочная прикидка против ориентиров производительности (load_test.go). Регламент запуска и формат отчётов — qa/README.md.
Directories
¶
| Path | Synopsis |
|---|---|
|
Команда restsrv — минимальный хост REST /api/v1 для сквозных сценарных прогонов (qa/runner).
|
Команда restsrv — минимальный хост REST /api/v1 для сквозных сценарных прогонов (qa/runner). |
|
Сценарии CLI: сквозной прогон catalogctl против ноги прогона (sqlite или postgres).
|
Сценарии CLI: сквозной прогон catalogctl против ноги прогона (sqlite или postgres). |