atlas-chat 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. atlas/__init__.py +40 -0
  2. atlas/application/__init__.py +7 -0
  3. atlas/application/chat/__init__.py +7 -0
  4. atlas/application/chat/agent/__init__.py +10 -0
  5. atlas/application/chat/agent/act_loop.py +179 -0
  6. atlas/application/chat/agent/factory.py +142 -0
  7. atlas/application/chat/agent/protocols.py +46 -0
  8. atlas/application/chat/agent/react_loop.py +338 -0
  9. atlas/application/chat/agent/think_act_loop.py +171 -0
  10. atlas/application/chat/approval_manager.py +151 -0
  11. atlas/application/chat/elicitation_manager.py +191 -0
  12. atlas/application/chat/events/__init__.py +1 -0
  13. atlas/application/chat/events/agent_event_relay.py +112 -0
  14. atlas/application/chat/modes/__init__.py +1 -0
  15. atlas/application/chat/modes/agent.py +125 -0
  16. atlas/application/chat/modes/plain.py +74 -0
  17. atlas/application/chat/modes/rag.py +81 -0
  18. atlas/application/chat/modes/tools.py +179 -0
  19. atlas/application/chat/orchestrator.py +213 -0
  20. atlas/application/chat/policies/__init__.py +1 -0
  21. atlas/application/chat/policies/tool_authorization.py +99 -0
  22. atlas/application/chat/preprocessors/__init__.py +1 -0
  23. atlas/application/chat/preprocessors/message_builder.py +92 -0
  24. atlas/application/chat/preprocessors/prompt_override_service.py +104 -0
  25. atlas/application/chat/service.py +454 -0
  26. atlas/application/chat/utilities/__init__.py +6 -0
  27. atlas/application/chat/utilities/error_handler.py +367 -0
  28. atlas/application/chat/utilities/event_notifier.py +546 -0
  29. atlas/application/chat/utilities/file_processor.py +613 -0
  30. atlas/application/chat/utilities/tool_executor.py +789 -0
  31. atlas/atlas_chat_cli.py +347 -0
  32. atlas/atlas_client.py +238 -0
  33. atlas/core/__init__.py +0 -0
  34. atlas/core/auth.py +205 -0
  35. atlas/core/authorization_manager.py +27 -0
  36. atlas/core/capabilities.py +123 -0
  37. atlas/core/compliance.py +215 -0
  38. atlas/core/domain_whitelist.py +147 -0
  39. atlas/core/domain_whitelist_middleware.py +82 -0
  40. atlas/core/http_client.py +28 -0
  41. atlas/core/log_sanitizer.py +102 -0
  42. atlas/core/metrics_logger.py +59 -0
  43. atlas/core/middleware.py +131 -0
  44. atlas/core/otel_config.py +242 -0
  45. atlas/core/prompt_risk.py +200 -0
  46. atlas/core/rate_limit.py +0 -0
  47. atlas/core/rate_limit_middleware.py +64 -0
  48. atlas/core/security_headers_middleware.py +51 -0
  49. atlas/domain/__init__.py +37 -0
  50. atlas/domain/chat/__init__.py +1 -0
  51. atlas/domain/chat/dtos.py +85 -0
  52. atlas/domain/errors.py +96 -0
  53. atlas/domain/messages/__init__.py +12 -0
  54. atlas/domain/messages/models.py +160 -0
  55. atlas/domain/rag_mcp_service.py +664 -0
  56. atlas/domain/sessions/__init__.py +7 -0
  57. atlas/domain/sessions/models.py +36 -0
  58. atlas/domain/unified_rag_service.py +371 -0
  59. atlas/infrastructure/__init__.py +10 -0
  60. atlas/infrastructure/app_factory.py +135 -0
  61. atlas/infrastructure/events/__init__.py +1 -0
  62. atlas/infrastructure/events/cli_event_publisher.py +140 -0
  63. atlas/infrastructure/events/websocket_publisher.py +140 -0
  64. atlas/infrastructure/sessions/in_memory_repository.py +56 -0
  65. atlas/infrastructure/transport/__init__.py +7 -0
  66. atlas/infrastructure/transport/websocket_connection_adapter.py +33 -0
  67. atlas/init_cli.py +226 -0
  68. atlas/interfaces/__init__.py +15 -0
  69. atlas/interfaces/events.py +134 -0
  70. atlas/interfaces/llm.py +54 -0
  71. atlas/interfaces/rag.py +40 -0
  72. atlas/interfaces/sessions.py +75 -0
  73. atlas/interfaces/tools.py +57 -0
  74. atlas/interfaces/transport.py +24 -0
  75. atlas/main.py +564 -0
  76. atlas/mcp/api_key_demo/README.md +76 -0
  77. atlas/mcp/api_key_demo/main.py +172 -0
  78. atlas/mcp/api_key_demo/run.sh +56 -0
  79. atlas/mcp/basictable/main.py +147 -0
  80. atlas/mcp/calculator/main.py +149 -0
  81. atlas/mcp/code-executor/execution_engine.py +98 -0
  82. atlas/mcp/code-executor/execution_environment.py +95 -0
  83. atlas/mcp/code-executor/main.py +528 -0
  84. atlas/mcp/code-executor/result_processing.py +276 -0
  85. atlas/mcp/code-executor/script_generation.py +195 -0
  86. atlas/mcp/code-executor/security_checker.py +140 -0
  87. atlas/mcp/corporate_cars/main.py +437 -0
  88. atlas/mcp/csv_reporter/main.py +545 -0
  89. atlas/mcp/duckduckgo/main.py +182 -0
  90. atlas/mcp/elicitation_demo/README.md +171 -0
  91. atlas/mcp/elicitation_demo/main.py +262 -0
  92. atlas/mcp/env-demo/README.md +158 -0
  93. atlas/mcp/env-demo/main.py +199 -0
  94. atlas/mcp/file_size_test/main.py +284 -0
  95. atlas/mcp/filesystem/main.py +348 -0
  96. atlas/mcp/image_demo/main.py +113 -0
  97. atlas/mcp/image_demo/requirements.txt +4 -0
  98. atlas/mcp/logging_demo/README.md +72 -0
  99. atlas/mcp/logging_demo/main.py +103 -0
  100. atlas/mcp/many_tools_demo/main.py +50 -0
  101. atlas/mcp/order_database/__init__.py +0 -0
  102. atlas/mcp/order_database/main.py +369 -0
  103. atlas/mcp/order_database/signal_data.csv +1001 -0
  104. atlas/mcp/pdfbasic/main.py +394 -0
  105. atlas/mcp/pptx_generator/main.py +760 -0
  106. atlas/mcp/pptx_generator/requirements.txt +13 -0
  107. atlas/mcp/pptx_generator/run_test.sh +1 -0
  108. atlas/mcp/pptx_generator/test_pptx_generator_security.py +169 -0
  109. atlas/mcp/progress_demo/main.py +167 -0
  110. atlas/mcp/progress_updates_demo/QUICKSTART.md +273 -0
  111. atlas/mcp/progress_updates_demo/README.md +120 -0
  112. atlas/mcp/progress_updates_demo/main.py +497 -0
  113. atlas/mcp/prompts/main.py +222 -0
  114. atlas/mcp/public_demo/main.py +189 -0
  115. atlas/mcp/sampling_demo/README.md +169 -0
  116. atlas/mcp/sampling_demo/main.py +234 -0
  117. atlas/mcp/thinking/main.py +77 -0
  118. atlas/mcp/tool_planner/main.py +240 -0
  119. atlas/mcp/ui-demo/badmesh.png +0 -0
  120. atlas/mcp/ui-demo/main.py +383 -0
  121. atlas/mcp/ui-demo/templates/button_demo.html +32 -0
  122. atlas/mcp/ui-demo/templates/data_visualization.html +32 -0
  123. atlas/mcp/ui-demo/templates/form_demo.html +28 -0
  124. atlas/mcp/username-override-demo/README.md +320 -0
  125. atlas/mcp/username-override-demo/main.py +308 -0
  126. atlas/modules/__init__.py +0 -0
  127. atlas/modules/config/__init__.py +34 -0
  128. atlas/modules/config/cli.py +231 -0
  129. atlas/modules/config/config_manager.py +1096 -0
  130. atlas/modules/file_storage/__init__.py +22 -0
  131. atlas/modules/file_storage/cli.py +330 -0
  132. atlas/modules/file_storage/content_extractor.py +290 -0
  133. atlas/modules/file_storage/manager.py +295 -0
  134. atlas/modules/file_storage/mock_s3_client.py +402 -0
  135. atlas/modules/file_storage/s3_client.py +417 -0
  136. atlas/modules/llm/__init__.py +19 -0
  137. atlas/modules/llm/caller.py +287 -0
  138. atlas/modules/llm/litellm_caller.py +675 -0
  139. atlas/modules/llm/models.py +19 -0
  140. atlas/modules/mcp_tools/__init__.py +17 -0
  141. atlas/modules/mcp_tools/client.py +2123 -0
  142. atlas/modules/mcp_tools/token_storage.py +556 -0
  143. atlas/modules/prompts/prompt_provider.py +130 -0
  144. atlas/modules/rag/__init__.py +24 -0
  145. atlas/modules/rag/atlas_rag_client.py +336 -0
  146. atlas/modules/rag/client.py +129 -0
  147. atlas/routes/admin_routes.py +865 -0
  148. atlas/routes/config_routes.py +484 -0
  149. atlas/routes/feedback_routes.py +361 -0
  150. atlas/routes/files_routes.py +274 -0
  151. atlas/routes/health_routes.py +40 -0
  152. atlas/routes/mcp_auth_routes.py +223 -0
  153. atlas/server_cli.py +164 -0
  154. atlas/tests/conftest.py +20 -0
  155. atlas/tests/integration/test_mcp_auth_integration.py +152 -0
  156. atlas/tests/manual_test_sampling.py +87 -0
  157. atlas/tests/modules/mcp_tools/test_client_auth.py +226 -0
  158. atlas/tests/modules/mcp_tools/test_client_env.py +191 -0
  159. atlas/tests/test_admin_mcp_server_management_routes.py +141 -0
  160. atlas/tests/test_agent_roa.py +135 -0
  161. atlas/tests/test_app_factory_smoke.py +47 -0
  162. atlas/tests/test_approval_manager.py +439 -0
  163. atlas/tests/test_atlas_client.py +188 -0
  164. atlas/tests/test_atlas_rag_client.py +447 -0
  165. atlas/tests/test_atlas_rag_integration.py +224 -0
  166. atlas/tests/test_attach_file_flow.py +287 -0
  167. atlas/tests/test_auth_utils.py +165 -0
  168. atlas/tests/test_backend_public_url.py +185 -0
  169. atlas/tests/test_banner_logging.py +287 -0
  170. atlas/tests/test_capability_tokens_and_injection.py +203 -0
  171. atlas/tests/test_compliance_level.py +54 -0
  172. atlas/tests/test_compliance_manager.py +253 -0
  173. atlas/tests/test_config_manager.py +617 -0
  174. atlas/tests/test_config_manager_paths.py +12 -0
  175. atlas/tests/test_core_auth.py +18 -0
  176. atlas/tests/test_core_utils.py +190 -0
  177. atlas/tests/test_docker_env_sync.py +202 -0
  178. atlas/tests/test_domain_errors.py +329 -0
  179. atlas/tests/test_domain_whitelist.py +359 -0
  180. atlas/tests/test_elicitation_manager.py +408 -0
  181. atlas/tests/test_elicitation_routing.py +296 -0
  182. atlas/tests/test_env_demo_server.py +88 -0
  183. atlas/tests/test_error_classification.py +113 -0
  184. atlas/tests/test_error_flow_integration.py +116 -0
  185. atlas/tests/test_feedback_routes.py +333 -0
  186. atlas/tests/test_file_content_extraction.py +1134 -0
  187. atlas/tests/test_file_extraction_routes.py +158 -0
  188. atlas/tests/test_file_library.py +107 -0
  189. atlas/tests/test_file_manager_unit.py +18 -0
  190. atlas/tests/test_health_route.py +49 -0
  191. atlas/tests/test_http_client_stub.py +8 -0
  192. atlas/tests/test_imports_smoke.py +30 -0
  193. atlas/tests/test_interfaces_llm_response.py +9 -0
  194. atlas/tests/test_issue_access_denied_fix.py +136 -0
  195. atlas/tests/test_llm_env_expansion.py +836 -0
  196. atlas/tests/test_log_level_sensitive_data.py +285 -0
  197. atlas/tests/test_mcp_auth_routes.py +341 -0
  198. atlas/tests/test_mcp_client_auth.py +331 -0
  199. atlas/tests/test_mcp_data_injection.py +270 -0
  200. atlas/tests/test_mcp_get_authorized_servers.py +95 -0
  201. atlas/tests/test_mcp_hot_reload.py +512 -0
  202. atlas/tests/test_mcp_image_content.py +424 -0
  203. atlas/tests/test_mcp_logging.py +172 -0
  204. atlas/tests/test_mcp_progress_updates.py +313 -0
  205. atlas/tests/test_mcp_prompt_override_system_prompt.py +102 -0
  206. atlas/tests/test_mcp_prompts_server.py +39 -0
  207. atlas/tests/test_mcp_tool_result_parsing.py +296 -0
  208. atlas/tests/test_metrics_logger.py +56 -0
  209. atlas/tests/test_middleware_auth.py +379 -0
  210. atlas/tests/test_prompt_risk_and_acl.py +141 -0
  211. atlas/tests/test_rag_mcp_aggregator.py +204 -0
  212. atlas/tests/test_rag_mcp_service.py +224 -0
  213. atlas/tests/test_rate_limit_middleware.py +45 -0
  214. atlas/tests/test_routes_config_smoke.py +60 -0
  215. atlas/tests/test_routes_files_download_token.py +41 -0
  216. atlas/tests/test_routes_files_health.py +18 -0
  217. atlas/tests/test_runtime_imports.py +53 -0
  218. atlas/tests/test_sampling_integration.py +482 -0
  219. atlas/tests/test_security_admin_routes.py +61 -0
  220. atlas/tests/test_security_capability_tokens.py +65 -0
  221. atlas/tests/test_security_file_stats_scope.py +21 -0
  222. atlas/tests/test_security_header_injection.py +191 -0
  223. atlas/tests/test_security_headers_and_filename.py +63 -0
  224. atlas/tests/test_shared_session_repository.py +101 -0
  225. atlas/tests/test_system_prompt_loading.py +181 -0
  226. atlas/tests/test_token_storage.py +505 -0
  227. atlas/tests/test_tool_approval_config.py +93 -0
  228. atlas/tests/test_tool_approval_utils.py +356 -0
  229. atlas/tests/test_tool_authorization_group_filtering.py +223 -0
  230. atlas/tests/test_tool_details_in_config.py +108 -0
  231. atlas/tests/test_tool_planner.py +300 -0
  232. atlas/tests/test_unified_rag_service.py +398 -0
  233. atlas/tests/test_username_override_in_approval.py +258 -0
  234. atlas/tests/test_websocket_auth_header.py +168 -0
  235. atlas/version.py +6 -0
  236. atlas_chat-0.1.0.data/data/.env.example +253 -0
  237. atlas_chat-0.1.0.data/data/config/defaults/compliance-levels.json +44 -0
  238. atlas_chat-0.1.0.data/data/config/defaults/domain-whitelist.json +123 -0
  239. atlas_chat-0.1.0.data/data/config/defaults/file-extractors.json +74 -0
  240. atlas_chat-0.1.0.data/data/config/defaults/help-config.json +198 -0
  241. atlas_chat-0.1.0.data/data/config/defaults/llmconfig-buggy.yml +11 -0
  242. atlas_chat-0.1.0.data/data/config/defaults/llmconfig.yml +19 -0
  243. atlas_chat-0.1.0.data/data/config/defaults/mcp.json +138 -0
  244. atlas_chat-0.1.0.data/data/config/defaults/rag-sources.json +17 -0
  245. atlas_chat-0.1.0.data/data/config/defaults/splash-config.json +16 -0
  246. atlas_chat-0.1.0.dist-info/METADATA +236 -0
  247. atlas_chat-0.1.0.dist-info/RECORD +250 -0
  248. atlas_chat-0.1.0.dist-info/WHEEL +5 -0
  249. atlas_chat-0.1.0.dist-info/entry_points.txt +4 -0
  250. atlas_chat-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,158 @@
1
+ """Tests for file extraction config exposure in /api/config endpoint."""
2
+
3
+ from unittest.mock import patch
4
+
5
+ from main import app
6
+ from starlette.testclient import TestClient
7
+
8
+ from atlas.infrastructure.app_factory import app_factory
9
+ from atlas.modules.config.config_manager import FileExtractorConfig, FileExtractorsConfig
10
+
11
+
12
+ def test_config_endpoint_includes_file_extraction_feature_flag():
13
+ """Config endpoint should include file_content_extraction in features."""
14
+ client = TestClient(app)
15
+ resp = client.get("/api/config", headers={"X-User-Email": "test@test.com"})
16
+
17
+ assert resp.status_code == 200
18
+ data = resp.json()
19
+ assert "features" in data
20
+ assert "file_content_extraction" in data["features"]
21
+ assert isinstance(data["features"]["file_content_extraction"], bool)
22
+
23
+
24
+ def test_config_endpoint_includes_file_extraction_config():
25
+ """Config endpoint should include file_extraction configuration."""
26
+ client = TestClient(app)
27
+ resp = client.get("/api/config", headers={"X-User-Email": "test@test.com"})
28
+
29
+ assert resp.status_code == 200
30
+ data = resp.json()
31
+ assert "file_extraction" in data
32
+ file_extraction = data["file_extraction"]
33
+ assert "enabled" in file_extraction
34
+ assert "default_behavior" in file_extraction
35
+ assert "supported_extensions" in file_extraction
36
+
37
+
38
+ def test_file_extraction_disabled_when_feature_flag_off():
39
+ """File extraction should be disabled when feature flag is off."""
40
+ config_manager = app_factory.get_config_manager()
41
+ original_setting = config_manager.app_settings.feature_file_content_extraction_enabled
42
+
43
+ try:
44
+ config_manager.app_settings.feature_file_content_extraction_enabled = False
45
+
46
+ client = TestClient(app)
47
+ resp = client.get("/api/config", headers={"X-User-Email": "test@test.com"})
48
+
49
+ assert resp.status_code == 200
50
+ data = resp.json()
51
+ assert data["features"]["file_content_extraction"] is False
52
+ assert data["file_extraction"]["enabled"] is False
53
+ assert data["file_extraction"]["default_behavior"] == "none"
54
+ assert data["file_extraction"]["supported_extensions"] == []
55
+ finally:
56
+ config_manager.app_settings.feature_file_content_extraction_enabled = original_setting
57
+
58
+
59
+ def test_file_extraction_enabled_with_correct_extensions():
60
+ """File extraction should show supported extensions when enabled."""
61
+ config_manager = app_factory.get_config_manager()
62
+ original_feature = config_manager.app_settings.feature_file_content_extraction_enabled
63
+ original_extractors = config_manager._file_extractors_config
64
+
65
+ try:
66
+ # Enable feature and set up test config
67
+ config_manager.app_settings.feature_file_content_extraction_enabled = True
68
+ config_manager._file_extractors_config = FileExtractorsConfig(
69
+ enabled=True,
70
+ default_behavior="extract",
71
+ extractors={
72
+ "pdf-text": FileExtractorConfig(url="http://localhost/pdf", enabled=True),
73
+ "image-vision": FileExtractorConfig(url="http://localhost/img", enabled=False)
74
+ },
75
+ extension_mapping={
76
+ ".pdf": "pdf-text",
77
+ ".png": "image-vision"
78
+ }
79
+ )
80
+
81
+ client = TestClient(app)
82
+ resp = client.get("/api/config", headers={"X-User-Email": "test@test.com"})
83
+
84
+ assert resp.status_code == 200
85
+ data = resp.json()
86
+ assert data["features"]["file_content_extraction"] is True
87
+ assert data["file_extraction"]["enabled"] is True
88
+ assert data["file_extraction"]["default_behavior"] == "full"
89
+ # Only .pdf should be in supported_extensions since image-vision is disabled
90
+ assert ".pdf" in data["file_extraction"]["supported_extensions"]
91
+ assert ".png" not in data["file_extraction"]["supported_extensions"]
92
+ finally:
93
+ config_manager.app_settings.feature_file_content_extraction_enabled = original_feature
94
+ config_manager._file_extractors_config = original_extractors
95
+
96
+
97
+ def test_file_extraction_handles_config_errors_gracefully():
98
+ """File extraction config should handle errors gracefully."""
99
+ config_manager = app_factory.get_config_manager()
100
+ original_feature = config_manager.app_settings.feature_file_content_extraction_enabled
101
+ original_extractors = config_manager._file_extractors_config
102
+
103
+ try:
104
+ config_manager.app_settings.feature_file_content_extraction_enabled = True
105
+
106
+ # Force an error by setting invalid config that will cause exception
107
+ config_manager._file_extractors_config = None
108
+
109
+ # Patch the property to raise an exception
110
+ with patch.object(
111
+ type(config_manager),
112
+ 'file_extractors_config',
113
+ property(lambda self: (_ for _ in ()).throw(Exception("Config error")))
114
+ ):
115
+ client = TestClient(app)
116
+ resp = client.get("/api/config", headers={"X-User-Email": "test@test.com"})
117
+
118
+ assert resp.status_code == 200
119
+ data = resp.json()
120
+ # Should return safe defaults on error
121
+ assert data["file_extraction"]["enabled"] is False
122
+ assert data["file_extraction"]["default_behavior"] == "none"
123
+ assert data["file_extraction"]["supported_extensions"] == []
124
+ finally:
125
+ config_manager.app_settings.feature_file_content_extraction_enabled = original_feature
126
+ config_manager._file_extractors_config = original_extractors
127
+
128
+
129
+ def test_file_extraction_extensions_sorted():
130
+ """File extraction supported extensions should be sorted."""
131
+ config_manager = app_factory.get_config_manager()
132
+ original_feature = config_manager.app_settings.feature_file_content_extraction_enabled
133
+ original_extractors = config_manager._file_extractors_config
134
+
135
+ try:
136
+ config_manager.app_settings.feature_file_content_extraction_enabled = True
137
+ config_manager._file_extractors_config = FileExtractorsConfig(
138
+ enabled=True,
139
+ extractors={
140
+ "pdf-text": FileExtractorConfig(url="http://localhost/pdf", enabled=True)
141
+ },
142
+ extension_mapping={
143
+ ".pdf": "pdf-text",
144
+ ".doc": "pdf-text",
145
+ ".txt": "pdf-text"
146
+ }
147
+ )
148
+
149
+ client = TestClient(app)
150
+ resp = client.get("/api/config", headers={"X-User-Email": "test@test.com"})
151
+
152
+ assert resp.status_code == 200
153
+ data = resp.json()
154
+ extensions = data["file_extraction"]["supported_extensions"]
155
+ assert extensions == sorted(extensions)
156
+ finally:
157
+ config_manager.app_settings.feature_file_content_extraction_enabled = original_feature
158
+ config_manager._file_extractors_config = original_extractors
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unit tests for File Library implementation.
4
+ Tests the new file library feature including:
5
+ - AllFilesView component functionality
6
+ - SessionFilesView component
7
+ - FileManagerPanel tab switching
8
+ - Backend attach_file endpoint
9
+ - WebSocket attach_file message handling
10
+ """
11
+
12
+
13
+
14
+ # Test the backend attach_file functionality
15
+ class TestAttachFileBackend:
16
+ def test_handle_attach_file_success(self):
17
+ """Test successful file attachment to session"""
18
+ # This would be a full integration test when backend is running
19
+ pass
20
+
21
+ def test_handle_attach_file_file_not_found(self):
22
+ """Test handling of file not found error"""
23
+ pass
24
+
25
+ def test_handle_attach_file_unauthorized(self):
26
+ """Test handling of unauthorized access"""
27
+ pass
28
+
29
+ # Frontend component tests would go here
30
+ # These would typically use a testing framework like Jest or Vitest
31
+
32
+ class TestAllFilesView:
33
+ def test_fetch_all_files(self):
34
+ """Test fetching all user files"""
35
+ pass
36
+
37
+ def test_search_filter(self):
38
+ """Test file search functionality"""
39
+ pass
40
+
41
+ def test_sort_functionality(self):
42
+ """Test file sorting by different criteria"""
43
+ pass
44
+
45
+ def test_type_filter(self):
46
+ """Test filtering by file type (uploaded vs generated)"""
47
+ pass
48
+
49
+ def test_load_to_session(self):
50
+ """Test loading file to current session"""
51
+ pass
52
+
53
+ def test_download_file(self):
54
+ """Test file download functionality"""
55
+ pass
56
+
57
+ def test_delete_file(self):
58
+ """Test file deletion"""
59
+ pass
60
+
61
+ class TestSessionFilesView:
62
+ def test_display_session_files(self):
63
+ """Test displaying files in current session"""
64
+ pass
65
+
66
+ def test_file_actions(self):
67
+ """Test download, delete, and tagging actions"""
68
+ pass
69
+
70
+ class TestFileManagerPanel:
71
+ def test_tab_switching(self):
72
+ """Test switching between Session Files and File Library tabs"""
73
+ pass
74
+
75
+ def test_initial_tab_state(self):
76
+ """Test that panel opens on Session Files tab by default"""
77
+ pass
78
+
79
+ # Integration test scenarios
80
+ class TestFileLibraryIntegration:
81
+ def test_end_to_end_workflow(self):
82
+ """
83
+ Test end-to-end workflow:
84
+ 1. Upload file in session A
85
+ 2. Start new session B
86
+ 3. Open File Library tab
87
+ 4. Search for and find file from session A
88
+ 5. Load file into session B
89
+ 6. Verify file appears in Session Files
90
+ """
91
+ pass
92
+
93
+ if __name__ == "__main__":
94
+ print("File Library unit tests")
95
+ print("Note: Most testing should be done manually through the UI")
96
+ print("because the functionality primarily involves user interaction.")
97
+ print("")
98
+ print("Manual testing checklist:")
99
+ print("- Open File Manager panel")
100
+ print("- Switch between 'Session Files' and 'File Library' tabs")
101
+ print("- Verify files are displayed correctly in each tab")
102
+ print("- Search, filter, and sort files in File Library")
103
+ print("- Download files from File Library")
104
+ print("- Delete files from File Library")
105
+ print("- Load files from File Library to current session")
106
+ print("- Verify loaded files appear in Session Files tab")
107
+ print("- Test error handling for failed operations")
@@ -0,0 +1,18 @@
1
+
2
+ from atlas.modules.file_storage.manager import FileManager
3
+
4
+
5
+ def test_file_manager_content_type_and_category():
6
+ fm = FileManager(s3_client=None) # will construct default
7
+
8
+ assert fm.get_content_type("report.pdf") == "application/pdf"
9
+ assert fm.get_content_type("diagram.png") == "image/png"
10
+ assert fm.get_content_type("unknown.bin") == "application/octet-stream"
11
+
12
+ assert fm.categorize_file_type("main.py") == "code"
13
+ assert fm.categorize_file_type("photo.jpg") == "image"
14
+ assert fm.categorize_file_type("data.csv") == "data"
15
+ assert fm.categorize_file_type("notes.txt") == "document"
16
+
17
+ assert fm.should_display_in_canvas("plot.png") is True
18
+ assert fm.should_display_in_canvas("archive.zip") is False
@@ -0,0 +1,49 @@
1
+ """Unit tests for health check endpoint."""
2
+
3
+ from datetime import datetime
4
+
5
+ from main import app
6
+ from starlette.testclient import TestClient
7
+
8
+
9
+ def test_health_endpoint_returns_200():
10
+ """Test that health endpoint returns 200 status."""
11
+ client = TestClient(app)
12
+ resp = client.get("/api/health")
13
+ assert resp.status_code == 200
14
+
15
+
16
+ def test_health_endpoint_no_auth_required():
17
+ """Test that health endpoint works without authentication."""
18
+ client = TestClient(app)
19
+ # No X-User-Email header provided
20
+ resp = client.get("/api/health")
21
+ assert resp.status_code == 200
22
+
23
+
24
+ def test_health_endpoint_response_structure():
25
+ """Test that health endpoint returns correct response structure."""
26
+ client = TestClient(app)
27
+ resp = client.get("/api/health")
28
+ assert resp.status_code == 200
29
+
30
+ data = resp.json()
31
+
32
+ # Verify all required fields are present
33
+ assert "status" in data
34
+ assert "service" in data
35
+ assert "version" in data
36
+ assert "timestamp" in data
37
+
38
+ # Verify field values
39
+ assert data["status"] == "healthy"
40
+ assert data["service"] == "atlas-ui-3-backend"
41
+
42
+ # This version number can change, so just check it's a non-empty string
43
+ assert isinstance(data["version"], str) and len(data["version"]) > 0
44
+
45
+ # Verify timestamp is valid ISO-8601 format
46
+ try:
47
+ datetime.fromisoformat(data["timestamp"])
48
+ except ValueError:
49
+ assert False, f"Invalid timestamp format: {data['timestamp']}"
@@ -0,0 +1,8 @@
1
+ from atlas.core.http_client import create_rag_client
2
+
3
+
4
+ def test_http_client_stub_returns_object():
5
+ client = create_rag_client("http://example", 5.0)
6
+ assert client is not None
7
+ # ensure object has query coroutine
8
+ assert hasattr(client, "query")
@@ -0,0 +1,30 @@
1
+ import pkgutil
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ # Import all backend python modules to catch import-time errors quickly.
6
+ # Skip heavy runtime side effects by not executing app.run, etc.
7
+
8
+ def iter_backend_modules():
9
+ backend_root = Path(__file__).resolve().parents[1]
10
+ sys.path.insert(0, str(backend_root))
11
+
12
+ for module in pkgutil.walk_packages([str(backend_root)], prefix=""):
13
+ name = module.name
14
+ # Skip private and test packages
15
+ if name.startswith("tests"):
16
+ continue
17
+ # Skip MCP servers as they may require external binaries
18
+ if name.startswith("mcp.") or name.startswith("mcp/"):
19
+ continue
20
+ yield name
21
+
22
+
23
+ def test_import_all_backend_modules():
24
+ failed = []
25
+ for name in iter_backend_modules():
26
+ try:
27
+ __import__(name)
28
+ except Exception as e:
29
+ failed.append((name, str(e)))
30
+ assert not failed, f"Import failures: {failed[:5]} (and {max(0, len(failed)-5)} more)"
@@ -0,0 +1,9 @@
1
+ from atlas.interfaces.llm import LLMResponse
2
+
3
+
4
+ def test_llm_response_has_tool_calls():
5
+ r1 = LLMResponse(content="hello", tool_calls=None)
6
+ assert r1.has_tool_calls() is False
7
+
8
+ r2 = LLMResponse(content="hi", tool_calls=[{"type": "function", "function": {"name": "t"}}])
9
+ assert r2.has_tool_calls() is True
@@ -0,0 +1,136 @@
1
+ """
2
+ Integration test demonstrating the fix for the access denied issue.
3
+
4
+ This test simulates the exact scenario from the issue:
5
+ - A file belongs to user 'agarlan@sandia.gov'
6
+ - WebSocket connection is authenticated as 'agarlan@sandia.gov' via X-User-Email header
7
+ - Attaching the file should succeed (not fail with "Access denied")
8
+ """
9
+
10
+ import base64
11
+ from unittest.mock import AsyncMock, MagicMock, patch
12
+
13
+ import pytest
14
+ from fastapi.testclient import TestClient
15
+ from main import app
16
+
17
+
18
+ @pytest.fixture
19
+ def mock_components():
20
+ """Mock all components needed for the test."""
21
+ with patch('main.app_factory') as mock_factory:
22
+ # Mock config
23
+ mock_config = MagicMock()
24
+ mock_config.app_settings.test_user = 'test@test.com'
25
+ mock_config.app_settings.auth_user_header = 'X-User-Email'
26
+ mock_factory.get_config_manager.return_value = mock_config
27
+
28
+ # Mock file manager with S3 client
29
+ mock_file_manager = MagicMock()
30
+ mock_s3_client = MagicMock()
31
+
32
+ # Simulate a file that belongs to agarlan@sandia.gov
33
+ async def mock_get_file(user_email, s3_key):
34
+ """Mock S3 get_file that enforces user prefix check."""
35
+ # This is the actual check from s3_client.py line 185
36
+ if not s3_key.startswith(f"users/{user_email}/"):
37
+ raise Exception("Access denied to file")
38
+
39
+ # If user matches, return file metadata
40
+ return {
41
+ "key": s3_key,
42
+ "filename": "mypdf.pdf",
43
+ "content_base64": base64.b64encode(b"test content").decode(),
44
+ "content_type": "application/pdf",
45
+ "size": 100,
46
+ "etag": "test-etag"
47
+ }
48
+
49
+ mock_s3_client.get_file = AsyncMock(side_effect=mock_get_file)
50
+ mock_file_manager.s3_client = mock_s3_client
51
+
52
+ # Mock chat service
53
+ mock_chat_service = MagicMock()
54
+ mock_chat_service.handle_attach_file = AsyncMock(return_value={
55
+ 'type': 'file_attach',
56
+ 'success': True,
57
+ 'filename': 'mypdf.pdf'
58
+ })
59
+ mock_chat_service.end_session = MagicMock()
60
+ mock_factory.create_chat_service.return_value = mock_chat_service
61
+
62
+ yield {
63
+ 'factory': mock_factory,
64
+ 'config': mock_config,
65
+ 'file_manager': mock_file_manager,
66
+ 'chat_service': mock_chat_service
67
+ }
68
+
69
+
70
+ def test_issue_scenario_fixed_with_correct_user(mock_components):
71
+ """
72
+ Test the exact scenario from the issue, demonstrating the fix.
73
+
74
+ Before fix:
75
+ - WebSocket would use test@test.com (from fallback)
76
+ - Attempting to access users/agarlan@sandia.gov/generated/file.pdf would fail
77
+ - Error: "Access denied: test@test.com attempted to access users/agarlan@sandia.gov/..."
78
+
79
+ After fix:
80
+ - WebSocket uses agarlan@sandia.gov (from X-User-Email header)
81
+ - Accessing users/agarlan@sandia.gov/generated/file.pdf succeeds
82
+ """
83
+ client = TestClient(app)
84
+
85
+ # Simulate the production scenario: reverse proxy sets X-User-Email header
86
+ actual_user = "agarlan@sandia.gov"
87
+
88
+ # Connect with X-User-Email header (as set by reverse proxy)
89
+ with client.websocket_connect("/ws", headers={"X-User-Email": actual_user}):
90
+ # Verify the connection was created with the correct user
91
+ call_args = mock_components['factory'].create_chat_service.call_args
92
+ connection_adapter = call_args[0][0]
93
+
94
+ # This should be the actual user, not test@test.com
95
+ assert connection_adapter.user_email == actual_user, (
96
+ f"Expected user to be {actual_user}, but got {connection_adapter.user_email}. "
97
+ "This would cause 'Access denied' errors when accessing user's files."
98
+ )
99
+
100
+
101
+ def test_issue_scenario_would_fail_without_header():
102
+ """
103
+ Demonstrate that without the header, the old behavior (test user fallback) occurs.
104
+ This test shows why the issue existed in the first place.
105
+ """
106
+ with patch('main.app_factory') as mock_factory:
107
+ # Mock config
108
+ mock_config = MagicMock()
109
+ mock_config.app_settings.test_user = 'test@test.com'
110
+ mock_config.app_settings.auth_user_header = 'X-User-Email'
111
+ mock_factory.get_config_manager.return_value = mock_config
112
+
113
+ # Mock chat service
114
+ mock_chat_service = MagicMock()
115
+ mock_chat_service.end_session = MagicMock()
116
+ mock_factory.create_chat_service.return_value = mock_chat_service
117
+
118
+ client = TestClient(app)
119
+
120
+ # Connect WITHOUT X-User-Email header (simulating old behavior or dev mode)
121
+ with client.websocket_connect("/ws"):
122
+ call_args = mock_factory.create_chat_service.call_args
123
+ connection_adapter = call_args[0][0]
124
+
125
+ # Without header, it falls back to test user
126
+ assert connection_adapter.user_email == 'test@test.com', (
127
+ "Without X-User-Email header, should fall back to test user"
128
+ )
129
+
130
+ # This would cause access denied when trying to access:
131
+ # users/agarlan@sandia.gov/generated/file.pdf
132
+ # because connection is authenticated as test@test.com
133
+
134
+
135
+ if __name__ == "__main__":
136
+ pytest.main([__file__, "-v"])