Add test/acceptance/ so the customer's reviewers can re-run what the Stage 8 documents claim on an isolated stand built from this branch: - compose.acceptance.yml + stand.sh: compose project gis-acceptance (app 18080, postgres 15432, minio 19000/19001, rabbitmq mgmt 15672), POSTGIS_PLATFORM / MINIO_IMAGE knobs for arm64 and amd64 hosts. - functional.sh: the 89 test-stand checks of Приложение 7 section 5 (F01-F38, N01-N38, L02-L05, L07-L15), own prefixed test data, PostGIS/MinIO confirmation via the stand containers, full cleanup. - lp_gen.py / lp_run.sh / lp_verify.py: synthetic ЛП inputs, a run from stage 0, and an independent numpy re-implementation of every node that compares all 15 outputs pixel by pixel; read-only (GET) mode works against any stand, in row/column windows for 22219x14977 rasters. - load.sh + seed.sh: scenarios S1-S6 of Приложение 3 with hey 0.1.5 running inside the stand network; CSV/JSON summaries, criteria K1-K4. - scan.sh: govulncheck (source and binary), gosec, trivy fs/image, golangci-lint; fails on any High/Critical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
321 lines
26 KiB
Markdown
321 lines
26 KiB
Markdown
# Пакет приёмочных испытаний (Этап 8)
|
||
|
||
Пакет позволяет Заказчику самостоятельно воспроизвести то, что заявлено в
|
||
документах Этапа 8, на изолированном испытательном стенде, развёрнутом из
|
||
исходного кода этой ветки:
|
||
|
||
| № | Что проверяется | Скрипт | Документ |
|
||
|---|---|---|---|
|
||
| 1 | 89 функциональных проверок испытательного стенда (F01–F38, N01–N38, L02–L05, L07–L15) | `functional.sh` | Приложение 7 «Протокол функционального тестирования», раздел 5 |
|
||
| 2 | Попиксельная сверка расчёта Ландшафтной программы (ЛП) с независимой эталонной реализацией | `lp_gen.py`, `lp_run.sh`, `lp_verify.py` | ЛП техописание (правила пикселя, граф узлов); Приложение 7, L07/L08/L12 |
|
||
| 3 | Нагрузочные испытания: 1000 одновременных клиентов, сценарии S1–S6 | `load.sh` (+ `seed.sh`) | Приложение 3 «Протокол нагрузочного тестирования» |
|
||
| 4 | Повторный анализ безопасности: 0 уязвимостей High/Critical | `scan.sh` | Приложение 4 «Отчет об устранении уязвимостей» |
|
||
|
||
Все скрипты — bash (macOS и Linux, `set -euo pipefail`, совместимы с bash 3.2),
|
||
curl, jq и Python 3. Результаты прогонов сохраняются в `test/acceptance/results/`
|
||
(каталог не версионируется).
|
||
|
||
> **Внимание.** `functional.sh`, `lp_run.sh`, `seed.sh` и сценарий S5 из
|
||
> `load.sh` **создают данные** (в том числе заполняют слоты ЛП). Их следует
|
||
> запускать только на изолированном стенде `gis-acceptance`. На рабочем стенде
|
||
> https://dssgis.dwh.kz допускается только `lp_verify.py` — он выполняет
|
||
> исключительно GET-запросы (`lp_run.sh` и `seed.sh` отказываются работать с
|
||
> этим адресом).
|
||
|
||
## Состав пакета
|
||
|
||
| Файл | Назначение |
|
||
|---|---|
|
||
| `compose.acceptance.yml` | Надстройка над `deployments/docker-compose.yml` для изолированного стенда (проект `gis-acceptance`) |
|
||
| `stand.sh` | Развёртывание (`up`: сборка, миграции, справочник районов, ожидание `/readyz`) и удаление (`down`) стенда |
|
||
| `functional.sh` | 89 функциональных проверок, построчный вывод `ID \| описание \| PASS/FAIL \| detail` |
|
||
| `lp_gen.py` | Генерация 13 синтетических входных растров ЛП и одного невыровненного растра |
|
||
| `lp_run.sh` | Загрузка входов в 13 слотов → прогон `{"from_stage":0}` → `lp_verify.py` |
|
||
| `lp_verify.py` | Независимая (numpy) реализация всех узлов ЛП и попиксельная сверка 15 выходов; только GET |
|
||
| `load.sh` | Сценарии нагрузки S1–S6 (hey 0.1.5), сводная таблица, CSV/JSON |
|
||
| `seed.sh` | Наполнение стенда данными для нагрузочных испытаний (`seed.sh clean` — удаление) |
|
||
| `scan.sh` | govulncheck (исходный код и бинарный файл), gosec, trivy fs, trivy image, golangci-lint |
|
||
| `data/` | Тестовые векторные данные (участки, население и поголовье КРС по 14 районам СКО с реальными кодами КАТО), SLD-стили |
|
||
| `lib/` | Вспомогательные скрипты: генерация NDVI, анализ вывода hey, Dockerfile генератора нагрузки, пересоздание `data/` |
|
||
| `requirements.txt` | Python-зависимости (`numpy`, `rasterio`) |
|
||
|
||
## Требования
|
||
|
||
* Docker Engine с Docker Compose v2.24+ (используется тег `!override` для портов).
|
||
* `curl`, `jq`, Python 3.10+ с `numpy` и `rasterio`:
|
||
```sh
|
||
python3 -m venv .venv && .venv/bin/pip install -r test/acceptance/requirements.txt
|
||
export PYTHON=$PWD/.venv/bin/python # скрипты используют $PYTHON (по умолчанию python3)
|
||
```
|
||
* Для `scan.sh`: Go (версия toolchain берётся из `go.mod`), `govulncheck`,
|
||
`gosec`, `trivy`; `golangci-lint` — по желанию.
|
||
|
||
## 0. Испытательный стенд
|
||
|
||
```sh
|
||
git clone https://dssgit.dwh.kz/infureal/gis.git && cd gis && git checkout stage8/security-fixes
|
||
test/acceptance/stand.sh up # сборка образа, запуск, migrate up, import-districts, ожидание /readyz
|
||
```
|
||
|
||
`stand.sh up` выполняет те же шаги, что и вручную:
|
||
|
||
```sh
|
||
C="docker compose -p gis-acceptance -f deployments/docker-compose.yml -f test/acceptance/compose.acceptance.yml"
|
||
$C up -d --build --wait postgres minio rabbitmq
|
||
$C run --rm --no-deps app migrate up # 11 миграций 00001…00011
|
||
$C run --rm --no-deps app import-districts /stubs/districts.geojson # 14 районов СКО
|
||
$C up -d app worker
|
||
curl http://localhost:18080/readyz # {"components":{"postgres":"ok","rabbitmq":"ok","s3":"ok"},"ready":true}
|
||
```
|
||
|
||
Порты стенда: API — 18080, PostgreSQL — 15432, MinIO — 19000 (API) / 19001
|
||
(консоль), RabbitMQ — 15672 (только веб-интерфейс управления). Сеть контейнеров —
|
||
`gis-acceptance_default`. Учётные данные — значения по умолчанию из
|
||
`deployments/docker-compose.yml` (стенд изолированный).
|
||
|
||
Переменные окружения стенда:
|
||
|
||
| Переменная | По умолчанию | Назначение |
|
||
|---|---|---|
|
||
| `POSTGIS_PLATFORM` | `linux/amd64` | Образ `postgis/postgis:17-3.5` публикуется только для amd64. На Apple Silicon (arm64) он запускается в эмуляции, на amd64-хостах — нативно, поэтому значение по умолчанию работает на обеих платформах |
|
||
| `MINIO_IMAGE` | `minio/minio:latest` | Если образ `minio/minio` недоступен в реестре, подойдёт любой образ с сервером `minio` и клиентом `mc`, например `MINIO_IMAGE=cgr.dev/chainguard/minio:latest` |
|
||
| `APP_IMAGE` | `gis-acceptance-app:local` | Тег локально собранного образа приложения |
|
||
| `HTTP_MAX_UPLOAD_BYTES` | `2147483648` | Предельный размер тела запроса (2 ГиБ) |
|
||
| `LP_MAX_WORKERS` / `LP_MAX_NODES` | `8` / `2` | Параллелизм расчёта ЛП |
|
||
|
||
Удаление стенда вместе с томами: `test/acceptance/stand.sh down`
|
||
(= `docker compose -p gis-acceptance down -v`).
|
||
|
||
## 1. Функциональные проверки — `functional.sh`
|
||
|
||
```sh
|
||
test/acceptance/functional.sh # стенд http://localhost:18080
|
||
BASE_URL=http://host:18080 LOCAL_CHECKS=0 test/acceptance/functional.sh # удалённый испытательный стенд
|
||
```
|
||
|
||
| Переменная | По умолчанию | Назначение |
|
||
|---|---|---|
|
||
| `BASE_URL` | `http://localhost:18080` | Адрес API |
|
||
| `LOCAL_CHECKS` | `auto` | `1` — подтверждение в PostGIS и MinIO через контейнеры проекта (`docker exec … psql`, `mc` в контейнере MinIO): F35 (часть MinIO), F36, F37, F38, ресурсы worker в L15. `0` — эти проверки пропускаются (`SKIP`). `auto` — `1`, если запущены контейнеры проекта `gis-acceptance` |
|
||
| `COMPOSE_PROJECT` | `gis-acceptance` | Проект Compose для `LOCAL_CHECKS` |
|
||
| `KEEP_DATA` | `0` | `1` — не удалять созданные данные |
|
||
| `WAIT_TIMEOUT` | `300` | Ожидание асинхронной обработки, с |
|
||
|
||
Скрипт создаёт собственные тестовые данные (все коды имеют уникальный префикс
|
||
`acc<время>-`), никогда не изменяет и не удаляет существовавшие объекты и в
|
||
конце удаляет всё созданное: наборы данных (в том числе входы и выходы ЛП своих
|
||
прогонов), группы, категории. Записи прогонов ЛП остаются — API не
|
||
предусматривает их удаления. Порядок и смысл проверок совпадают с таблицами
|
||
раздела 5 Приложения 7.
|
||
|
||
Ожидаемый вывод (фрагмент):
|
||
|
||
```
|
||
F01 | Создание корневой категории POST /categories | PASS | 201; acc1792084680-ecology «Экология», parent_id null
|
||
...
|
||
N38 | Загрузка 100 МБ мусора с сигнатурой TIFF | PASS | 201 (104 857 604 байт за 0.5 с) → status failed, parse_error от gdal_translate; /readyz 200, сервис доступен
|
||
...
|
||
L07 | Попиксельная сверка 15 выходов с эталонной реализацией | PASS | 15 выходов × 6 000 000 пикселей; «TOTAL mismatching pixels: 0 -> PASS» …
|
||
...
|
||
ИТОГО: 89 проверок — PASS 89, FAIL 0, SKIP 0
|
||
RESULT: 89/89 PASS
|
||
```
|
||
|
||
Код возврата ненулевой при любом `FAIL`. Журнал и вспомогательные выводы
|
||
(`L07_lp_verify.txt`, `L08_points.txt`, `L12_lp_verify.txt`, `F36.txt`) —
|
||
в `results/functional-<время>/`.
|
||
|
||
### Соответствие Приложению 7 и уточнения методики
|
||
|
||
| Проверки | Раздел Приложения 7 | Как проверяется в пакете |
|
||
|---|---|---|
|
||
| F01–F09 | 5.2 Категории | HTTP-код и поля ответа, контроль цикла, дубликат, удаление |
|
||
| F10–F34, N01–N13 | 5.3 Импорт/экспорт, SLD, группы, фильтры | Загрузка `data/plots_nko.geojson` (vector), `data/population_kato.geojson` и `data/livestock_kato.gpkg` (vector_with_kato), растра NDVI 600×400 EPSG:4326 Float32 (генерируется `lib/helpers.py`) |
|
||
| N14–N38, F35 | 5.4 Негативные проверки | 12 некорректных загрузок, 10 эндпоинтов с несуществующим id, 4 — с некорректным id, некорректные тела JSON, параметры ЛП, 100 МБ мусора |
|
||
| F36–F38 | 5.5 PostGIS и MinIO | SQL `ST_GeometryType/ST_SRID/ST_IsValid`, соединение наблюдений с `districts`, `mc stat` объектов |
|
||
| L02–L15 | 5.6 Ландшафтная программа | 13 растров `lp_gen.py`, прогоны, `lp_verify.py`, инкрементальный пересчёт, `from_stage`, параметры, невыровненная сетка |
|
||
|
||
Уточнения относительно протокола (смысл и ожидаемые результаты проверок не
|
||
изменены):
|
||
|
||
* **Коды объектов** имеют префикс прогона (`acc…-ecology` вместо `ecology`),
|
||
чтобы повторный запуск не конфликтовал с данными прошлых прогонов.
|
||
* **F33, F34** — выборки ограничены собственной категорией (`category_id`), чтобы
|
||
результат не зависел от других данных стенда; во втором фильтре F34
|
||
(`file_type + status`) добавлен `category_id`.
|
||
* **F12** — охват проверяется по координатам выгруженного GeoJSON (вместо
|
||
`ogrinfo`); **F22, L05** — формат COG проверяется через rasterio/GDAL
|
||
(`LAYOUT=COG`, сжатие, размер, EPSG, тип, NoData) вместо `gdalinfo`;
|
||
**L08** — 9 точек проверяются `lp_verify.py --points` вместо `gdallocationinfo`.
|
||
* **F38** — проверяется наличие всех 7 объектов, созданных скриптом (4 исходных
|
||
файла, COG, SLD набора, SLD группы), а не общее число объектов в бакете.
|
||
* **F16, F17** — эталонные значения берутся из `data/population_kato.geojson`
|
||
(значения, приведённые в протоколе, в файле сохранены).
|
||
* **L05** — перед чтением `/landscape/results` скрипт дожидается статуса `ready`
|
||
всех 15 выходов: конвертация выходов в COG выполняется worker'ом асинхронно
|
||
после завершения прогона, а `/landscape/results` возвращает только готовые слои.
|
||
* **N38 и ограничение размера тела запроса.** В этой ветке (исправления по
|
||
Приложению 4) тело запроса ограничено `HTTP_MAX_UPLOAD_BYTES` (по умолчанию
|
||
2 ГиБ); загрузка сверх предела отклоняется с **413** `request body exceeds
|
||
the … byte limit`. Файл N38 (100 МБ) меньше предела, поэтому ожидание N38
|
||
не изменилось: 201, затем `status = failed`. Проверить 413 можно вручную,
|
||
запустив стенд с `HTTP_MAX_UPLOAD_BYTES=1048576` и загрузив файл больше 1 МиБ.
|
||
* **`/readyz`** в этой ветке не раскрывает текст ошибок зависимостей: при сбое
|
||
компонент помечается `"unavailable"` (ранее — `"error: <текст>"`). На исправном
|
||
стенде ответ не изменился: `200 {"components":{"postgres":"ok","rabbitmq":"ok","s3":"ok"},"ready":true}`;
|
||
N38 проверяет `200` и `ready = true`.
|
||
* **L15** — PASS определяется успешным полным прогоном (202, `succeeded`, 9 узлов
|
||
`succeeded`); время CPU и `memory.peak` worker (cgroup v2) выводятся
|
||
справочно и включают COG-конвертацию выходов.
|
||
|
||
## 2. Ландшафтная программа: попиксельная сверка
|
||
|
||
### Входные данные — `lp_gen.py`
|
||
|
||
```sh
|
||
$PYTHON test/acceptance/lp_gen.py --out /tmp/lp-in [--width 3000 --height 2000]
|
||
```
|
||
|
||
13 растров `<код слота>.tif`: Int16, EPSG:32642, пиксель 100 м, 3000×2000,
|
||
классы 1…5 из гладких тригонометрических полей (у каждого слота своё),
|
||
рамка NoData −9999 шириной 40 пикселей. Особые случаи для правил пикселя:
|
||
в `lp-u-crop` — блок NoData 200×200 (U считается по water, soil и veg),
|
||
в `lp-u-veg` — блок NoData 100×100, наполовину перекрывающий его (там нет ни
|
||
crop, ни veg → U = NoData), в `lp-p-snow` — блоки со значениями 0 и 6
|
||
(не NoData, но вне 1…5 → недопустимы). Плюс `misaligned-lp-u-veg.tif`
|
||
(1500×1000, 200 м) для проверки L13. Генерация детерминирована.
|
||
|
||
### Прогон на стенде — `lp_run.sh`, режим (a)
|
||
|
||
```sh
|
||
test/acceptance/lp_run.sh # генерация → 13 слотов → {"from_stage":0} → lp_verify.py
|
||
```
|
||
|
||
Входы и выходы остаются на стенде, прогон можно перепроверить:
|
||
`$PYTHON test/acceptance/lp_verify.py --base-url http://localhost:18080 --run <run-id>`.
|
||
|
||
### Проверка любого стенда (только чтение) — `lp_verify.py`, режим (b)
|
||
|
||
```sh
|
||
$PYTHON test/acceptance/lp_verify.py --base-url https://dssgis.dwh.kz --run <run-id> [--cache DIR]
|
||
```
|
||
|
||
Скрипт выполняет только GET-запросы:
|
||
|
||
1. `GET /landscape/runs/{id}` — параметры прогона, для каждого узла `inputs` и
|
||
`outputs` (группа → набор данных);
|
||
2. `GET /datasets/{id}.cog` — 13 входов (наборы слотов, которые прогон
|
||
фактически использовал) и 15 выходов (`--inputs-from download` читает
|
||
входы через `/datasets/{id}/download` — исходный файл, который читал узел).
|
||
|
||
Ожидаемые значения рассчитываются по всей цепочке **только из 13 входов**
|
||
(E — из пересчитанных Z и U, C и V — из пересчитанного E), поэтому ошибка в
|
||
промежуточном слое не может скрыть ошибку в последующем. Дополнительно
|
||
проверяется согласованность цепочки самого прогона (узел 10 читал выход узла
|
||
00 этого прогона и т. д.) и совпадение сетки всех 28 растров. Растры
|
||
обрабатываются окнами 512×4096 пикселей (выровнены по тайлам COG 512), поэтому
|
||
расход памяти ограничен (~0,5 ГБ) и для растров рабочего стенда 22219×14977.
|
||
|
||
Правила пикселя (источник истины — `internal/landscape/kernels.go`, они же —
|
||
в ЛП техописании): значение допустимо при `1 ≤ v ≤ 5`; `classify` в float32
|
||
(`≤1.8→1, ≤2.6→2, ≤3.4→3, ≤4.2→4, ≤5.0→5`); z_integrated — среднее четырёх Z с
|
||
весом 0,25; u_integrated — water ∧ soil ∧ (crop ∨ veg), веса 0,25 только у
|
||
допустимых crop/veg; z_max/u_max — максимум; E = max(Z, U); C_j = min(P_j, E);
|
||
p_max/c_max — максимум по допустимым; func_zoning V1–V3 с порогами
|
||
`v1_min_s_e12`, `v1_min_s_e3`, `v2_min_s_e3`, `v2_min_s_e4` и `tie_priority`
|
||
из параметров прогона.
|
||
|
||
Вывод: по каждому из 15 выходов — число пикселей, допустимых (по эталону) и
|
||
**число несовпадающих пикселей** (с координатами первого расхождения), итоговая
|
||
строка `TOTAL mismatching pixels: N -> PASS/FAIL`. Код возврата: 0 — полное
|
||
совпадение, 1 — есть расхождения, 2 — ошибка данных (прогон не `succeeded`,
|
||
не найден вход/выход, разные сетки).
|
||
|
||
## 3. Нагрузочные испытания — `load.sh`
|
||
|
||
```sh
|
||
test/acceptance/load.sh # S1–S6, генератор внутри сети стенда
|
||
SCENARIOS="S1 S3 S6" test/acceptance/load.sh # часть сценариев
|
||
GEN=host TARGET_URL=http://localhost:18080 test/acceptance/load.sh # hey на хосте, через порт стенда
|
||
NETWORK=bridge TARGET_URL=https://host BASE_URL=https://host SCENARIOS="S1 S3" test/acceptance/load.sh
|
||
```
|
||
|
||
Генератор — hey 0.1.5, собирается из исходного кода в образ
|
||
`gis-acceptance-hey:0.1.5` (`lib/hey.Dockerfile`, на тех же закреплённых образах
|
||
Go/Alpine, что и приложение) и по умолчанию запускается **внутри сети стенда**
|
||
(`docker run --network gis-acceptance_default …`, цель `http://app:8080`).
|
||
Перед нагрузкой `seed.sh` (идемпотентно) наполняет стенд: 25 vector, 25
|
||
vector_with_kato (с маппингом КАТО, 14 районов × 3 года) и 50 raster в
|
||
категории `acc-load`; если прогонов ЛП ещё нет, выполняется `lp_run.sh`.
|
||
На удалённом стенде без `ALLOW_WRITES=1` данные не создаются: id для
|
||
эндпоинтов определяются GET-запросами, сценарий S5 пропускается.
|
||
|
||
Эндпоинты (табл. 5 Приложения 3): `/datasets?page_size=20`, `/datasets/{id}`,
|
||
`/datasets/{id}.geojson`, `/datasets/{id}/observations`, `/categories`,
|
||
`/dataset-groups/{id}/latest`, `/landscape/slots`, `/landscape/results`,
|
||
`/landscape/runs/{id}`, `/readyz`.
|
||
|
||
| Сценарий | Параметры hey |
|
||
|---|---|
|
||
| S1 | `-n 100 -c 1` последовательно по 10 эндпоинтам (1000 запросов) |
|
||
| S2 | 1000 запросов, 100 клиентов: 10 параллельных процессов `-n 100 -c 10` |
|
||
| S3.1 | `-n 1000 -c 1000 /datasets?page_size=20`, 3 прогона |
|
||
| S3.2 | `-n 1000 -c 1000 /landscape/results`, 3 прогона |
|
||
| S4 | `-z 60s -q 1`, 1000 клиентов в 8 процессах: 300 `/datasets?page_size=20`, 150 `/datasets/{id}`, по 100 `.geojson`, `/observations`, `/categories`, `/latest`, `/landscape/results`, 50 `/readyz` |
|
||
| S5 | `-z 30s -c 10` × 10 эндпоинтов + 20 параллельных `POST /datasets` (10 GeoJSON + 10 GeoTIFF), все загрузки должны дойти до `ready` (затем удаляются) |
|
||
| S6 | Повтор S1 и `GET /readyz` после нагрузки |
|
||
|
||
S3.3 протокола (1000 одновременных запросов через опубликованный порт)
|
||
воспроизводится запуском `GEN=host TARGET_URL=http://localhost:18080 SCENARIOS=S3`.
|
||
|
||
Вывод: сводная таблица (запросы, клиенты, длительность, RPS, доля успешных
|
||
ответов, min/avg/P50/P90/P95/P99/max в мс, коды ответов, транспортные ошибки,
|
||
итог по критериям), таблицы по эндпоинтам/прогонам для S2–S5, результаты
|
||
загрузок S5, пиковое потребление CPU/памяти контейнерами (docker stats во время
|
||
S4/S5) и критерий K4. Как и в протоколе, перцентили сценариев из нескольких
|
||
процессов/прогонов — средневзвешенные по числу ответов. Файлы:
|
||
`results/load-<время>/summary.csv`, `summary.json`, `detail.csv`, `raw/*.txt`
|
||
(исходные выводы hey). Критерии (табл. 8 Приложения 3): K1 — успешных ответов
|
||
≥ 99 %, K2 — ответов 5xx нет, K3 — P95 ≤ 2000 мс, K4 — после нагрузки `/readyz`
|
||
200 и P50 S6 не хуже P50 S1 (допуск ×2 + 1 мс). Код возврата 1 при
|
||
невыполнении любого критерия.
|
||
|
||
Генератор и стенд работают на одной машине и делят CPU, поэтому абсолютные
|
||
значения RPS зависят от оборудования (протокол фиксирует их как нижнюю оценку).
|
||
|
||
## 4. Анализ безопасности — `scan.sh`
|
||
|
||
```sh
|
||
test/acceptance/scan.sh
|
||
```
|
||
|
||
| Инструмент | Что проверяет | Что считается High/Critical |
|
||
|---|---|---|
|
||
| `govulncheck ./...` | Уязвимости Go-модулей и стандартной библиотеки, достижимые из кода | Каждая уязвимость с цепочкой вызовов из кода (у govulncheck нет шкалы severity) |
|
||
| `govulncheck -mode=binary` | Бинарный файл `/usr/local/bin/gis` из собранного образа | Каждая уязвимость с затронутыми символами в бинарнике |
|
||
| `gosec ./...` | Статический анализ кода | Severity `HIGH` |
|
||
| `trivy fs` | Зависимости (`go.mod`), секреты, конфигурации (Dockerfile, compose) | `HIGH`, `CRITICAL` |
|
||
| `trivy image` | Образ приложения (Alpine + GDAL + бинарник), собирается из `build/package/Dockerfile` | `HIGH`, `CRITICAL` |
|
||
| `golangci-lint` | Качество кода (если установлен) | Справочно: число замечаний (шкалы severity нет) |
|
||
|
||
Скрипт печатает таблицу «инструмент | цель | High | Critical | примечания» и
|
||
итог `RESULT: 0 High / 0 Critical -> PASS`; код возврата 1 при наличии
|
||
High/Critical. Полные отчёты (JSON) — `results/scan-<время>/`. Без Docker:
|
||
`SKIP_IMAGE=1` (бинарник собирается локально с cgo и GDAL, `trivy image`
|
||
пропускается).
|
||
|
||
## Порядок полного воспроизведения
|
||
|
||
```sh
|
||
test/acceptance/stand.sh up
|
||
test/acceptance/functional.sh | tee functional.txt # RESULT: 89/89 PASS
|
||
test/acceptance/lp_run.sh | tee lp_local.txt # TOTAL mismatching pixels: 0 -> PASS
|
||
$PYTHON test/acceptance/lp_verify.py --base-url https://dssgis.dwh.kz --run <run-id> | tee lp_prod.txt
|
||
test/acceptance/load.sh | tee load.txt # критерии K1–K4 выполнены
|
||
test/acceptance/scan.sh | tee scan.txt # RESULT: 0 High / 0 Critical -> PASS
|
||
test/acceptance/stand.sh down
|
||
```
|
||
|
||
Тестовые векторные данные `data/` пересоздаются командой
|
||
`python3 test/acceptance/lib/make_fixtures.py` (нужен `ogr2ogr`).
|