-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_auth.py
More file actions
184 lines (132 loc) · 7.63 KB
/
test_auth.py
File metadata and controls
184 lines (132 loc) · 7.63 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import pytest
from unittest.mock import Mock, patch, MagicMock
from modules.auth.mvc.auth_model import AuthModel
from modules.auth.mvc.auth_controller import AuthController
class TestAuthModel:
@pytest.fixture
def model(self):
"""Fixture to create AuthModel with mocked dependencies."""
with patch("modules.auth.mvc.auth_model.load_config", return_value={}), \
patch("modules.auth.mvc.auth_model.APIClient") as MockAPIClient, \
patch("modules.auth.mvc.auth_model.get_app_data_dir") as mock_app_dir, \
patch("modules.auth.mvc.auth_model.get_local_data_dir") as mock_local_dir:
mock_app_dir.return_value = MagicMock()
mock_local_dir.return_value = MagicMock()
model = AuthModel()
# APIClient is instantiated in __init__, so we get the mock instance
model.api = MockAPIClient.return_value
return model
def test_login(self, model):
"""Test login method calls API correctly."""
model.api.login.return_value = {"token": "123"}
assert model.login("test@test.com", "pass") == {"token": "123"}
model.api.login.assert_called_once_with(email="test@test.com", password="pass")
def test_signup(self, model):
"""Test signup method calls API correctly."""
model.api.signup.return_value = {"status": "ok"}
assert model.signup("123456", "test@test.com", "pass") == {"status": "ok"}
model.api.signup.assert_called_once_with(code="123456", email="test@test.com", password="pass")
def test_save_user(self, model):
"""Test saving user data to keyring and file."""
user_data = {
"user": {"id": 1, "email": "test@test.com", "username": "User", "department": "Dept"},
"access_token": "acc",
"refresh_token": "ref"
}
with patch("modules.auth.mvc.auth_model.keyring.set_password") as mock_keyring, \
patch("builtins.open", new_callable=MagicMock) as mock_open, \
patch("json.dump") as mock_json_dump:
model.save_user(user_data, True)
# Check keyring
assert mock_keyring.call_count == 2
mock_keyring.assert_any_call(service_name="Documents Exp", username="access_token_1", password="acc")
mock_keyring.assert_any_call(service_name="Documents Exp", username="refresh_token_1", password="ref")
# Check file writing (user data and last logged)
assert mock_open.call_count >= 2
def test_logout(self, model):
"""Test logout clears keyring and deletes last logged file."""
# Configure the mock path object to simulate file existence
model.LOCAL_DIR_LAST_LOGGED.exists.return_value = True
with patch("modules.auth.mvc.auth_model.read_json", return_value={"user_id": 1}), \
patch("modules.auth.mvc.auth_model.keyring.delete_password") as mock_keyring_del:
model.logout()
assert mock_keyring_del.call_count == 2
model.LOCAL_DIR_LAST_LOGGED.unlink.assert_called_once()
def test_verify_token(self, model):
"""Test token verification logic."""
with patch("modules.auth.mvc.auth_model.read_json", return_value={"user_id": 1}), \
patch("modules.auth.mvc.auth_model.keyring.get_password", return_value="valid_token"):
model.api.verify.return_value = {"user": "data"}
result = model.verify_token()
assert result == {"user": "data"}
model.api.verify.assert_called_once_with(token="valid_token")
class TestAuthController:
@pytest.fixture
def controller(self, qapp):
"""Fixture to create AuthController with mocked MVC components."""
model = Mock()
view = Mock()
window = Mock()
# Mock view pages structure
view.login_page = Mock()
view.signup_page = Mock()
view.forgot_page_email_confirm = Mock()
view.forgot_page_reset_password = Mock()
view.pages = {}
# Mock NotificationService to avoid errors during init
with patch("modules.auth.mvc.auth_controller.NotificationService"), \
patch("modules.auth.mvc.auth_controller.FieldValidator") as mock_validator:
ctrl = AuthController(model, view, window)
# Attach validator mock to controller instance for easy access in tests
ctrl.field_validator = mock_validator.return_value
yield ctrl
def test_login_flow(self, controller):
"""Test login button click initiates worker with correct params."""
# Setup view data
controller.view.login_page.get_email.return_value = "test@test.com"
controller.view.login_page.get_password.return_value = "password"
# Mock worker creation to execute immediately or check call
with patch.object(controller, "_create_worker") as mock_worker:
controller.on_login_page_login_button_clicked()
mock_worker.assert_called_once()
args, kwargs = mock_worker.call_args
assert kwargs['method'] == controller.model.login
assert kwargs['email'] == "test@test.com"
assert kwargs['password'] == "password"
def test_login_success_callback(self, controller):
"""Test login success callback saves user and emits signal."""
data = {"user": {"id": 1}}
controller.view.login_page.get_auto_login_state.return_value = True
controller.model.save_user.return_value = 1
# Mock signal
mock_signal = Mock()
controller.login_successful.connect(mock_signal)
controller.login_user(data)
controller.model.save_user.assert_called_once_with(user_data=data, auto_login=True)
controller.view.login_page.clear_lineedits.assert_called_once()
mock_signal.assert_called_once_with("auth", 1)
def test_signup_flow(self, controller):
"""Test signup button click initiates code sending."""
controller.view.signup_page.get_email.return_value = "new@test.com"
controller.view.signup_page.get_password.return_value = "pass"
with patch.object(controller, "_create_worker") as mock_worker:
controller.on_signup_page_create_button_clicked()
mock_worker.assert_called_once()
args, kwargs = mock_worker.call_args
assert kwargs['method'] == controller.model.signup_send_code
assert kwargs['email'] == "new@test.com"
def test_validation_login(self, controller):
"""Test login form validation enables submit button."""
# Setup validator mock
controller.field_validator.validate_email.return_value = True
controller.field_validator.validate_password.return_value = True
controller.view.login_page.get_email.return_value = "valid@email.com"
controller.view.login_page.get_password.return_value = "ValidPass1!"
controller.on_login_page_lineedits_changed()
controller.view.login_page.update_submit_button_state.assert_called_with(state=True)
def test_validation_login_invalid(self, controller):
"""Test login form validation fails with invalid email."""
controller.field_validator.validate_email.return_value = False
controller.view.login_page.get_email.return_value = "invalid"
controller.on_login_page_lineedits_changed()
controller.view.login_page.update_submit_button_state.assert_not_called()