import unittest from unittest.mock import MagicMock from services.scoped_auth_client import ScopedAuthClient class TestScopedAuthClient(unittest.TestCase): def setUp(self): self.mock_transport = MagicMock() self.mock_session_store = MagicMock() self.client = ScopedAuthClient( base_url='http://test.example.com', client_type='workbuddy', session_store=self.mock_session_store, transport=self.mock_transport, timeout=10 ) def test_build_session_key(self): gateway_session_id = 'GWS_abc123' session_key = ScopedAuthClient.build_session_key(gateway_session_id) self.assertEqual(session_key, 'gws_GWS_abc123') def test_exchange_success(self): gateway_session_id = 'GWS_test123' auth_code = 'AC_xyz789' # Mock successful response self.mock_transport.post_json.return_value = { 'code': 'MCP_0000', 'msg': 'success', 'data': { 'mcp_token': 'MT_token123', 'admin_id': 456, 'company_id': 200, 'expire_time': '2099-12-31 23:59:59', } } result = self.client.exchange(gateway_session_id, auth_code) # Verify transport call self.mock_transport.post_json.assert_called_once_with( 'http://test.example.com/mcp/auth/exchange', { 'auth_code': auth_code, 'client_type': 'workbuddy', 'session_key': 'gws_GWS_test123', }, {}, 10 ) # Verify session store save self.mock_session_store.save.assert_called_once() save_call_args = self.mock_session_store.save.call_args self.assertEqual(save_call_args[0][0], gateway_session_id) saved_session = save_call_args[0][1] self.assertEqual(saved_session['session_key'], 'gws_GWS_test123') self.assertEqual(saved_session['mcp_token'], 'MT_token123') self.assertEqual(saved_session['admin_id'], 456) self.assertEqual(saved_session['company_id'], 200) self.assertEqual(saved_session['client_type'], 'workbuddy') self.assertEqual(saved_session['expire_time'], '2099-12-31 23:59:59') # Verify result self.assertEqual(result['code'], 'MCP_0000') def test_exchange_failure_no_token(self): gateway_session_id = 'GWS_fail' auth_code = 'AC_invalid' # Mock failure response self.mock_transport.post_json.return_value = { 'code': 'MCP_1001', 'msg': 'auth_code invalid', 'data': {} } result = self.client.exchange(gateway_session_id, auth_code) # Should not save to session store self.mock_session_store.save.assert_not_called() # Should still return the response self.assertEqual(result['code'], 'MCP_1001') def test_exchange_partial_data(self): gateway_session_id = 'GWS_partial' auth_code = 'AC_partial' # Mock response with missing admin_id and company_id self.mock_transport.post_json.return_value = { 'code': 'MCP_0000', 'msg': 'success', 'data': { 'mcp_token': 'MT_token456', 'expire_time': '2099-12-31 23:59:59', # Missing admin_id and company_id } } result = self.client.exchange(gateway_session_id, auth_code) # Should still save, with None values self.mock_session_store.save.assert_called_once() saved_session = self.mock_session_store.save.call_args[0][1] self.assertEqual(saved_session['mcp_token'], 'MT_token456') self.assertIsNone(saved_session['admin_id']) self.assertIsNone(saved_session['company_id']) def test_exchange_no_data_field(self): gateway_session_id = 'GWS_nodata' auth_code = 'AC_nodata' # Mock response without data field self.mock_transport.post_json.return_value = { 'code': 'MCP_9999', 'msg': 'error' } result = self.client.exchange(gateway_session_id, auth_code) # Should not save self.mock_session_store.save.assert_not_called() def test_exchange_strips_base_url_trailing_slash(self): client_with_slash = ScopedAuthClient( base_url='http://test.example.com/', client_type='workbuddy', session_store=self.mock_session_store, transport=self.mock_transport, timeout=10 ) self.mock_transport.post_json.return_value = { 'code': 'MCP_0000', 'data': {'mcp_token': 'MT_test'} } client_with_slash.exchange('GWS_test', 'AC_test') # Should call without double slash call_url = self.mock_transport.post_json.call_args[0][0] self.assertEqual(call_url, 'http://test.example.com/mcp/auth/exchange') if __name__ == '__main__': unittest.main()