Files
pvemanager/backend/tests
Dilshod Rasulov 972d54d1f1
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Failing after 1m44s
Deploy to Production / Trigger production update (push) Failing after 11s
SonarCloud / SonarCloud Analysis (push) Failing after 3m0s
fix(ipam): keep extra addresses across a guest reboot
Testing on a live AlmaLinux container showed the alias disappearing after
a restart even though it was reported as applied: Proxmox regenerates the
guest network configuration on every start (pve-container rewrites
/etc/network/interfaces, ifcfg or the NM keyfile from netN), wiping any
address added to the native stack.

    before reboot: address1=10.10.10.103/24 address2=10.10.10.106/24
    after reboot:  addresses=10.10.10.103/24     (file recreated by PVE)

Install a oneshot systemd unit that re-adds the recorded addresses after
network-online.target — /etc/systemd is not touched by Proxmox. The native
branches stay, so persist is now reported as e.g. "nm+systemd"; the unit
and its helper are removed once the last address is released.

Also fixed while testing on the two test instances:
- the alias script is ASCII-only now: it travels through the Proxmox API
  and its marker ends up in guest system configs
- a composite persist marker no longer falls through the parser, which
  used to report a successful apply as runtime_only
- allocate_ip marks the first address of a guest as primary on its own,
  so allocations coming from the creation wizard are flagged correctly
2026-08-22 01:36:10 +05:00
..

Backend Tests

Smoke / unit tests for the FastAPI backend, focused on the security-critical modules: app.crypto, app.auth, app.rbac.

Layout

backend/
├── pytest.ini              # pytest config (testpaths=tests, markers, addopts)
├── requirements-dev.txt    # test deps (-r requirements.txt + pytest …)
└── tests/
    ├── conftest.py         # env setup + reusable fixtures
    ├── test_crypto.py      # Fernet field-encryption helpers
    ├── test_auth.py        # password hashing, JWT, IP parsing, perm helpers
    └── test_rbac.py        # PermissionEngine + registry

How conftest.py keeps tests hermetic

The application reads configuration at import time:

  • app.config.Settings is a pydantic-settings model that auto-loads .env from the current working directory.
  • app.db constructs the SQLAlchemy engine on import.

tests/conftest.py therefore, before any app.* import:

  1. Sets DATABASE_URL, SECRET_KEY, ADMIN_PASSWORD, LOG_LEVEL to safe test values via os.environ.setdefault.
  2. Generates a fresh FERNET_KEY so encrypt/decrypt round-trips succeed.
  3. Adds backend/ to sys.path.
  4. chdirs to a temp directory so the developer's local .env (which contains keys not declared on Settings) is not picked up. Without this step pydantic raises extra_forbidden.

No real Postgres connection is opened — SQLAlchemy connects lazily, and these tests never issue a query.

Running locally

There is no system-wide Python on this host, so use the same Python image as CI:

docker run --rm -v "$PWD/backend:/app" -w /app python:3.12-slim bash -c '
  pip install -r requirements-dev.txt &&
  python -m pytest -p no:warnings
'

Run a single test:

python -m pytest tests/test_auth.py::TestJWT -v

CI

GitHub Actions runs the suite on every push / PR to main that touches backend/** — see .github/workflows/backend-tests.yml. Coverage is reported to the job log and uploaded as an artifact.

Adding more tests

  • Pure / domain logic (no DB): use the fake_user_factory / fake_role_factory fixtures in conftest.py. They produce SimpleNamespace objects that satisfy duck-typed access in auth.py and rbac/engine.py.
  • Touches the DB: build out a SQLite-backed fixture (override app.db.engine with sqlite:///:memory: and Base.metadata.create_all). Not required for the current smoke layer.
  • API endpoints: spin up TestClient(app) once get_db and any external Proxmox / SSH dependencies are dependency-overridden.

Markers

Defined in pytest.ini:

  • @pytest.mark.smoke — quickest sanity checks.
  • @pytest.mark.unit — pure unit tests.
  • @pytest.mark.integration — multi-module integration.

Run only smoke tests:

python -m pytest -m smoke