Continuation of the previous commit (gdrive_backup.py removal landed separately by accident) — adds the new module itself, the admin routes/template using it, generalized Setting keys, updated docs/ provisioning notes, and tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.3 KiB
Automated tests
A repo-committed pytest suite lives under tests/. It replaces the
ad-hoc "write a one-off script to a scratch temp dir, run it by hand"
verification method used throughout this project's development — every
test here follows the same pattern those scripts used (a fresh temp
SQLite DB, create_app(), db.create_all() + migrate() + seed(),
monkeypatching a module's send_message / fiscalize_in_background /
get_invoice_status attribute directly instead of pulling in a mocking
framework), just organized into real fixtures and committed so it runs in
CI / before every deploy instead of living only in someone's memory.
Running it
pip install -r requirements-dev.txt
pytest
pytest.ini at the repo root points pytest at tests/ and needs no
extra flags — just run it from the repo root (or the worktree root).
Each test gets its own fresh temp SQLite database file (created and torn
down per test in tests/conftest.py's app fixture) — slower than a
single shared DB, but immune to one test's leftover state (an order, a
lockout, a supplier) leaking into another. Module-level shared state used
intentionally in production for cross-request coordination (login
lockouts, the error-alert cooldown, the Checkbox token cache) is also
explicitly reset between tests — see _reset_module_level_state() in
conftest.py.
What's covered
| Module | Test file | What's tested |
|---|---|---|
bober_bbq/payments/monobank.py |
tests/test_payments.py |
claim_order_paid() atomicity (first caller wins, second loses); the Monobank webhook, checkout poll, and reconciliation job never double-fire paid-order side effects for the same invoice |
bober_bbq/utils/checkbox_prro.py |
tests/test_fiscalization_alerts.py |
_mark_error() alerts every owner, records fiscal_status/fiscal_error, and respects error_alerts' per-(exception, source) cooldown |
bober_bbq/utils/login_throttle.py + /admin/login |
tests/test_login_throttle.py |
Lockout after MAX_ATTEMPTS wrong passwords from one IP (even the correct password is then rejected), an unaffected sibling IP, lockout expiry, and clearing the failure counter on success |
run_web._warn_on_insecure_defaults |
tests/test_startup_checks.py |
Alerts the owner when SECRET_KEY/FLASK_ADMIN_PASSWORD are still the hardcoded defaults; stays silent once overridden |
bober_bbq/utils/supplier_import.py |
tests/test_supplier_import_security.py |
_resolve_upload_path() rejects absolute paths (both slash styles), ..//..\ traversal, a wrong-shape-but-real-extension token, and an empty string — all with FileNotFoundError, none touching a file outside the upload temp dir; a legitimate token still round-trips |
bober_bbq/admin/suppliers.py |
tests/test_inventory_suppliers.py |
_apply_stock_topup() credits stock, auto-assigns Ingredient.supplier_id only when unset (never overwrites an existing link), and leaves StockMovement.note exactly as typed (regression test for a real bug where it was getting overwritten with the supplier's name) |
bober_bbq/api/checkout.py |
tests/test_checkout.py |
Delivery and pickup order creation via the checkout API produces an Order row with sane fields (type, totals, address/pickup fields, linked items) and clears the cart; an empty cart is rejected |
What's explicitly NOT covered yet
- The React webapp (
webapp/) has no automated tests at all (no unit tests, no component tests, no E2E). It's tested manually in the browser. - The Telegram bot's conversation flows (
bot/) — aiogram handlers, FSM states, inline keyboards — aren't covered beyond what the checkout HTTP API itself exercises (bot/services/order_service.pyandbot/services/cart_service.py, indirectly, viatests/test_checkout.py). A real bot conversation test would need an aiogram test harness/fakeBottransport, which doesn't exist yet. - Checkbox (ПРРО) fiscalization's happy path —
create_receipt()actually calling out to Checkbox's API (signin, shift open/close, receipt creation, PDF polling) isn't exercised; only the error-handling side (_mark_error/ alerting) is. It's allrequestscalls to a real third-party API with no local fake/sandbox available. - Monobank invoice creation (
create_invoice,verify_token, webhook-signature verification against a real Monobank public key) — only the webhook handler's payment-status logic is tested, with signature verification monkeypatched out. Testing the actual HTTP calls would need a fake Monobank server. - Remote backups' actual cloud calls (
bober_bbq/utils/backup_remote.py) —tests/test_backup_remote.pycovers the env/path construction per storage type and the rotation logic withsubprocess.runmocked, but never shells out to a realrclonebinary or a real cloud provider. - The scheduled jobs' timing/APScheduler wiring itself in
run_web.py(only the job functions they call are tested directly, e.g.reconcile_pending_payments()). - Admin panel pages beyond
/admin/login— the rest of the Jinja admin UI (order management, menu editing, reports, settings screens) has no route-level tests here;tests/test_inventory_suppliers.pycalls_apply_stock_topup()directly rather than through its HTTP route for simplicity.