mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 22:17:00 +00:00
feat: init pg migration
This commit is contained in:
1 parent
bd03a513e3
commit
ececcb8b17
31 files changed
+2119
-42
No files matched your search
@@ -0,0 +1,179 @@
|
||||
"""Integration tests for ``UsersRepository`` against a live Postgres.
|
||||
|
||||
These tests need:
|
||||
|
||||
* A running Postgres reachable via ``POSTGRES_URI``
|
||||
* Alembic migration ``0001_initial`` applied
|
||||
|
||||
They are skipped automatically by the default ``pytest`` run because
|
||||
``pytest.ini`` has ``--ignore=tests/integration`` in ``addopts``. They
|
||||
are additionally marked ``@pytest.mark.integration`` so they can be
|
||||
selected/excluded explicitly. Run them locally with::
|
||||
|
||||
.venv/bin/python -m pytest tests/integration/test_users_repository.py \\
|
||||
--override-ini="addopts=" --no-cov
|
||||
|
||||
Covers every operation the legacy Mongo code performs on
|
||||
``users_collection``: upsert + get + add/remove on the pinned and
|
||||
shared_with_me JSONB arrays, plus explicit tenant-isolation checks
|
||||
(a user's operations must never touch another user's data).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from application.storage.db.repositories.users import UsersRepository
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repo(pg_clean_users):
|
||||
return UsersRepository(pg_clean_users)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestUpsert:
|
||||
def test_upsert_creates_new_user_with_default_preferences(self, repo):
|
||||
doc = repo.upsert("alice@example.com")
|
||||
assert doc["user_id"] == "alice@example.com"
|
||||
assert doc["agent_preferences"] == {"pinned": [], "shared_with_me": []}
|
||||
assert "id" in doc
|
||||
assert "_id" in doc # Mongo-compat alias
|
||||
|
||||
def test_upsert_is_idempotent(self, repo):
|
||||
first = repo.upsert("alice@example.com")
|
||||
second = repo.upsert("alice@example.com")
|
||||
assert first["id"] == second["id"]
|
||||
assert first["agent_preferences"] == second["agent_preferences"]
|
||||
|
||||
def test_upsert_preserves_existing_preferences(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
doc = repo.upsert("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-1"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestGet:
|
||||
def test_get_returns_none_for_missing_user(self, repo):
|
||||
assert repo.get("nobody@example.com") is None
|
||||
|
||||
def test_get_returns_dict_for_existing_user(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc is not None
|
||||
assert doc["user_id"] == "alice@example.com"
|
||||
assert doc["agent_preferences"] == {"pinned": [], "shared_with_me": []}
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestAddPinned:
|
||||
def test_add_pinned_to_new_user_creates_user(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-1"]
|
||||
|
||||
def test_add_pinned_is_idempotent(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-1"]
|
||||
|
||||
def test_add_pinned_preserves_order(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
repo.add_pinned("alice@example.com", "agent-2")
|
||||
repo.add_pinned("alice@example.com", "agent-3")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-1", "agent-2", "agent-3"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestRemovePinned:
|
||||
def test_remove_pinned_single(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
repo.add_pinned("alice@example.com", "agent-2")
|
||||
repo.remove_pinned("alice@example.com", "agent-1")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-2"]
|
||||
|
||||
def test_remove_pinned_missing_is_noop(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-1")
|
||||
repo.remove_pinned("alice@example.com", "agent-999")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-1"]
|
||||
|
||||
def test_remove_pinned_bulk(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
for i in range(5):
|
||||
repo.add_pinned("alice@example.com", f"agent-{i}")
|
||||
repo.remove_pinned_bulk("alice@example.com", ["agent-1", "agent-3", "agent-999"])
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-0", "agent-2", "agent-4"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestSharedWithMe:
|
||||
def test_add_shared_is_idempotent(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_shared("alice@example.com", "agent-x")
|
||||
repo.add_shared("alice@example.com", "agent-x")
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["shared_with_me"] == ["agent-x"]
|
||||
|
||||
def test_remove_shared_bulk(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
for i in range(3):
|
||||
repo.add_shared("alice@example.com", f"shared-{i}")
|
||||
repo.remove_shared_bulk("alice@example.com", ["shared-0", "shared-2"])
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["shared_with_me"] == ["shared-1"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestRemoveAgentFromAll:
|
||||
def test_removes_from_both_pinned_and_shared(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-X")
|
||||
repo.add_pinned("alice@example.com", "agent-keep")
|
||||
repo.add_shared("alice@example.com", "agent-X")
|
||||
repo.add_shared("alice@example.com", "agent-keep-2")
|
||||
|
||||
repo.remove_agent_from_all("alice@example.com", "agent-X")
|
||||
|
||||
doc = repo.get("alice@example.com")
|
||||
assert doc["agent_preferences"]["pinned"] == ["agent-keep"]
|
||||
assert doc["agent_preferences"]["shared_with_me"] == ["agent-keep-2"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestTenantIsolation:
|
||||
"""Security-critical: operations on one user must never touch another's data."""
|
||||
|
||||
def test_add_pinned_does_not_leak_across_users(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.upsert("bob@example.com")
|
||||
repo.add_pinned("alice@example.com", "agent-a")
|
||||
repo.add_pinned("bob@example.com", "agent-b")
|
||||
|
||||
alice = repo.get("alice@example.com")
|
||||
bob = repo.get("bob@example.com")
|
||||
assert alice["agent_preferences"]["pinned"] == ["agent-a"]
|
||||
assert bob["agent_preferences"]["pinned"] == ["agent-b"]
|
||||
|
||||
def test_remove_does_not_leak_across_users(self, repo):
|
||||
repo.upsert("alice@example.com")
|
||||
repo.upsert("bob@example.com")
|
||||
repo.add_pinned("alice@example.com", "shared-agent-id")
|
||||
repo.add_pinned("bob@example.com", "shared-agent-id")
|
||||
|
||||
repo.remove_pinned("alice@example.com", "shared-agent-id")
|
||||
|
||||
assert repo.get("alice@example.com")["agent_preferences"]["pinned"] == []
|
||||
assert repo.get("bob@example.com")["agent_preferences"]["pinned"] == [
|
||||
"shared-agent-id"
|
||||
]
|
||||
Reference in new issue
Block a user