File size: 3,702 Bytes
0304d75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
tests/test_security_auth.py
============================
Unit tests for security/auth.py — JWT, bcrypt hashing, Fernet encryption.
"""

import time

import pytest
from jose import jwt, JWTError

from security.auth import (
    JWT_ALGORITHM,
    JWT_SECRET_KEY,
    create_jwt_token,
    decode_jwt_token,
    decrypt_data,
    encrypt_data,
    hash_password,
    verify_password,
)


# ---------------------------------------------------------------------------
# Password hashing (bcrypt)
# ---------------------------------------------------------------------------


class TestPasswordHashing:
    def test_hash_is_not_plaintext(self):
        hashed = hash_password("mysecret123")
        assert hashed != "mysecret123"
        assert hashed.startswith("$2b$")

    def test_verify_correct_password(self):
        hashed = hash_password("testpass")
        assert verify_password("testpass", hashed) is True

    def test_reject_wrong_password(self):
        hashed = hash_password("correct")
        assert verify_password("wrong", hashed) is False

    def test_different_hashes_for_same_password(self):
        """bcrypt uses a random salt, so two hashes of the same password differ."""
        h1 = hash_password("same")
        h2 = hash_password("same")
        assert h1 != h2
        # But both verify
        assert verify_password("same", h1)
        assert verify_password("same", h2)


# ---------------------------------------------------------------------------
# JWT tokens
# ---------------------------------------------------------------------------


class TestJWT:
    def test_create_and_decode(self):
        token = create_jwt_token({"sub": "alice"})
        payload = decode_jwt_token(token)
        assert payload["sub"] == "alice"
        assert "exp" in payload

    def test_token_contains_custom_data(self):
        token = create_jwt_token({"sub": "bob", "role": "admin"})
        payload = decode_jwt_token(token)
        assert payload["role"] == "admin"

    def test_expired_token_raises(self):
        from datetime import timedelta

        token = create_jwt_token(
            {"sub": "charlie"}, expires_delta=timedelta(seconds=-1)
        )
        with pytest.raises(Exception):
            decode_jwt_token(token)

    def test_tampered_token_raises(self):
        token = create_jwt_token({"sub": "dave"})
        # Tamper with the token
        tampered = token[:-5] + "XXXXX"
        with pytest.raises(Exception):
            decode_jwt_token(tampered)


# ---------------------------------------------------------------------------
# Fernet encryption (AES-256)
# ---------------------------------------------------------------------------


class TestFernetEncryption:
    def test_encrypt_decrypt_dict(self):
        data = {"scores": [0.5, 0.8], "timestamps": [1000.0, 2000.0]}
        encrypted = encrypt_data(data)
        assert isinstance(encrypted, str)
        assert encrypted != str(data)
        decrypted = decrypt_data(encrypted)
        assert decrypted == data

    def test_encrypt_decrypt_list(self):
        data = [[1000.0, 0.5], [2000.0, 0.8]]
        encrypted = encrypt_data(data)
        decrypted = decrypt_data(encrypted)
        assert decrypted == data

    def test_encrypted_is_not_plaintext(self):
        data = {"secret": "value"}
        encrypted = encrypt_data(data)
        assert "secret" not in encrypted
        assert "value" not in encrypted

    def test_decrypt_corrupted_returns_none(self):
        result = decrypt_data("not-a-valid-ciphertext")
        assert result is None

    def test_encrypt_empty_list(self):
        encrypted = encrypt_data([])
        assert decrypt_data(encrypted) == []