Files
pvemanager/backend/tests
Dilshod Rasulov 6796903712
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Failing after 1m46s
Deploy to Production / Trigger production update (push) Failing after 10s
SonarCloud / SonarCloud Analysis (push) Failing after 3m7s
feat(ipam): several IP addresses per instance
A guest was implicitly limited to a single allocation: lookups were
"(server, vmid) -> one row", release_ip_by_vmid freed one address, and
the link tool skipped guests that already had one. An instance can now
hold a primary address plus any number of aliases, managed from the
Network tab of the instance and visible on the IPAM pages.

Schema and service:
- ipam_allocations gains is_primary, assignment_kind, target_interface
  and apply_* columns; migration 43 marks existing rows as primary
- find_allocations_by_resource/set_primary; release_ip_by_vmid now frees
  every address of a deleted guest instead of just the first one
- next-available search may scan the whole subnet when a network has no
  auto-assign pool, skipping node and cached guest addresses so it never
  hands out an address that is already live

Applying to the guest (services/guest_ip_service.py):
- reuses script_engine, so LXC goes through pct exec and VMs through the
  guest agent; the shell script synchronises the whole alias list of an
  interface, which makes add, remove and retry the same code path
- persists via ifupdown, netplan, NetworkManager or sysconfig and never
  restarts guest networking; an unknown stack degrades to runtime_only
  and an unreachable guest keeps the allocation as pending

API and UI:
- /ipam/api/guests/{server_id}/{vmid}/addresses: list, add, apply,
  primary, delete; allocations list accepts guest filters
- instance list exposes ips[] alongside the primary ip
- addresses card on the Network tab, "+N" badge in the IP column,
  primary badge in the allocations table

Fixes found along the way:
- link-allocations registers every address of a guest, carries the MAC
  over, refuses to take an address away from another live guest and no
  longer matches allocations by resource name (which leaked an address
  between same-named guests on different servers)
- IPAM history no longer fails to serialise datetime values, which used
  to abort re-linking with a 500
2026-08-21 23:48:20 +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