More tests

This commit is contained in:
Alex committed 2026-03-31 00:07:19 +01:00
1 parent d5c0322e2a
commit a3b08a5b44
37 files changed
+21904 -6

No files matched your search

File diff suppressed because it is too large. Load diff
+385
View File
@@ -1336,3 +1336,388 @@ class TestTaskStatus:
response = TaskStatus().get()
assert _status(response) == 400
# ---------------------------------------------------------------------------
# Additional coverage: zip extraction paths and ManageSourceFiles edge cases
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestUploadFileZipExtraction:
"""Cover zip file extraction (lines 102-136) and error fallback."""
def test_zip_file_extraction_success(self, app):
"""Lines 102-127: zip file is extracted and inner files uploaded."""
import zipfile
from application.api.user.sources.upload import UploadFile
mock_storage = MagicMock()
mock_task = SimpleNamespace(id="task-zip")
# Create a real zip file in memory
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
zf.writestr("inner_file.txt", "hello zip content")
zip_buffer.seek(0)
with app.test_request_context(
"/api/upload",
method="POST",
data={
"user": "u1",
"name": "ZipDoc",
"file": (zip_buffer, "archive.zip"),
},
content_type="multipart/form-data",
):
from flask import request
request.decoded_token = {"sub": "u1"}
with patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
), patch(
"application.api.user.sources.upload.ingest"
) as mock_ingest, patch(
"application.api.user.sources.upload._enforce_audio_path_size_limit",
):
mock_ingest.delay.return_value = mock_task
response = UploadFile().post()
assert _status(response) == 200
# Storage should have been called to save extracted files
assert mock_storage.save_file.called
def test_zip_extraction_error_falls_back_to_original(self, app):
"""Lines 128-136: zip extraction fails, original zip file is saved."""
import zipfile
from application.api.user.sources.upload import UploadFile
mock_storage = MagicMock()
mock_task = SimpleNamespace(id="task-zip-err")
# Create a real zip file in memory
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
zf.writestr("inner.txt", "content")
zip_buffer.seek(0)
def bad_extractall(**kwargs):
raise Exception("corrupt zip")
with app.test_request_context(
"/api/upload",
method="POST",
data={
"user": "u1",
"name": "BadZip",
"file": (zip_buffer, "bad.zip"),
},
content_type="multipart/form-data",
):
from flask import request
request.decoded_token = {"sub": "u1"}
with patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
), patch(
"application.api.user.sources.upload.ingest"
) as mock_ingest, patch(
"application.api.user.sources.upload._enforce_audio_path_size_limit",
), patch(
"application.api.user.sources.upload.zipfile.ZipFile"
) as mock_zip_cls:
mock_zip_instance = MagicMock()
mock_zip_instance.__enter__ = MagicMock(return_value=mock_zip_instance)
mock_zip_instance.__exit__ = MagicMock(return_value=False)
mock_zip_instance.extractall.side_effect = Exception("corrupt zip")
mock_zip_cls.return_value = mock_zip_instance
mock_ingest.delay.return_value = mock_task
response = UploadFile().post()
assert _status(response) == 200
# Fallback: storage should save the original zip
assert mock_storage.save_file.called
def test_upload_returns_413_for_oversized_audio(self, app):
"""Lines 152-161: AudioFileTooLargeError caught."""
from application.api.user.sources.upload import UploadFile
from application.stt.upload_limits import AudioFileTooLargeError
mock_storage = MagicMock()
with app.test_request_context(
"/api/upload",
method="POST",
data={
"user": "u1",
"name": "AudioDoc",
"file": (io.BytesIO(b"audio data"), "big.wav"),
},
content_type="multipart/form-data",
):
from flask import request
request.decoded_token = {"sub": "u1"}
with patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
), patch(
"application.api.user.sources.upload._enforce_audio_path_size_limit",
side_effect=AudioFileTooLargeError("too big"),
):
response = UploadFile().post()
assert _status(response) == 413
assert "success" in _json(response) and _json(response)["success"] is False
@pytest.mark.unit
class TestManageSourceFilesAdditional:
"""Additional edge cases for ManageSourceFiles."""
def test_remove_with_absolute_directory_path_rejected(self, app):
"""Lines 513-523: directory_path starting with / is rejected."""
from application.api.user.sources.upload import ManageSourceFiles
source_id = str(ObjectId())
mock_collection = Mock()
mock_collection.find_one.return_value = {
"_id": ObjectId(source_id),
"user": "u1",
"file_path": "uploads/u1/src",
}
mock_storage = MagicMock()
with patch(
"application.api.user.sources.upload.sources_collection",
mock_collection,
), patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
):
with app.test_request_context(
"/api/manage_source_files",
method="POST",
data={
"source_id": source_id,
"operation": "remove_directory",
"directory_path": "/etc/passwd",
},
):
from flask import request
request.decoded_token = {"sub": "u1"}
response = ManageSourceFiles().post()
assert _status(response) == 400
assert "Invalid directory" in _json(response)["message"]
def test_remove_directory_no_keys_to_remove(self, app):
"""Lines 564-577: remove_directory with file_name_map that has no
matching keys (keys_to_remove is empty)."""
from application.api.user.sources.upload import ManageSourceFiles
source_id = str(ObjectId())
mock_collection = Mock()
mock_collection.find_one.return_value = {
"_id": ObjectId(source_id),
"user": "u1",
"file_path": "uploads/u1/src",
"file_name_map": {"unrelated.txt": "File.txt"},
}
mock_storage = MagicMock()
mock_storage.is_directory.return_value = True
mock_storage.remove_directory.return_value = True
mock_task = SimpleNamespace(id="reingest-x")
with patch(
"application.api.user.sources.upload.sources_collection",
mock_collection,
), patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
), patch(
"application.api.user.tasks.reingest_source_task"
) as mock_reingest:
mock_reingest.delay.return_value = mock_task
with app.test_request_context(
"/api/manage_source_files",
method="POST",
data={
"source_id": source_id,
"operation": "remove_directory",
"directory_path": "no_match_dir",
},
):
from flask import request
request.decoded_token = {"sub": "u1"}
response = ManageSourceFiles().post()
assert _status(response) == 200
# update_one should NOT be called because no keys matched
mock_collection.update_one.assert_not_called()
def test_general_error_remove_directory_context(self, app):
"""Line 598-600: error context includes directory_path for
remove_directory operation."""
from application.api.user.sources.upload import ManageSourceFiles
source_id = str(ObjectId())
mock_collection = Mock()
mock_collection.find_one.return_value = {
"_id": ObjectId(source_id),
"user": "u1",
"file_path": "uploads/u1/src",
}
with patch(
"application.api.user.sources.upload.sources_collection",
mock_collection,
), patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
side_effect=Exception("storage crash"),
):
with app.test_request_context(
"/api/manage_source_files",
method="POST",
data={
"source_id": source_id,
"operation": "remove_directory",
"directory_path": "mydir",
},
):
from flask import request
request.decoded_token = {"sub": "u1"}
response = ManageSourceFiles().post()
assert _status(response) == 500
assert "Operation failed" in _json(response)["message"]
def test_general_error_add_context(self, app):
"""Lines 604-606: error context includes parent_dir for add operation."""
from application.api.user.sources.upload import ManageSourceFiles
source_id = str(ObjectId())
mock_collection = Mock()
mock_collection.find_one.return_value = {
"_id": ObjectId(source_id),
"user": "u1",
"file_path": "uploads/u1/src",
}
with patch(
"application.api.user.sources.upload.sources_collection",
mock_collection,
), patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
side_effect=Exception("storage crash"),
):
with app.test_request_context(
"/api/manage_source_files",
method="POST",
data={
"source_id": source_id,
"operation": "add",
"parent_dir": "sub",
},
):
from flask import request
request.decoded_token = {"sub": "u1"}
response = ManageSourceFiles().post()
assert _status(response) == 500
def test_file_name_map_non_dict_reset(self, app):
"""Lines 366-367: file_name_map not a dict is reset to {}."""
from application.api.user.sources.upload import ManageSourceFiles
source_id = str(ObjectId())
mock_collection = Mock()
mock_collection.find_one.return_value = {
"_id": ObjectId(source_id),
"user": "u1",
"file_path": "uploads/u1/src",
"file_name_map": [1, 2, 3], # not a dict
}
mock_storage = MagicMock()
mock_storage.file_exists.return_value = True
mock_task = SimpleNamespace(id="reingest-nd")
with patch(
"application.api.user.sources.upload.sources_collection",
mock_collection,
), patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
), patch(
"application.api.user.tasks.reingest_source_task"
) as mock_reingest:
mock_reingest.delay.return_value = mock_task
with app.test_request_context(
"/api/manage_source_files",
method="POST",
data={
"source_id": source_id,
"operation": "remove",
"file_paths": json.dumps(["x.txt"]),
},
):
from flask import request
request.decoded_token = {"sub": "u1"}
response = ManageSourceFiles().post()
assert _status(response) == 200
def test_file_name_map_invalid_json_string_reset(self, app):
"""Lines 362-365: file_name_map is a string but not valid JSON."""
from application.api.user.sources.upload import ManageSourceFiles
source_id = str(ObjectId())
mock_collection = Mock()
mock_collection.find_one.return_value = {
"_id": ObjectId(source_id),
"user": "u1",
"file_path": "uploads/u1/src",
"file_name_map": "not-valid-json{",
}
mock_storage = MagicMock()
mock_storage.file_exists.return_value = False
mock_task = SimpleNamespace(id="reingest-ij")
with patch(
"application.api.user.sources.upload.sources_collection",
mock_collection,
), patch(
"application.api.user.sources.upload.StorageCreator.get_storage",
return_value=mock_storage,
), patch(
"application.api.user.tasks.reingest_source_task"
) as mock_reingest:
mock_reingest.delay.return_value = mock_task
with app.test_request_context(
"/api/manage_source_files",
method="POST",
data={
"source_id": source_id,
"operation": "remove",
"file_paths": json.dumps(["x.txt"]),
},
):
from flask import request
request.decoded_token = {"sub": "u1"}
response = ManageSourceFiles().post()
assert _status(response) == 200
File diff suppressed because it is too large. Load diff
+511
View File
@@ -386,3 +386,514 @@ class TestGetUserLogs:
response = GetUserLogs().post()
assert response.status_code == 401
@pytest.mark.unit
class TestGetTokenAnalyticsAdditional:
"""Additional tests for GetTokenAnalytics covering missing lines."""
def test_returns_401_unauthenticated(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={"filter_option": "last_30_days"},
):
from flask import request
request.decoded_token = None
response = GetTokenAnalytics().post()
assert response.status_code == 401
def test_last_hour_filter(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
mock_token_usage = Mock()
mock_token_usage.aggregate.return_value = [
{"_id": {"minute": "2024-06-01 12:00:00"}, "total_tokens": 500}
]
mock_agents = Mock()
mock_agents.find_one.return_value = None
with patch(
"application.api.user.analytics.routes.token_usage_collection",
mock_token_usage,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={"filter_option": "last_hour"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetTokenAnalytics().post()
assert response.status_code == 200
assert response.json["success"] is True
assert "token_usage" in response.json
def test_last_24_hour_filter(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
mock_token_usage = Mock()
mock_token_usage.aggregate.return_value = [
{"_id": {"hour": "2024-06-01 12:00"}, "total_tokens": 800}
]
mock_agents = Mock()
mock_agents.find_one.return_value = None
with patch(
"application.api.user.analytics.routes.token_usage_collection",
mock_token_usage,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={"filter_option": "last_24_hour"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetTokenAnalytics().post()
assert response.status_code == 200
assert response.json["success"] is True
def test_filters_by_api_key(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
agent_id = ObjectId()
mock_agents = Mock()
mock_agents.find_one.return_value = {
"_id": agent_id,
"key": "token_api_key",
}
mock_token_usage = Mock()
mock_token_usage.aggregate.return_value = []
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
), patch(
"application.api.user.analytics.routes.token_usage_collection",
mock_token_usage,
):
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={
"filter_option": "last_7_days",
"api_key_id": str(agent_id),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetTokenAnalytics().post()
assert response.status_code == 200
pipeline = mock_token_usage.aggregate.call_args[0][0]
assert pipeline[0]["$match"].get("api_key") == "token_api_key"
def test_api_key_error_returns_400(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
mock_agents = Mock()
mock_agents.find_one.side_effect = Exception("db error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={
"filter_option": "last_30_days",
"api_key_id": str(ObjectId()),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetTokenAnalytics().post()
assert response.status_code == 400
def test_aggregate_error_returns_400(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
mock_agents = Mock()
mock_agents.find_one.return_value = None
mock_token_usage = Mock()
mock_token_usage.aggregate.side_effect = Exception("aggregate error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
), patch(
"application.api.user.analytics.routes.token_usage_collection",
mock_token_usage,
):
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={"filter_option": "last_30_days"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetTokenAnalytics().post()
assert response.status_code == 400
def test_last_15_days_filter(self, app):
from application.api.user.analytics.routes import GetTokenAnalytics
mock_token_usage = Mock()
mock_token_usage.aggregate.return_value = []
mock_agents = Mock()
mock_agents.find_one.return_value = None
with patch(
"application.api.user.analytics.routes.token_usage_collection",
mock_token_usage,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_token_analytics",
method="POST",
json={"filter_option": "last_15_days"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetTokenAnalytics().post()
assert response.status_code == 200
@pytest.mark.unit
class TestGetFeedbackAnalyticsAdditional:
"""Additional tests for GetFeedbackAnalytics covering missing lines."""
def test_returns_401_unauthenticated(self, app):
from application.api.user.analytics.routes import GetFeedbackAnalytics
with app.test_request_context(
"/api/get_feedback_analytics",
method="POST",
json={"filter_option": "last_30_days"},
):
from flask import request
request.decoded_token = None
response = GetFeedbackAnalytics().post()
assert response.status_code == 401
def test_last_hour_filter(self, app):
from application.api.user.analytics.routes import GetFeedbackAnalytics
mock_conversations = Mock()
mock_conversations.aggregate.return_value = []
mock_agents = Mock()
mock_agents.find_one.return_value = None
with patch(
"application.api.user.analytics.routes.conversations_collection",
mock_conversations,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_feedback_analytics",
method="POST",
json={"filter_option": "last_hour"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetFeedbackAnalytics().post()
assert response.status_code == 200
def test_last_24_hour_filter(self, app):
from application.api.user.analytics.routes import GetFeedbackAnalytics
mock_conversations = Mock()
mock_conversations.aggregate.return_value = []
mock_agents = Mock()
mock_agents.find_one.return_value = None
with patch(
"application.api.user.analytics.routes.conversations_collection",
mock_conversations,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_feedback_analytics",
method="POST",
json={"filter_option": "last_24_hour"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetFeedbackAnalytics().post()
assert response.status_code == 200
def test_filters_by_api_key(self, app):
from application.api.user.analytics.routes import GetFeedbackAnalytics
agent_id = ObjectId()
mock_agents = Mock()
mock_agents.find_one.return_value = {
"_id": agent_id,
"key": "fb_api_key",
}
mock_conversations = Mock()
mock_conversations.aggregate.return_value = []
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
), patch(
"application.api.user.analytics.routes.conversations_collection",
mock_conversations,
):
with app.test_request_context(
"/api/get_feedback_analytics",
method="POST",
json={
"filter_option": "last_7_days",
"api_key_id": str(agent_id),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetFeedbackAnalytics().post()
assert response.status_code == 200
pipeline = mock_conversations.aggregate.call_args[0][0]
assert pipeline[0]["$match"].get("api_key") == "fb_api_key"
def test_api_key_error_returns_400(self, app):
from application.api.user.analytics.routes import GetFeedbackAnalytics
mock_agents = Mock()
mock_agents.find_one.side_effect = Exception("db error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_feedback_analytics",
method="POST",
json={
"filter_option": "last_30_days",
"api_key_id": str(ObjectId()),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetFeedbackAnalytics().post()
assert response.status_code == 400
def test_aggregate_error_returns_400(self, app):
from application.api.user.analytics.routes import GetFeedbackAnalytics
mock_agents = Mock()
mock_agents.find_one.return_value = None
mock_conversations = Mock()
mock_conversations.aggregate.side_effect = Exception("aggregate error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
), patch(
"application.api.user.analytics.routes.conversations_collection",
mock_conversations,
):
with app.test_request_context(
"/api/get_feedback_analytics",
method="POST",
json={"filter_option": "last_30_days"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetFeedbackAnalytics().post()
assert response.status_code == 400
@pytest.mark.unit
class TestGetMessageAnalyticsAdditional:
"""Additional tests for GetMessageAnalytics covering error paths."""
def test_api_key_error_returns_400(self, app):
from application.api.user.analytics.routes import GetMessageAnalytics
mock_agents = Mock()
mock_agents.find_one.side_effect = Exception("db error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_message_analytics",
method="POST",
json={
"filter_option": "last_30_days",
"api_key_id": str(ObjectId()),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetMessageAnalytics().post()
assert response.status_code == 400
def test_aggregate_error_returns_400(self, app):
from application.api.user.analytics.routes import GetMessageAnalytics
mock_agents = Mock()
mock_agents.find_one.return_value = None
mock_conversations = Mock()
mock_conversations.aggregate.side_effect = Exception("aggregate error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
), patch(
"application.api.user.analytics.routes.conversations_collection",
mock_conversations,
):
with app.test_request_context(
"/api/get_message_analytics",
method="POST",
json={"filter_option": "last_30_days"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetMessageAnalytics().post()
assert response.status_code == 400
def test_last_15_days_filter(self, app):
from application.api.user.analytics.routes import GetMessageAnalytics
mock_conversations = Mock()
mock_conversations.aggregate.return_value = []
mock_agents = Mock()
mock_agents.find_one.return_value = None
with patch(
"application.api.user.analytics.routes.conversations_collection",
mock_conversations,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_message_analytics",
method="POST",
json={"filter_option": "last_15_days"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetMessageAnalytics().post()
assert response.status_code == 200
@pytest.mark.unit
class TestGetUserLogsAdditional:
"""Additional tests for GetUserLogs covering api_key filtering and errors."""
def test_filters_by_api_key(self, app):
from application.api.user.analytics.routes import GetUserLogs
agent_id = ObjectId()
mock_agents = Mock()
mock_agents.find_one.return_value = {
"_id": agent_id,
"key": "logs_api_key",
}
mock_cursor = Mock()
mock_cursor.sort.return_value.skip.return_value.limit.return_value = []
mock_user_logs = Mock()
mock_user_logs.find.return_value = mock_cursor
with patch(
"application.api.user.analytics.routes.user_logs_collection",
mock_user_logs,
), patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_user_logs",
method="POST",
json={
"page": 1,
"page_size": 10,
"api_key_id": str(agent_id),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetUserLogs().post()
assert response.status_code == 200
query_arg = mock_user_logs.find.call_args[0][0]
assert query_arg == {"api_key": "logs_api_key"}
def test_api_key_error_returns_400(self, app):
from application.api.user.analytics.routes import GetUserLogs
mock_agents = Mock()
mock_agents.find_one.side_effect = Exception("db error")
with patch(
"application.api.user.analytics.routes.agents_collection",
mock_agents,
):
with app.test_request_context(
"/api/get_user_logs",
method="POST",
json={
"page": 1,
"api_key_id": str(ObjectId()),
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = GetUserLogs().post()
assert response.status_code == 400
+113
View File
@@ -507,3 +507,116 @@ class TestBulkMoveAgents:
response = BulkMoveAgents().post()
assert response.status_code == 404
# =====================================================================
# Coverage gap tests (lines 64, 90-91, 100, 125-126, 132, 136)
# =====================================================================
@pytest.mark.unit
class TestAgentFoldersGaps:
def test_create_folder_no_auth(self, app):
"""Cover line 64: post returns 401 when no decoded_token."""
from application.api.user.agents.folders import AgentFolders
with app.test_request_context(
"/api/agents/folders/",
method="POST",
json={"name": "Test"},
):
from flask import request
request.decoded_token = None
response = AgentFolders().post()
assert response.status_code == 401
def test_create_folder_exception(self, app):
"""Cover lines 90-91: exception during insert_one returns 400."""
from application.api.user.agents.folders import AgentFolders
mock_folders = Mock()
mock_folders.find_one.return_value = None
mock_folders.insert_one.side_effect = Exception("db error")
with patch(
"application.api.user.agents.folders.agent_folders_collection",
mock_folders,
):
with app.test_request_context(
"/api/agents/folders/",
method="POST",
json={"name": "Test"},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = AgentFolders().post()
assert response.status_code == 400
def test_get_folder_no_auth(self, app):
"""Cover line 100: get specific folder returns 401 when no auth."""
from application.api.user.agents.folders import AgentFolder
with app.test_request_context(
"/api/agents/folders/abc",
method="GET",
):
from flask import request
request.decoded_token = None
response = AgentFolder().get("abc")
assert response.status_code == 401
def test_get_folder_exception(self, app):
"""Cover lines 125-126: exception during find returns 400."""
from application.api.user.agents.folders import AgentFolder
mock_folders = Mock()
mock_folders.find_one.side_effect = Exception("db error")
with patch(
"application.api.user.agents.folders.agent_folders_collection",
mock_folders,
):
with app.test_request_context(
"/api/agents/folders/" + str(ObjectId()),
method="GET",
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = AgentFolder().get(str(ObjectId()))
assert response.status_code == 400
def test_update_folder_no_auth(self, app):
"""Cover line 132: put returns 401 when no decoded_token."""
from application.api.user.agents.folders import AgentFolder
with app.test_request_context(
"/api/agents/folders/abc",
method="PUT",
json={"name": "Updated"},
):
from flask import request
request.decoded_token = None
response = AgentFolder().put("abc")
assert response.status_code == 401
def test_update_folder_no_data(self, app):
"""Cover line 136: put with no data returns 400."""
from application.api.user.agents.folders import AgentFolder
with app.test_request_context(
"/api/agents/folders/abc",
method="PUT",
content_type="application/json",
data="null",
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = AgentFolder().put("abc")
assert response.status_code == 400
+113
View File
@@ -688,3 +688,116 @@ class TestShareConversationPromptable:
assert response.status_code == 201
mock_agents.insert_one.assert_called_once()
mock_shared.insert_one.assert_called_once()
# =====================================================================
# Coverage gap tests (lines 201-205)
# =====================================================================
@pytest.mark.unit
class TestShareConversationExceptionGap:
def test_share_conversation_exception_returns_400(self):
"""Cover lines 201-205: exception during sharing returns 400."""
from application.api.user.sharing.routes import ShareConversation
from unittest.mock import Mock, patch
app = Flask(__name__)
mock_conversations = Mock()
mock_conversations.find_one.side_effect = Exception("db error")
with patch(
"application.api.user.sharing.routes.conversations_collection",
mock_conversations,
):
with app.test_request_context(
"/api/share",
method="POST",
json={
"conversation_id": str(ObjectId()),
"source": str(ObjectId()),
"retriever": "classic",
},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = ShareConversation().post()
assert response.status_code == 400
# ---------------------------------------------------------------------------
# Coverage — additional uncovered lines: 201-205
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestShareConversationErrorPath:
def test_share_conversation_exception_returns_400(self, app):
"""Cover lines 201-205: exception during sharing returns 400."""
from application.api.user.sharing.routes import ShareConversation
mock_conversations = Mock()
mock_conversations.find_one.side_effect = Exception("DB error")
with patch(
"application.api.user.sharing.routes.conversations_collection",
mock_conversations,
):
with app.test_request_context(
"/api/share",
method="POST",
json={"conversation_id": str(ObjectId())},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = ShareConversation().post()
assert response.status_code == 400
# ---------------------------------------------------------------------------
# Additional coverage for sharing/routes.py
# Lines: 201-205: exception in try block (different entry point)
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestShareConversationInsertException:
"""Cover lines 201-205: exception during insert_one."""
def test_insert_one_exception_returns_400(self, app):
from application.api.user.sharing.routes import ShareConversation
mock_conversations = Mock()
mock_conversations.find_one.return_value = {
"_id": ObjectId(),
"user": "user1",
"queries": [],
}
mock_shared = Mock()
mock_shared.find_one.return_value = None
mock_shared.insert_one.side_effect = Exception("Insert failed")
with patch(
"application.api.user.sharing.routes.conversations_collection",
mock_conversations,
), patch(
"application.api.user.sharing.routes.shared_conversations_collections",
mock_shared,
):
with app.test_request_context(
"/api/share",
method="POST",
json={"conversation_id": str(ObjectId())},
):
from flask import request
request.decoded_token = {"sub": "user1"}
response = ShareConversation().post()
assert response.status_code == 400
File diff suppressed because it is too large. Load diff