mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-15 20:15:30 +00:00
chore: utils tests
This commit is contained in:
1 parent
e06debad5f
commit
126fa01b14
8 files changed
+2826
No files matched your search
@@ -0,0 +1,331 @@
|
||||
"""Tests for application/api/answer/services/stream_processor.py — get_prompt and helpers."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.api.answer.services.stream_processor import get_prompt
|
||||
|
||||
|
||||
class TestGetPrompt:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_default_preset(self):
|
||||
prompt = get_prompt("default")
|
||||
assert isinstance(prompt, str)
|
||||
assert len(prompt) > 0
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_creative_preset(self):
|
||||
prompt = get_prompt("creative")
|
||||
assert isinstance(prompt, str)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_strict_preset(self):
|
||||
prompt = get_prompt("strict")
|
||||
assert isinstance(prompt, str)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_reduce_preset(self):
|
||||
prompt = get_prompt("reduce")
|
||||
assert isinstance(prompt, str)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_agentic_default_preset(self):
|
||||
prompt = get_prompt("agentic_default")
|
||||
assert isinstance(prompt, str)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_agentic_creative_preset(self):
|
||||
prompt = get_prompt("agentic_creative")
|
||||
assert isinstance(prompt, str)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_agentic_strict_preset(self):
|
||||
prompt = get_prompt("agentic_strict")
|
||||
assert isinstance(prompt, str)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_mongo_prompt_by_id(self):
|
||||
mock_collection = MagicMock()
|
||||
mock_collection.find_one.return_value = {"_id": "abc", "content": "Custom prompt"}
|
||||
prompt = get_prompt("507f1f77bcf86cd799439011", prompts_collection=mock_collection)
|
||||
assert prompt == "Custom prompt"
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_mongo_prompt_not_found_raises(self):
|
||||
mock_collection = MagicMock()
|
||||
mock_collection.find_one.return_value = None
|
||||
with pytest.raises(ValueError, match="Invalid prompt ID"):
|
||||
get_prompt("507f1f77bcf86cd799439011", prompts_collection=mock_collection)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_invalid_id_raises(self):
|
||||
mock_collection = MagicMock()
|
||||
mock_collection.find_one.side_effect = Exception("bad id")
|
||||
with pytest.raises(ValueError, match="Invalid prompt ID"):
|
||||
get_prompt("not-an-objectid", prompts_collection=mock_collection)
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_mongo_fallback_when_no_collection(self):
|
||||
"""When no collection passed, it reads from MongoDB."""
|
||||
mock_collection = MagicMock()
|
||||
mock_collection.find_one.return_value = {"content": "From DB"}
|
||||
mock_db = MagicMock()
|
||||
mock_db.__getitem__ = MagicMock(return_value=mock_collection)
|
||||
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "test_db"
|
||||
MockMongo.get_client.return_value = {"test_db": mock_db}
|
||||
prompt = get_prompt("507f1f77bcf86cd799439011")
|
||||
assert prompt == "From DB"
|
||||
|
||||
|
||||
class TestStreamProcessorInit:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_init_sets_attributes(self):
|
||||
mock_db = MagicMock()
|
||||
mock_client = {"docsgpt": mock_db}
|
||||
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = mock_client
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(
|
||||
request_data={"conversation_id": "conv1", "agent_id": "a1"},
|
||||
decoded_token={"sub": "user1"},
|
||||
)
|
||||
assert sp.conversation_id == "conv1"
|
||||
assert sp.initial_user_id == "user1"
|
||||
assert sp.agent_id == "a1"
|
||||
assert sp.history == []
|
||||
assert sp.attachments == []
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_init_no_token(self):
|
||||
mock_db = MagicMock()
|
||||
mock_client = {"docsgpt": mock_db}
|
||||
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = mock_client
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token=None)
|
||||
assert sp.initial_user_id is None
|
||||
|
||||
|
||||
class TestGetAttachmentsContent:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_empty_ids_returns_empty(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
result = sp._get_attachments_content([], "u")
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_returns_matching_attachments(self):
|
||||
mock_db = MagicMock()
|
||||
mock_attachments = MagicMock()
|
||||
mock_attachments.find_one.return_value = {"_id": "att1", "content": "data"}
|
||||
mock_db.__getitem__ = MagicMock(return_value=mock_attachments)
|
||||
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
result = sp._get_attachments_content(["507f1f77bcf86cd799439011"], "u")
|
||||
assert len(result) == 1
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_invalid_attachment_id_continues(self):
|
||||
mock_db = MagicMock()
|
||||
mock_attachments = MagicMock()
|
||||
mock_attachments.find_one.side_effect = Exception("bad id")
|
||||
mock_db.__getitem__ = MagicMock(return_value=mock_attachments)
|
||||
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
result = sp._get_attachments_content(["bad"], "u")
|
||||
assert result == []
|
||||
|
||||
|
||||
class TestResolveAgentId:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_from_request_data(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(
|
||||
request_data={"agent_id": "agent_123"},
|
||||
decoded_token={"sub": "u"},
|
||||
)
|
||||
assert sp._resolve_agent_id() == "agent_123"
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_no_agent_no_conversation(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
assert sp._resolve_agent_id() is None
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_from_conversation(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(
|
||||
request_data={"conversation_id": "conv1"},
|
||||
decoded_token={"sub": "u"},
|
||||
)
|
||||
sp.conversation_service = MagicMock()
|
||||
sp.conversation_service.get_conversation.return_value = {"agent_id": "from_conv"}
|
||||
assert sp._resolve_agent_id() == "from_conv"
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_conversation_not_found(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(
|
||||
request_data={"conversation_id": "conv1"},
|
||||
decoded_token={"sub": "u"},
|
||||
)
|
||||
sp.conversation_service = MagicMock()
|
||||
sp.conversation_service.get_conversation.return_value = None
|
||||
assert sp._resolve_agent_id() is None
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_conversation_lookup_exception(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(
|
||||
request_data={"conversation_id": "conv1"},
|
||||
decoded_token={"sub": "u"},
|
||||
)
|
||||
sp.conversation_service = MagicMock()
|
||||
sp.conversation_service.get_conversation.side_effect = Exception("db error")
|
||||
assert sp._resolve_agent_id() is None
|
||||
|
||||
|
||||
class TestGetPromptContent:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_caches_result(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
sp.agent_config = {"prompt_id": "default"}
|
||||
result1 = sp._get_prompt_content()
|
||||
result2 = sp._get_prompt_content()
|
||||
assert result1 == result2
|
||||
assert result1 is not None
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_no_prompt_id(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
sp.agent_config = {}
|
||||
assert sp._get_prompt_content() is None
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_invalid_prompt_id_returns_none(self):
|
||||
mock_db = MagicMock()
|
||||
mock_prompts = MagicMock()
|
||||
mock_prompts.find_one.side_effect = Exception("bad")
|
||||
mock_db.__getitem__ = MagicMock(return_value=mock_prompts)
|
||||
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
sp.agent_config = {"prompt_id": "bad_id"}
|
||||
assert sp._get_prompt_content() is None
|
||||
|
||||
|
||||
class TestGetRequiredToolActions:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_no_prompt_returns_none(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
sp.agent_config = {}
|
||||
assert sp._get_required_tool_actions() is None
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_no_template_syntax_returns_empty(self):
|
||||
mock_db = MagicMock()
|
||||
with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \
|
||||
patch("application.api.answer.services.stream_processor.settings") as mock_settings:
|
||||
mock_settings.MONGO_DB_NAME = "docsgpt"
|
||||
MockMongo.get_client.return_value = {"docsgpt": mock_db}
|
||||
|
||||
from application.api.answer.services.stream_processor import StreamProcessor
|
||||
sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"})
|
||||
sp.agent_config = {"prompt_id": "default"}
|
||||
sp._prompt_content = "No template syntax here"
|
||||
result = sp._get_required_tool_actions()
|
||||
assert result == {}
|
||||
@@ -0,0 +1,333 @@
|
||||
"""Tests for application/api/connector/routes.py"""
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import mongomock
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
with patch("application.app.handle_auth", return_value={"sub": "test_user"}):
|
||||
from application.app import app as flask_app
|
||||
flask_app.config["TESTING"] = True
|
||||
yield flask_app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(app):
|
||||
return app.test_client()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_sessions(monkeypatch):
|
||||
mock_client = mongomock.MongoClient()
|
||||
mock_db = mock_client["docsgpt"]
|
||||
sessions = mock_db["connector_sessions"]
|
||||
sources = mock_db["sources"]
|
||||
monkeypatch.setattr("application.api.connector.routes.sessions_collection", sessions)
|
||||
monkeypatch.setattr("application.api.connector.routes.sources_collection", sources)
|
||||
return {"sessions": sessions, "sources": sources}
|
||||
|
||||
|
||||
class TestConnectorAuth:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_missing_provider(self, client):
|
||||
resp = client.get("/api/connectors/auth")
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_unsupported_provider(self, client):
|
||||
resp = client.get("/api/connectors/auth?provider=dropbox")
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_unauthorized(self, client, app):
|
||||
with patch("application.app.handle_auth", return_value=None):
|
||||
resp = client.get("/api/connectors/auth?provider=google_drive")
|
||||
data = json.loads(resp.data)
|
||||
# decoded_token is None -> 401
|
||||
assert resp.status_code == 401 or data.get("error") == "Unauthorized"
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_success(self, client, mock_sessions):
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
MockCC.is_supported.return_value = True
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.get_authorization_url.return_value = "https://oauth.example.com/auth"
|
||||
MockCC.create_auth.return_value = mock_auth
|
||||
|
||||
resp = client.get("/api/connectors/auth?provider=google_drive")
|
||||
assert resp.status_code == 200
|
||||
data = json.loads(resp.data)
|
||||
assert data["success"] is True
|
||||
assert "authorization_url" in data
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_exception_returns_500(self, client, mock_sessions):
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
MockCC.is_supported.return_value = True
|
||||
MockCC.create_auth.side_effect = Exception("oauth fail")
|
||||
resp = client.get("/api/connectors/auth?provider=google_drive")
|
||||
assert resp.status_code == 500
|
||||
|
||||
|
||||
class TestConnectorFiles:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_missing_params(self, client):
|
||||
resp = client.post("/api/connectors/files", json={"provider": "google_drive"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_invalid_session(self, client, mock_sessions):
|
||||
resp = client.post("/api/connectors/files", json={
|
||||
"provider": "google_drive",
|
||||
"session_token": "bad_token",
|
||||
})
|
||||
assert resp.status_code == 401
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_success(self, client, mock_sessions):
|
||||
mock_sessions["sessions"].insert_one({
|
||||
"session_token": "valid_tok",
|
||||
"user": "test_user",
|
||||
"provider": "google_drive",
|
||||
})
|
||||
|
||||
mock_doc = MagicMock()
|
||||
mock_doc.doc_id = "f1"
|
||||
mock_doc.extra_info = {
|
||||
"file_name": "test.pdf",
|
||||
"mime_type": "application/pdf",
|
||||
"size": 1024,
|
||||
"modified_time": "2025-01-01T12:00:00.000Z",
|
||||
"is_folder": False,
|
||||
}
|
||||
mock_loader = MagicMock()
|
||||
mock_loader.load_data.return_value = [mock_doc]
|
||||
mock_loader.next_page_token = None
|
||||
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
MockCC.create_connector.return_value = mock_loader
|
||||
resp = client.post("/api/connectors/files", json={
|
||||
"provider": "google_drive",
|
||||
"session_token": "valid_tok",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = json.loads(resp.data)
|
||||
assert data["success"] is True
|
||||
assert len(data["files"]) == 1
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_no_modified_time(self, client, mock_sessions):
|
||||
mock_sessions["sessions"].insert_one({
|
||||
"session_token": "tok2",
|
||||
"user": "test_user",
|
||||
"provider": "google_drive",
|
||||
})
|
||||
mock_doc = MagicMock()
|
||||
mock_doc.doc_id = "f1"
|
||||
mock_doc.extra_info = {"file_name": "test.pdf", "mime_type": "application/pdf"}
|
||||
mock_loader = MagicMock()
|
||||
mock_loader.load_data.return_value = [mock_doc]
|
||||
mock_loader.next_page_token = None
|
||||
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
MockCC.create_connector.return_value = mock_loader
|
||||
resp = client.post("/api/connectors/files", json={
|
||||
"provider": "google_drive", "session_token": "tok2",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
class TestConnectorValidateSession:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_missing_params(self, client):
|
||||
resp = client.post("/api/connectors/validate-session", json={"provider": "google_drive"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_invalid_session(self, client, mock_sessions):
|
||||
resp = client.post("/api/connectors/validate-session", json={
|
||||
"provider": "google_drive", "session_token": "bad",
|
||||
})
|
||||
assert resp.status_code == 401
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_valid_non_expired(self, client, mock_sessions):
|
||||
mock_sessions["sessions"].insert_one({
|
||||
"session_token": "valid",
|
||||
"user": "test_user",
|
||||
"provider": "google_drive",
|
||||
"token_info": {"access_token": "at", "refresh_token": "rt", "expiry": None},
|
||||
"user_email": "user@example.com",
|
||||
})
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.is_token_expired.return_value = False
|
||||
MockCC.create_auth.return_value = mock_auth
|
||||
resp = client.post("/api/connectors/validate-session", json={
|
||||
"provider": "google_drive", "session_token": "valid",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = json.loads(resp.data)
|
||||
assert data["success"] is True
|
||||
assert data["expired"] is False
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_expired_with_refresh(self, client, mock_sessions):
|
||||
mock_sessions["sessions"].insert_one({
|
||||
"session_token": "expired_tok",
|
||||
"user": "test_user",
|
||||
"provider": "google_drive",
|
||||
"token_info": {"access_token": "old_at", "refresh_token": "rt", "expiry": 100},
|
||||
})
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.is_token_expired.return_value = True
|
||||
mock_auth.refresh_access_token.return_value = {"access_token": "new_at", "refresh_token": "rt"}
|
||||
mock_auth.sanitize_token_info.return_value = {"access_token": "new_at", "refresh_token": "rt"}
|
||||
MockCC.create_auth.return_value = mock_auth
|
||||
resp = client.post("/api/connectors/validate-session", json={
|
||||
"provider": "google_drive", "session_token": "expired_tok",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_expired_no_refresh(self, client, mock_sessions):
|
||||
mock_sessions["sessions"].insert_one({
|
||||
"session_token": "exp_no_ref",
|
||||
"user": "test_user",
|
||||
"token_info": {"access_token": "at", "expiry": 100},
|
||||
})
|
||||
with patch("application.api.connector.routes.ConnectorCreator") as MockCC:
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.is_token_expired.return_value = True
|
||||
MockCC.create_auth.return_value = mock_auth
|
||||
resp = client.post("/api/connectors/validate-session", json={
|
||||
"provider": "google_drive", "session_token": "exp_no_ref",
|
||||
})
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
class TestConnectorDisconnect:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_missing_provider(self, client):
|
||||
resp = client.post("/api/connectors/disconnect", json={})
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_success_with_session(self, client, mock_sessions):
|
||||
mock_sessions["sessions"].insert_one({"session_token": "del_me", "provider": "google_drive"})
|
||||
resp = client.post("/api/connectors/disconnect", json={
|
||||
"provider": "google_drive", "session_token": "del_me",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = json.loads(resp.data)
|
||||
assert data["success"] is True
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_success_without_session(self, client, mock_sessions):
|
||||
resp = client.post("/api/connectors/disconnect", json={"provider": "google_drive"})
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
class TestConnectorSync:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_missing_params(self, client, mock_sessions):
|
||||
resp = client.post("/api/connectors/sync", json={"source_id": "abc"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_source_not_found(self, client, mock_sessions):
|
||||
from bson.objectid import ObjectId
|
||||
resp = client.post("/api/connectors/sync", json={
|
||||
"source_id": str(ObjectId()), "session_token": "tok",
|
||||
})
|
||||
assert resp.status_code == 404
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_unauthorized_source(self, client, mock_sessions):
|
||||
sid = mock_sessions["sources"].insert_one({"user": "other_user", "name": "src"}).inserted_id
|
||||
resp = client.post("/api/connectors/sync", json={
|
||||
"source_id": str(sid), "session_token": "tok",
|
||||
})
|
||||
assert resp.status_code == 403
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_missing_provider_in_remote_data(self, client, mock_sessions):
|
||||
sid = mock_sessions["sources"].insert_one({
|
||||
"user": "test_user", "name": "src", "remote_data": json.dumps({}),
|
||||
}).inserted_id
|
||||
resp = client.post("/api/connectors/sync", json={
|
||||
"source_id": str(sid), "session_token": "tok",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_success(self, client, mock_sessions):
|
||||
sid = mock_sessions["sources"].insert_one({
|
||||
"user": "test_user",
|
||||
"name": "src",
|
||||
"remote_data": json.dumps({"provider": "google_drive", "file_ids": ["f1"]}),
|
||||
}).inserted_id
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = "task_123"
|
||||
with patch("application.api.connector.routes.ingest_connector_task") as mock_ingest:
|
||||
mock_ingest.delay.return_value = mock_task
|
||||
resp = client.post("/api/connectors/sync", json={
|
||||
"source_id": str(sid), "session_token": "tok",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
data = json.loads(resp.data)
|
||||
assert data["task_id"] == "task_123"
|
||||
|
||||
|
||||
class TestConnectorCallbackStatus:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_success_status(self, client):
|
||||
resp = client.get("/api/connectors/callback-status?status=success&message=OK&provider=google_drive&session_token=tok&user_email=u@e.com")
|
||||
assert resp.status_code == 200
|
||||
assert b"success" in resp.data
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_error_status(self, client):
|
||||
resp = client.get("/api/connectors/callback-status?status=error&message=Failed")
|
||||
assert resp.status_code == 200
|
||||
assert b"error" in resp.data
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_cancelled_status(self, client):
|
||||
resp = client.get("/api/connectors/callback-status?status=cancelled&message=Cancelled&provider=google_drive")
|
||||
assert resp.status_code == 200
|
||||
assert b"cancelled" in resp.data
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_unknown_status_defaults_to_error(self, client):
|
||||
resp = client.get("/api/connectors/callback-status?status=badvalue")
|
||||
assert resp.status_code == 200
|
||||
assert b"error" in resp.data
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_html_escaping(self, client):
|
||||
resp = client.get('/api/connectors/callback-status?status=error&message=<script>alert(1)</script>')
|
||||
assert resp.status_code == 200
|
||||
# The raw <script> tag should be escaped (not executable)
|
||||
assert b"<script>alert(1)</script>" not in resp.data
|
||||
|
||||
|
||||
class TestBuildCallbackRedirect:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_builds_url(self):
|
||||
from application.api.connector.routes import build_callback_redirect
|
||||
url = build_callback_redirect({"status": "success", "message": "OK"})
|
||||
assert url.startswith("/api/connectors/callback-status?")
|
||||
assert "status=success" in url
|
||||
Reference in new issue
Block a user