mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-05-22 13:25:08 +00:00
* 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>
180 lines
6.2 KiB
Python
180 lines
6.2 KiB
Python
"""Tests for the Postgres dual-write path inside WorkflowAgent._save_workflow_run.
|
|
|
|
Specifically verifies the inner ``_pg_write`` closure that:
|
|
1. Calls WorkflowsRepository.get_by_legacy_id() to resolve the Mongo workflow id.
|
|
2. Returns early when the workflow is not found in Postgres.
|
|
3. Calls WorkflowRunsRepository.create() with the correct arguments when the
|
|
workflow is found.
|
|
|
|
No bson/pymongo imports. Mongo collections are mocked; repository objects are
|
|
constructed via Mock so no real DB connection is needed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _make_agent(**overrides):
|
|
"""Construct a WorkflowAgent with mocked base-class dependencies."""
|
|
defaults = {
|
|
"endpoint": "https://api.example.com",
|
|
"llm_name": "openai",
|
|
"model_id": "gpt-4",
|
|
"api_key": "test_key",
|
|
"user_api_key": None,
|
|
"prompt": "You are helpful.",
|
|
"chat_history": [],
|
|
"decoded_token": {"sub": "user1"},
|
|
"attachments": [],
|
|
"json_schema": None,
|
|
}
|
|
defaults.update(overrides)
|
|
with patch("application.agents.workflow_agent.log_activity", lambda **kw: lambda f: f):
|
|
from application.agents.workflow_agent import WorkflowAgent
|
|
|
|
agent = WorkflowAgent(**defaults)
|
|
return agent
|
|
|
|
|
|
def _fake_oid():
|
|
"""Return a 24-character hex string used as a Mongo ObjectId substitute."""
|
|
return uuid.uuid4().hex[:24]
|
|
|
|
|
|
def _stub_mongo(agent, insert_id=None):
|
|
"""Wire a fake Mongo that returns ``insert_id`` from insert_one."""
|
|
mock_coll = MagicMock()
|
|
result_mock = MagicMock()
|
|
result_mock.inserted_id = insert_id or _fake_oid()
|
|
mock_coll.insert_one.return_value = result_mock
|
|
|
|
mock_db = MagicMock()
|
|
mock_db.__getitem__ = MagicMock(return_value=mock_coll)
|
|
return mock_coll, mock_db
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _save_workflow_run — PG dual-write logic
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSaveWorkflowRunPgWrite:
|
|
|
|
def _make_engine(self):
|
|
engine = MagicMock()
|
|
engine.state = {"input": "query text"}
|
|
engine.execution_log = []
|
|
engine.get_execution_summary.return_value = []
|
|
return engine
|
|
|
|
# ------------------------------------------------------------------
|
|
# dual_write is called exactly once when Mongo insert succeeds
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Inner _pg_write skips create when workflow not in PG
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Inner _pg_write calls create when workflow IS found in PG
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# pg_write skips entirely when workflow_id is empty
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# Mongo insert failure prevents pg_write from being called
|
|
# ------------------------------------------------------------------
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _determine_run_status — Postgres-agnostic (but PG value mapping matters)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDetermineRunStatusPg:
|
|
|
|
def test_completed_value_matches_pg_enum(self):
|
|
"""The string stored in Postgres must match ExecutionStatus.COMPLETED.value."""
|
|
from application.agents.workflows.schemas import ExecutionStatus
|
|
|
|
agent = _make_agent()
|
|
agent._engine = MagicMock()
|
|
agent._engine.execution_log = []
|
|
status = agent._determine_run_status()
|
|
assert status == ExecutionStatus.COMPLETED
|
|
# Value stored in PG column
|
|
assert status.value == "completed"
|
|
|
|
def test_failed_value_matches_pg_enum(self):
|
|
from application.agents.workflows.schemas import ExecutionStatus
|
|
|
|
agent = _make_agent()
|
|
agent._engine = MagicMock()
|
|
agent._engine.execution_log = [{"status": "failed"}]
|
|
status = agent._determine_run_status()
|
|
assert status == ExecutionStatus.FAILED
|
|
assert status.value == "failed"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _serialize_state — sanity for types stored in PG JSONB columns
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSerializeStateForPg:
|
|
|
|
def test_datetime_serialized_to_iso_string(self):
|
|
agent = _make_agent()
|
|
dt = datetime(2025, 3, 15, 8, 0, 0, tzinfo=timezone.utc)
|
|
result = agent._serialize_state({"ts": dt})
|
|
assert isinstance(result["ts"], str)
|
|
assert "2025-03-15" in result["ts"]
|
|
|
|
def test_nested_dict_keys_become_strings(self):
|
|
agent = _make_agent()
|
|
result = agent._serialize_state({"data": {1: "one", 2: "two"}})
|
|
assert "1" in result["data"]
|
|
assert "2" in result["data"]
|
|
|
|
def test_tuple_becomes_list_for_jsonb(self):
|
|
"""JSONB cannot store Python tuples; they must be converted to lists."""
|
|
agent = _make_agent()
|
|
result = agent._serialize_state({"pair": (10, 20)})
|
|
assert result["pair"] == [10, 20]
|
|
assert isinstance(result["pair"], list)
|
|
|
|
def test_none_values_preserved(self):
|
|
agent = _make_agent()
|
|
result = agent._serialize_state({"nothing": None})
|
|
assert result["nothing"] is None
|
|
|
|
def test_unknown_object_becomes_string(self):
|
|
agent = _make_agent()
|
|
|
|
class Custom:
|
|
def __str__(self):
|
|
return "custom_repr"
|
|
|
|
result = agent._serialize_state({"obj": Custom()})
|
|
assert result["obj"] == "custom_repr"
|