235 lines
12 KiB
Bash
Executable File
235 lines
12 KiB
Bash
Executable File
#!/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 — все сценарии пройдены"
|