arm-ka-docs/scripts/test_sync_statements.sh
2026-10-18 19:10:11 +04:00

235 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Тест sync-statements.sh на синтетических репозиториях (bare origin + клон), без tea и сети.
# Разделы: помощник statements_check.py; дальше — сценарии самого скрипта (дописываются по задачам плана).
# Запуск: make test-sync-statements (или напрямую из корня docs; интерпретатор — STATEMENTS_PY, по умолчанию python3).
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
PY=${STATEMENTS_PY:-python3}
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
export GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@t GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@t
if ! "$PY" -c 'import tomllib, yaml' 2>/dev/null; then
echo "нужен Python >= 3.11 с PyYAML: выполните make venv (или задайте STATEMENTS_PY)" >&2
exit 1
fi
fail=0
check() { if [ "$1" = 0 ]; then echo "ok: $2"; else echo "FAIL: $2" >&2; fail=1; fi; }
put() { mkdir -p "$(dirname "$1")"; printf '%s\n' "${2:-x}" > "$1"; }
# shellcheck disable=SC2317
commit_push() { git -C "$1" add -A; git -C "$1" commit -qm "$2"; git -C "$1" push -q origin "HEAD:$3" 2>/dev/null; git -C "$1" fetch -q origin; }
# --- помощник statements_check.py ---------------------------------------------------------------------------------
H="$WORK/h"; mkdir -p "$H"
CHK="$HERE/statements_check.py"
# run <rc-ожидаемый> <описание> <аргументы…>: вывод — в $OUT
run() {
local want=$1 desc=$2 rc=0; shift 2
OUT=$("$PY" "$CHK" "$@" 2>&1) || rc=$?
check "$([ "$rc" = "$want" ] && echo 0 || echo 1)" "$desc (rc=$rc, ожидался $want)"
}
has() { grep -q -- "$1" <<<"$OUT"; }
put "$H/pp.toml" $'[project]\ndependencies = ["PyYAML==6.0.3"]'
put "$H/r1.txt" $'aiohttp==3.14.3\nPyYAML==6.0.3'
run 0 "deps: есть" deps "$H/pp.toml" "$H/r1.txt"
check "$([ -z "$OUT" ] && echo 0 || echo 1)" "deps: есть — вывод пустой"
put "$H/r2.txt" $'aiohttp==3.14.3\npyyaml==6.0.3'
run 0 "deps: регистр" deps "$H/pp.toml" "$H/r2.txt"
put "$H/pp_u.toml" $'[project]\ndependencies = ["Py_YAML==6.0.3"]'
put "$H/r2u.txt" $'py-yaml==6.0.3'
run 0 "deps: подчёркивание и дефис равны" deps "$H/pp_u.toml" "$H/r2u.txt"
put "$H/r3.txt" $'aiohttp==3.14.3\n# PyYAML==6.0.3\n-r other.txt'
run 20 "deps: нет" deps "$H/pp.toml" "$H/r3.txt"
check "$(has PyYAML && echo 0 || echo 1)" "deps: нет — в выводе PyYAML"
put "$H/r4.txt" $'PyYAML==6.0.4'
run 0 "deps: другая версия" deps "$H/pp.toml" "$H/r4.txt"
check "$(has 'примечание:' && has 6.0.4 && echo 0 || echo 1)" "deps: другая версия — примечание с 6.0.4"
put "$H/pp_none.toml" $'[tool.ruff]\nline-length = 120'
run 0 "deps: без [project]" deps "$H/pp_none.toml" "$H/r1.txt"
put "$H/pp_jwt.toml" $'[project]\ndependencies = ["PyJWT==2.8.0"]'
put "$H/r5.txt" $'PyJWT[crypto]==2.8.0'
run 0 "deps: экстры хоста" deps "$H/pp_jwt.toml" "$H/r5.txt"
put "$H/orders.yaml" 'version: 1
repeats: {}
editions:
66n:
BALANCE: {full: ["1110", "1100"], simplified: ["1100"]}
INCOME: {full: ["2110"], simplified: ["2110"]}
fsbu4:
BALANCE: {full: ["1110", "1100"], simplified: ["1100"]}
INCOME: {full: ["2110"], simplified: ["2110"]}'
put "$H/lines.yaml" 'editions:
- id: 66n
forms:
BALANCE:
- {code: "1110", name: "Основные средства"}
- {code: "1100", name: "Итого"}
- id: fsbu4
forms:
BALANCE:
- {code: "1110", name: "Основные средства"}'
run 0 "lines: все коды есть" lines "$H/lines.yaml" "$H/orders.yaml"
put "$H/lines_x.yaml" 'editions:
- id: 66n
forms:
BALANCE:
- {code: "1110", name: "а"}
- id: fsbu4
forms:
BALANCE:
- {code: "1110", name: "а"}
- {code: "1120", name: "б"}'
run 20 "lines: код вне перечня" lines "$H/lines_x.yaml" "$H/orders.yaml"
check "$(has 1120 && has fsbu4 && echo 0 || echo 1)" "lines: код вне перечня — в выводе 1120 и fsbu4"
put "$H/orders_s.yaml" 'version: 1
editions:
fsbu4:
BALANCE: {full: ["1110"], simplified: ["1100"]}'
put "$H/lines_s.yaml" 'editions:
- id: fsbu4
forms:
BALANCE:
- {code: "1100", name: "а"}'
run 0 "lines: код только в simplified" lines "$H/lines_s.yaml" "$H/orders_s.yaml"
put "$H/bad.yaml" 'editions: ['
run 1 "битый YAML" lines "$H/bad.yaml" "$H/orders.yaml"
run 2 "аргументы"
# --- sync-statements.sh --check -----------------------------------------------------------------------------------
SYNC="$HERE/sync-statements.sh"
export STATEMENTS_PY="$PY"
SRC="$WORK/arm-statements"
git init -q --bare "$WORK/arm-statements.git"
git clone -q "$WORK/arm-statements.git" "$SRC" 2>/dev/null
git -C "$SRC" checkout -q -b main
put "$SRC/pyproject.toml" $'[project]\nname = "arm-statements"\ndependencies = ["PyYAML==6.0.3"]'
put "$SRC/README.md" "пакет"
put "$SRC/arm_statements/__init__.py" "# init"
put "$SRC/arm_statements/steps.py" "def step(): return 1"
mkdir -p "$SRC/arm_statements/rules"; cp "$H/orders.yaml" "$SRC/arm_statements/rules/statement_code_orders.yaml"
put "$SRC/arm_statements/tests/__init__.py" "# t"
put "$SRC/arm_statements/tests/test_steps.py" "def test_step(): pass"
commit_push "$SRC" "S1" main
S1=$(git -C "$SRC" rev-parse HEAD)
mkdir -p "$WORK/docs/contracts" "$WORK/docs-empty" "$WORK/wt"
cp "$H/orders.yaml" "$WORK/docs/contracts/statement-code-orders.yaml"
# make_host <каталог> <имя origin>: bare + клон с копией S1 в api/arm_statements, requirements и (для arm-core) словарём
make_host() {
local dir=$1 name=$2
git init -q --bare "$WORK/$name.git"
git clone -q "$WORK/$name.git" "$dir" 2>/dev/null
git -C "$dir" checkout -q -b main
put "$dir/api/requirements.txt" $'aiohttp==3.14.3\nPyYAML==6.0.3'
mkdir -p "$dir/api"
git -C "$SRC" archive "$S1" arm_statements | tar -x -C "$dir/api"
printf '%s\n' "$S1" > "$dir/api/arm_statements/VERSION"
}
make_host "$WORK/arm-core" arm-core
mkdir -p "$WORK/arm-core/api/rules"; cp "$H/lines.yaml" "$WORK/arm-core/api/rules/statement_lines.yaml"
commit_push "$WORK/arm-core" "copy S1" main
make_host "$WORK/tabular-parser" arm-tabular-parser
commit_push "$WORK/tabular-parser" "copy S1" main
make_host "$WORK/scan-gateway" arm-scan-gateway
mkdir -p "$WORK/scan-gateway/api/rules"
put "$WORK/scan-gateway/api/rules/statement_lines.yaml" $'editions:\n - id: fsbu4\n forms:\n BALANCE:\n - {code: "9999", name: "х"}'
commit_push "$WORK/scan-gateway" "copy S1" main
COMMON=(--source "$WORK/arm-statements" --docs "$WORK/docs" --wt-root "$WORK/wt" --core-dir "$WORK/arm-core")
AC="$WORK/arm-core/api"
reset_hosts() { local h; for h in arm-core tabular-parser scan-gateway; do git -C "$WORK/$h" checkout -q -- . && git -C "$WORK/$h" clean -fdq; done; }
# sync <rc> <описание> <аргументы…>: вывод — в $OUT
sync() {
local want=$1 desc=$2 rc=0; shift 2
OUT=$("$SYNC" "${COMMON[@]}" "$@" 2>&1) || rc=$?
check "$([ "$rc" = "$want" ] && echo 0 || echo 1)" "$desc (rc=$rc, ожидался $want)"
}
sync 0 "check: синхронно" --check --host-dir "$WORK/arm-core"
check "$(has 'statements-check arm-core: 0' && echo 0 || echo 1)" "check: синхронно — итоговая строка"
sync 0 "check: имя по origin, словарь не у scan-gateway" --check --host-dir "$WORK/scan-gateway"
check "$(has 'scan-gateway' && ! has 9999 && echo 0 || echo 1)" "check: scan-gateway — в выводе имя, нет 9999"
OUT=$(cd "$WORK/tabular-parser" && "$SYNC" --check --host-dir . "${COMMON[@]}" 2>&1) && rc=0 || rc=$?
check "$([ "$rc" = 0 ] && has 'statements-check tabular-parser: 0' && echo 0 || echo 1)" "check: --host-dir . из каталога хоста (rc=$rc)"
sync 0 "check: --host вместо origin" --check --host scan-gateway --host-dir "$WORK/scan-gateway"
sync 2 "check: неизвестное имя --host" --check --host foo --host-dir "$WORK/arm-core"
TP="$WORK/tabular-parser/api/arm_statements"
mkdir -p "$TP/__pycache__" "$TP/tests/.pytest_cache" "$TP/.mypy_cache"
: > "$TP/__pycache__/steps.cpython-311.pyc"; : > "$TP/tests/.pytest_cache/x"; : > "$TP/.mypy_cache/x"; : > "$TP/.DS_Store"
sync 0 "check: кэши в копии игнорируются" --check --host-dir "$WORK/tabular-parser"
reset_hosts
echo "# правка" >> "$AC/arm_statements/steps.py"
sync 20 "check: изменён файл" --check --host-dir "$WORK/arm-core"
check "$(has steps.py && has 'прямая правка' && echo 0 || echo 1)" "check: изменён файл — steps.py и «прямая правка»"
reset_hosts
put "$AC/arm_statements/extra.py" "x = 1"
sync 20 "check: добавлен файл" --check --host-dir "$WORK/arm-core"
check "$(has extra.py && echo 0 || echo 1)" "check: добавлен файл — extra.py"
reset_hosts
rm "$AC/arm_statements/tests/test_steps.py"
sync 20 "check: удалён файл" --check --host-dir "$WORK/arm-core"
check "$(has test_steps.py && echo 0 || echo 1)" "check: удалён файл — test_steps.py"
reset_hosts
rm "$AC/arm_statements/VERSION"
sync 20 "check: нет VERSION" --check --host-dir "$WORK/arm-core"
reset_hosts
printf '%s\n' 0000000000000000000000000000000000000000 > "$AC/arm_statements/VERSION"
sync 20 "check: неизвестный VERSION" --check --host-dir "$WORK/arm-core"
reset_hosts
rm -rf "$AC/arm_statements"
sync 20 "check: нет копии" --check --host-dir "$WORK/arm-core"
check "$(has 'копии нет' && echo 0 || echo 1)" "check: нет копии — сообщение"
reset_hosts
git -C "$SRC" checkout -q -b feat/x
echo "# F" >> "$SRC/arm_statements/steps.py"
commit_push "$SRC" "F" feat/x
F=$(git -C "$SRC" rev-parse HEAD)
git -C "$SRC" checkout -q main
git -C "$SRC" archive "$F" arm_statements | tar -x -C "$AC"
printf '%s\n' "$F" > "$AC/arm_statements/VERSION"
sync 20 "check: чужая версия" --check --host-dir "$WORK/arm-core"
check "$(has 'чужая версия' && echo 0 || echo 1)" "check: чужая версия — сообщение"
reset_hosts
put "$AC/requirements.txt" "aiohttp==3.14.3"
sync 20 "check: нет зависимости" --check --host-dir "$WORK/arm-core"
check "$(has PyYAML && echo 0 || echo 1)" "check: нет зависимости — PyYAML"
reset_hosts
cp "$H/lines_x.yaml" "$AC/rules/statement_lines.yaml"
sync 20 "check: код словаря вне перечня" --check --host-dir "$WORK/arm-core"
check "$(has 1120 && has fsbu4 && echo 0 || echo 1)" "check: код вне перечня — 1120 и fsbu4"
reset_hosts
echo "# новый код" >> "$WORK/docs/contracts/statement-code-orders.yaml"
sync 10 "check: перечень в docs новее" --check --host-dir "$WORK/arm-core"
check "$(has contracts/statement-code-orders.yaml && has 'statements-check arm-core: 10' && echo 0 || echo 1)" "check: перечень в docs — в выводе файл и итог 10"
cp "$H/orders.yaml" "$WORK/docs/contracts/statement-code-orders.yaml"
sync 10 "check: перечня нет в docs" --check --host-dir "$WORK/arm-core" --docs "$WORK/docs-empty"
sync 2 "check: нет источника" --check --host-dir "$WORK/arm-core" --source "$WORK/nope"
sync 2 "check: нет хоста" --check --host-dir "$WORK/nope"
sync 2 "check: неизвестный аргумент" --check --bogus
sync 2 "check: нет --host/--host-dir" --check
sync 2 "check: --ref не разрешается" --check --host-dir "$WORK/arm-core" --ref origin/nope
sync 2 "--in-place вместе с --dry-run" --in-place --dry-run --host-dir "$WORK/arm-core"
sync 0 "check важнее --dry-run и --in-place" --in-place --dry-run --check --host-dir "$WORK/arm-core"
# проверки с S2 — последними
put "$SRC/README.md" "пакет, правка"
commit_push "$SRC" "S2 правка README" main
sync 10 "check: отстала" --check --host-dir "$WORK/arm-core"
check "$(has 'копия отстала' && has 'S2 правка README' && echo 0 || echo 1)" "check: отстала — сообщение и тема S2"
echo "# правка" >> "$AC/arm_statements/steps.py"
sync 20 "check: правка + отставание = 20" --check --host-dir "$WORK/arm-core"
reset_hosts
[ "$fail" = 0 ] || exit 1
echo "ok: sync-statements.sh — все сценарии пройдены"