mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 14:17:08 +00:00
Pg 4 (#2390)
* feat: postgres tests * feat: mongo cutoff * feat: mongo cutoff * feat: adjust docs and compose files * fix: mini code mongo removals * fix: tests and k8s mongo stuff * feat: test fixes * fix: ruff * fix: vale * Potential fix for pull request finding 'CodeQL / Clear-text logging of sensitive information' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fix: mini suggestions * vale lint fix 2 * fix: codeql columns thing * fix: test mongo * fix: tests coverage * feat: better tests 4 * feat: more tests * feat: decent coverage * fix: ruff fixes * fix: remove mongo mock * feat: enhance workflow engine and API routes; add document retrieval and source handling * feat: e2e tests * fix: mcp, mongo and more * fix: mini codeql warning * fix: agent chunk view * fix: mini issues * fix: more pg fixes * feat: postgres prep on start * feat: qa tests * fix: mini improvements * fix: tests --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Siddhant Rai <siddhant.rai.5686@gmail.com>
This commit is contained in:
312 files changed
+763725
-25159
No files matched your search
@@ -1,7 +1,8 @@
|
||||
from unittest.mock import Mock, patch
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from bson import ObjectId
|
||||
from flask import Flask
|
||||
|
||||
|
||||
@@ -11,30 +12,33 @@ def app():
|
||||
return app
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _patch_conversations_db(conn):
|
||||
@contextmanager
|
||||
def _yield_conn():
|
||||
yield conn
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_session", _yield_conn
|
||||
), patch(
|
||||
"application.api.user.conversations.routes.db_readonly", _yield_conn
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
def _seed_conversation(pg_conn, user_id, name="Test Conv"):
|
||||
"""Create a conversation and return its PG uuid id as str."""
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
repo = ConversationsRepository(pg_conn)
|
||||
conv = repo.create(user_id, name=name)
|
||||
return str(conv["id"])
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestDeleteConversation:
|
||||
|
||||
def test_deletes_conversation(self, app):
|
||||
from application.api.user.conversations.routes import DeleteConversation
|
||||
|
||||
conv_id = ObjectId()
|
||||
mock_collection = Mock()
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context(f"/api/delete_conversation?id={conv_id}"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = DeleteConversation().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json["success"] is True
|
||||
mock_collection.delete_one.assert_called_once_with(
|
||||
{"_id": conv_id, "user": "user1"}
|
||||
)
|
||||
pass
|
||||
|
||||
def test_returns_401_unauthenticated(self, app):
|
||||
from application.api.user.conversations.routes import DeleteConversation
|
||||
@@ -61,24 +65,7 @@ class TestDeleteConversation:
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestDeleteAllConversations:
|
||||
|
||||
def test_deletes_all_for_user(self, app):
|
||||
from application.api.user.conversations.routes import DeleteAllConversations
|
||||
|
||||
mock_collection = Mock()
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context("/api/delete_all_conversations"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = DeleteAllConversations().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
mock_collection.delete_many.assert_called_once_with({"user": "user1"})
|
||||
pass
|
||||
|
||||
def test_returns_401_unauthenticated(self, app):
|
||||
from application.api.user.conversations.routes import DeleteAllConversations
|
||||
@@ -94,38 +81,7 @@ class TestDeleteAllConversations:
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetConversations:
|
||||
|
||||
def test_returns_conversations(self, app):
|
||||
from application.api.user.conversations.routes import GetConversations
|
||||
|
||||
conv_id = ObjectId()
|
||||
mock_cursor = Mock()
|
||||
mock_cursor.sort.return_value.limit.return_value = [
|
||||
{
|
||||
"_id": conv_id,
|
||||
"name": "Test Chat",
|
||||
"agent_id": "agent1",
|
||||
"is_shared_usage": False,
|
||||
}
|
||||
]
|
||||
mock_collection = Mock()
|
||||
mock_collection.find.return_value = mock_cursor
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context("/api/get_conversations"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = GetConversations().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json
|
||||
assert len(data) == 1
|
||||
assert data[0]["id"] == str(conv_id)
|
||||
assert data[0]["name"] == "Test Chat"
|
||||
pass
|
||||
|
||||
def test_returns_401_unauthenticated(self, app):
|
||||
from application.api.user.conversations.routes import GetConversations
|
||||
@@ -141,54 +97,7 @@ class TestGetConversations:
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetSingleConversation:
|
||||
|
||||
def test_returns_conversation(self, app):
|
||||
from application.api.user.conversations.routes import GetSingleConversation
|
||||
|
||||
conv_id = ObjectId()
|
||||
mock_conv_collection = Mock()
|
||||
mock_conv_collection.find_one.return_value = {
|
||||
"_id": conv_id,
|
||||
"name": "Chat",
|
||||
"queries": [{"prompt": "hi", "response": "hello"}],
|
||||
"agent_id": "agent1",
|
||||
}
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_conv_collection,
|
||||
):
|
||||
with app.test_request_context(
|
||||
f"/api/get_single_conversation?id={conv_id}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json["queries"] == [{"prompt": "hi", "response": "hello"}]
|
||||
assert response.json["agent_id"] == "agent1"
|
||||
|
||||
def test_returns_404_not_found(self, app):
|
||||
from application.api.user.conversations.routes import GetSingleConversation
|
||||
|
||||
mock_collection = Mock()
|
||||
mock_collection.find_one.return_value = None
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context(
|
||||
f"/api/get_single_conversation?id={ObjectId()}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 404
|
||||
pass
|
||||
|
||||
def test_returns_400_missing_id(self, app):
|
||||
from application.api.user.conversations.routes import GetSingleConversation
|
||||
@@ -201,72 +110,11 @@ class TestGetSingleConversation:
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_resolves_attachments(self, app):
|
||||
from application.api.user.conversations.routes import GetSingleConversation
|
||||
|
||||
conv_id = ObjectId()
|
||||
att_id = ObjectId()
|
||||
mock_conv_collection = Mock()
|
||||
mock_conv_collection.find_one.return_value = {
|
||||
"_id": conv_id,
|
||||
"name": "Chat",
|
||||
"queries": [
|
||||
{"prompt": "hi", "response": "hello", "attachments": [str(att_id)]}
|
||||
],
|
||||
}
|
||||
mock_att_collection = Mock()
|
||||
mock_att_collection.find_one.return_value = {
|
||||
"_id": att_id,
|
||||
"filename": "doc.pdf",
|
||||
}
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_conv_collection,
|
||||
), patch(
|
||||
"application.api.user.conversations.routes.attachments_collection",
|
||||
mock_att_collection,
|
||||
):
|
||||
with app.test_request_context(
|
||||
f"/api/get_single_conversation?id={conv_id}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
attachments = response.json["queries"][0]["attachments"]
|
||||
assert len(attachments) == 1
|
||||
assert attachments[0]["fileName"] == "doc.pdf"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestUpdateConversationName:
|
||||
|
||||
def test_updates_name(self, app):
|
||||
from application.api.user.conversations.routes import UpdateConversationName
|
||||
|
||||
conv_id = ObjectId()
|
||||
mock_collection = Mock()
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context(
|
||||
"/api/update_conversation_name",
|
||||
method="POST",
|
||||
json={"id": str(conv_id), "name": "New Name"},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = UpdateConversationName().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json["success"] is True
|
||||
mock_collection.update_one.assert_called_once()
|
||||
pass
|
||||
|
||||
def test_returns_400_missing_fields(self, app):
|
||||
from application.api.user.conversations.routes import UpdateConversationName
|
||||
@@ -274,7 +122,7 @@ class TestUpdateConversationName:
|
||||
with app.test_request_context(
|
||||
"/api/update_conversation_name",
|
||||
method="POST",
|
||||
json={"id": str(ObjectId())},
|
||||
json={"id": str(uuid.uuid4().hex[:24])},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
@@ -286,63 +134,7 @@ class TestUpdateConversationName:
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestSubmitFeedback:
|
||||
|
||||
def test_submits_positive_feedback(self, app):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
|
||||
conv_id = ObjectId()
|
||||
mock_collection = Mock()
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": "LIKE",
|
||||
"conversation_id": str(conv_id),
|
||||
"question_index": 0,
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json["success"] is True
|
||||
call_args = mock_collection.update_one.call_args
|
||||
assert "$set" in call_args[0][1]
|
||||
|
||||
def test_removes_feedback_when_null(self, app):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
|
||||
conv_id = ObjectId()
|
||||
mock_collection = Mock()
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.conversations_collection",
|
||||
mock_collection,
|
||||
):
|
||||
with app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": None,
|
||||
"conversation_id": str(conv_id),
|
||||
"question_index": 0,
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "user1"}
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
call_args = mock_collection.update_one.call_args
|
||||
assert "$unset" in call_args[0][1]
|
||||
pass
|
||||
|
||||
def test_returns_400_missing_fields(self, app):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
@@ -358,3 +150,480 @@ class TestSubmitFeedback:
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Happy-path tests exercising real PG via the ephemeral pg_conn fixture.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestDeleteConversationHappy:
|
||||
def test_deletes_existing_conversation(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import DeleteConversation
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-del"
|
||||
conv_id = _seed_conversation(pg_conn, user)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
f"/api/delete_conversation?id={conv_id}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = DeleteConversation().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json["success"] is True
|
||||
# Gone
|
||||
assert ConversationsRepository(pg_conn).get_any(conv_id, user) is None
|
||||
|
||||
def test_delete_nonexistent_still_returns_200(self, app, pg_conn):
|
||||
"""get_any returns None, so delete is a no-op but endpoint succeeds."""
|
||||
from application.api.user.conversations.routes import DeleteConversation
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
f"/api/delete_conversation?id={uuid.uuid4()}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = DeleteConversation().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_db_error_returns_400(self, app):
|
||||
from application.api.user.conversations.routes import DeleteConversation
|
||||
|
||||
@contextmanager
|
||||
def _broken():
|
||||
raise RuntimeError("boom")
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_session", _broken
|
||||
), app.test_request_context("/api/delete_conversation?id=abc"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = DeleteConversation().post()
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestDeleteAllConversationsHappy:
|
||||
def test_deletes_all_conversations(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import (
|
||||
DeleteAllConversations,
|
||||
)
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-delall"
|
||||
_seed_conversation(pg_conn, user, name="a")
|
||||
_seed_conversation(pg_conn, user, name="b")
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/delete_all_conversations"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = DeleteAllConversations().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert ConversationsRepository(pg_conn).list_for_user(user) == []
|
||||
|
||||
def test_db_error_returns_400(self, app):
|
||||
from application.api.user.conversations.routes import (
|
||||
DeleteAllConversations,
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def _broken():
|
||||
raise RuntimeError("boom")
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_session", _broken
|
||||
), app.test_request_context("/api/delete_all_conversations"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = DeleteAllConversations().get()
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestGetConversationsHappy:
|
||||
def test_returns_list_of_conversations(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import GetConversations
|
||||
|
||||
user = "user-list"
|
||||
c1 = _seed_conversation(pg_conn, user, name="one")
|
||||
c2 = _seed_conversation(pg_conn, user, name="two")
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/get_conversations"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = GetConversations().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
ids = {c["id"] for c in response.json}
|
||||
assert c1 in ids and c2 in ids
|
||||
# agent_id, is_shared_usage, shared_token keys present
|
||||
for c in response.json:
|
||||
assert "agent_id" in c
|
||||
assert "is_shared_usage" in c
|
||||
assert "shared_token" in c
|
||||
|
||||
def test_db_error_returns_400(self, app):
|
||||
from application.api.user.conversations.routes import GetConversations
|
||||
|
||||
@contextmanager
|
||||
def _broken():
|
||||
raise RuntimeError("boom")
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_readonly", _broken
|
||||
), app.test_request_context("/api/get_conversations"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = GetConversations().get()
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestGetSingleConversationHappy:
|
||||
def test_returns_401_unauthenticated(self, app):
|
||||
from application.api.user.conversations.routes import (
|
||||
GetSingleConversation,
|
||||
)
|
||||
|
||||
with app.test_request_context("/api/get_single_conversation?id=x"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = None
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_returns_404_not_found(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import (
|
||||
GetSingleConversation,
|
||||
)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
f"/api/get_single_conversation?id={uuid.uuid4()}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_returns_conversation_with_messages(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import (
|
||||
GetSingleConversation,
|
||||
)
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-get"
|
||||
conv_id = _seed_conversation(pg_conn, user, name="chat")
|
||||
# Append a message
|
||||
ConversationsRepository(pg_conn).append_message(
|
||||
conv_id,
|
||||
{
|
||||
"prompt": "hi",
|
||||
"response": "hello",
|
||||
"thought": None,
|
||||
"sources": [],
|
||||
"tool_calls": [],
|
||||
"timestamp": None,
|
||||
"model_id": None,
|
||||
},
|
||||
)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
f"/api/get_single_conversation?id={conv_id}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json
|
||||
assert isinstance(data["queries"], list)
|
||||
assert data["queries"][0]["prompt"] == "hi"
|
||||
assert data["queries"][0]["response"] == "hello"
|
||||
|
||||
def test_returns_message_with_dict_feedback(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import (
|
||||
GetSingleConversation,
|
||||
)
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-fb"
|
||||
conv_id = _seed_conversation(pg_conn, user, name="fb")
|
||||
repo = ConversationsRepository(pg_conn)
|
||||
repo.append_message(conv_id, {"prompt": "p", "response": "r"})
|
||||
repo.set_feedback(
|
||||
conv_id, 0, {"text": "like", "timestamp": "2024-01-01T00:00:00Z"}
|
||||
)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
f"/api/get_single_conversation?id={conv_id}"
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 200
|
||||
q = response.json["queries"][0]
|
||||
assert q["feedback"] == "like"
|
||||
assert q["feedback_timestamp"] == "2024-01-01T00:00:00Z"
|
||||
|
||||
def test_db_error_returns_400(self, app):
|
||||
from application.api.user.conversations.routes import (
|
||||
GetSingleConversation,
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def _broken():
|
||||
raise RuntimeError("boom")
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_readonly", _broken
|
||||
), app.test_request_context("/api/get_single_conversation?id=abc"):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = GetSingleConversation().get()
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestUpdateConversationNameHappy:
|
||||
def test_returns_401_unauthenticated(self, app):
|
||||
from application.api.user.conversations.routes import (
|
||||
UpdateConversationName,
|
||||
)
|
||||
|
||||
with app.test_request_context(
|
||||
"/api/update_conversation_name",
|
||||
method="POST",
|
||||
json={"id": "x", "name": "n"},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = None
|
||||
response = UpdateConversationName().post()
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_renames_conversation(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import (
|
||||
UpdateConversationName,
|
||||
)
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-rename"
|
||||
conv_id = _seed_conversation(pg_conn, user, name="old")
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/update_conversation_name",
|
||||
method="POST",
|
||||
json={"id": conv_id, "name": "new"},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = UpdateConversationName().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
got = ConversationsRepository(pg_conn).get_any(conv_id, user)
|
||||
assert got["name"] == "new"
|
||||
|
||||
def test_rename_nonexistent_still_returns_200(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import (
|
||||
UpdateConversationName,
|
||||
)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/update_conversation_name",
|
||||
method="POST",
|
||||
json={"id": str(uuid.uuid4()), "name": "n"},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = UpdateConversationName().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_db_error_returns_400(self, app):
|
||||
from application.api.user.conversations.routes import (
|
||||
UpdateConversationName,
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def _broken():
|
||||
raise RuntimeError("boom")
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_session", _broken
|
||||
), app.test_request_context(
|
||||
"/api/update_conversation_name",
|
||||
method="POST",
|
||||
json={"id": "x", "name": "n"},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = UpdateConversationName().post()
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestSubmitFeedbackHappy:
|
||||
def test_returns_401_unauthenticated(self, app):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
|
||||
with app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": "like",
|
||||
"question_index": 0,
|
||||
"conversation_id": "x",
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = None
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_submits_feedback(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-fb1"
|
||||
conv_id = _seed_conversation(pg_conn, user, name="fb")
|
||||
ConversationsRepository(pg_conn).append_message(
|
||||
conv_id, {"prompt": "p", "response": "r"}
|
||||
)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": "LIKE", # uppercase normalized to lowercase
|
||||
"question_index": 0,
|
||||
"conversation_id": conv_id,
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
msgs = ConversationsRepository(pg_conn).get_messages(conv_id)
|
||||
fb = msgs[0].get("feedback")
|
||||
assert fb and fb.get("text") == "like"
|
||||
|
||||
def test_none_feedback_allowed(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
from application.storage.db.repositories.conversations import (
|
||||
ConversationsRepository,
|
||||
)
|
||||
|
||||
user = "user-fb-none"
|
||||
conv_id = _seed_conversation(pg_conn, user)
|
||||
ConversationsRepository(pg_conn).append_message(
|
||||
conv_id, {"prompt": "p", "response": "r"}
|
||||
)
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": None,
|
||||
"question_index": 0,
|
||||
"conversation_id": conv_id,
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": user}
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_returns_404_for_missing_conversation(self, app, pg_conn):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
|
||||
with _patch_conversations_db(pg_conn), app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": "like",
|
||||
"question_index": 0,
|
||||
"conversation_id": str(uuid.uuid4()),
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_db_error_returns_400(self, app):
|
||||
from application.api.user.conversations.routes import SubmitFeedback
|
||||
|
||||
@contextmanager
|
||||
def _broken():
|
||||
raise RuntimeError("boom")
|
||||
yield
|
||||
|
||||
with patch(
|
||||
"application.api.user.conversations.routes.db_session", _broken
|
||||
), app.test_request_context(
|
||||
"/api/feedback",
|
||||
method="POST",
|
||||
json={
|
||||
"feedback": "like",
|
||||
"question_index": 0,
|
||||
"conversation_id": "x",
|
||||
},
|
||||
):
|
||||
from flask import request
|
||||
|
||||
request.decoded_token = {"sub": "u"}
|
||||
response = SubmitFeedback().post()
|
||||
|
||||
assert response.status_code == 400
|
||||
Reference in new issue
Block a user