fix(reinstall): use OSTemplate.vmid (not template_vmid) and convert memory bytes->MB
- AttributeError: OSTemplate has no 'template_vmid' field; the actual column is 'vmid'. - Use OSTemplate.get_vmid_for_node() to support cross-node replicated templates, falling back to source_node/vmid otherwise. - VMInstance.memory is stored in bytes, but Proxmox config expects MB.
This commit is contained in:
69
backend/tests/test_crypto.py
Normal file
69
backend/tests/test_crypto.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""Smoke tests for ``app.crypto`` field-level encryption helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def crypto():
|
||||
return importlib.import_module("app.crypto")
|
||||
|
||||
|
||||
class TestEncryptDecryptRoundtrip:
|
||||
@pytest.mark.smoke
|
||||
def test_roundtrip_simple_string(self, crypto):
|
||||
token = crypto.encrypt_value("hunter2")
|
||||
assert token != "hunter2"
|
||||
assert crypto.decrypt_value(token) == "hunter2"
|
||||
|
||||
@pytest.mark.smoke
|
||||
def test_roundtrip_unicode(self, crypto):
|
||||
secret = "пароль-密码-🔐"
|
||||
assert crypto.decrypt_value(crypto.encrypt_value(secret)) == secret
|
||||
|
||||
def test_empty_value_passthrough(self, crypto):
|
||||
assert crypto.encrypt_value("") == ""
|
||||
assert crypto.decrypt_value("") == ""
|
||||
assert crypto.encrypt_value(None) is None
|
||||
assert crypto.decrypt_value(None) is None
|
||||
|
||||
def test_each_call_produces_unique_ciphertext(self, crypto):
|
||||
# Fernet embeds a random IV — same plaintext should encrypt differently.
|
||||
a = crypto.encrypt_value("same")
|
||||
b = crypto.encrypt_value("same")
|
||||
assert a != b
|
||||
assert crypto.decrypt_value(a) == crypto.decrypt_value(b) == "same"
|
||||
|
||||
|
||||
class TestLegacyPlaintextFallback:
|
||||
def test_decrypt_invalid_token_returns_input(self, crypto):
|
||||
# Legacy/un-encrypted rows must not blow up — fall through to plaintext.
|
||||
assert crypto.decrypt_value("plain-value-not-fernet") == "plain-value-not-fernet"
|
||||
|
||||
|
||||
class TestNoFernetKey:
|
||||
def test_encrypt_passthrough_without_key(self, crypto, monkeypatch):
|
||||
# ``settings`` is imported lazily inside ``_get_fernet`` — patch the live
|
||||
# singleton on the config module instead.
|
||||
from app import config as app_config
|
||||
|
||||
monkeypatch.setattr(app_config.settings, "FERNET_KEY", None, raising=False)
|
||||
monkeypatch.setattr(crypto, "_warned_no_key", False, raising=False)
|
||||
assert crypto.encrypt_value("plain") == "plain"
|
||||
assert crypto.decrypt_value("plain") == "plain"
|
||||
|
||||
|
||||
class TestEncryptedStringColumn:
|
||||
def test_bind_and_result_processors(self, crypto):
|
||||
col = crypto.EncryptedString(255)
|
||||
bound = col.process_bind_param("topsecret", dialect=None)
|
||||
assert bound != "topsecret"
|
||||
assert col.process_result_value(bound, dialect=None) == "topsecret"
|
||||
|
||||
def test_bind_and_result_handle_none(self, crypto):
|
||||
col = crypto.EncryptedString(255)
|
||||
assert col.process_bind_param(None, dialect=None) is None
|
||||
assert col.process_result_value(None, dialect=None) is None
|
||||
Reference in New Issue
Block a user