"""TDD red tests for S1 crypto (QUALITY-125, SETTINGS-20/25).""" import base64 import os import tempfile import pytest from backend import crypto def _valid_key_b64() -> str: # 32 raw bytes base64 raw = b"0123456789abcdef0123456789abcdef" # exactly 32 return base64.b64encode(raw).decode() def test_crypto_roundtrip_encrypt_decrypt(monkeypatch): """Given: valid SLAB_AES_KEY; When: encrypt then decrypt; Then: original value (QUALITY-125).""" key_b64 = _valid_key_b64() monkeypatch.setenv("SLAB_AES_KEY", key_b64) plaintext = "my-secret-app-password-123" ct_b64 = crypto.encrypt(plaintext) assert isinstance(ct_b64, str) recovered = crypto.decrypt(ct_b64) assert recovered == plaintext def test_crypto_decrypt_tampered_raises_clean_error(monkeypatch): """Given: valid key; When: tamper the blob (flip a byte); Then: raises detectable error, no crash, no raw leak.""" key_b64 = _valid_key_b64() monkeypatch.setenv("SLAB_AES_KEY", key_b64) plaintext = "secret" ct_b64 = crypto.encrypt(plaintext) # tamper: flip last byte blob = bytearray(base64.b64decode(ct_b64)) blob[-1] ^= 0xFF tampered_b64 = base64.b64encode(blob).decode() with pytest.raises(Exception) as exc: # will be specific like InvalidTag or our wrapper crypto.decrypt(tampered_b64) # must not contain the raw blob or secret msg = str(exc.value).lower() assert "secret" not in msg assert plaintext not in msg # detectable, e.g. contains "decrypt" or "tag" or "corrupt" assert any(k in msg for k in ("decrypt", "tag", "invalid", "corrupt", "fail")) def test_crypto_missing_key_raises(monkeypatch): """Given: no SLAB_AES_KEY; When: encrypt; Then: clear runtime error.""" monkeypatch.delenv("SLAB_AES_KEY", raising=False) with pytest.raises(RuntimeError, match="SLAB_AES_KEY"): crypto.encrypt("x")