Initial curated RAGcore source from c16667f0c9397e2bfc8dd0255b114fba23c8d05e
This commit is contained in:
commit
7ca726caf2
659 files changed
+98171
No files matched your search
@@ -0,0 +1,316 @@
|
||||
"""PostgreSQL integration coverage for the control-plane persistence boundary."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from alembic import command
|
||||
from alembic.config import Config
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from ragcore.application.authorization import AuthorizationService
|
||||
from ragcore.application.control.api import ControlPlaneApplication
|
||||
from ragcore.config import Settings
|
||||
from ragcore.domain.authorization import Principal, PrincipalType
|
||||
from ragcore.domain.control import DataPolicy, Grant, KnowledgeSpace, Policy, Role, Scope, Workspace
|
||||
from ragcore.infrastructure.database import (
|
||||
create_database_engine,
|
||||
create_session_factory,
|
||||
session_scope,
|
||||
)
|
||||
from ragcore.infrastructure.database.control import ControlPlaneRepository
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop_policy() -> asyncio.AbstractEventLoopPolicy:
|
||||
if sys.platform == "win32":
|
||||
return asyncio.WindowsSelectorEventLoopPolicy()
|
||||
return asyncio.get_event_loop_policy()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def repository() -> ControlPlaneRepository:
|
||||
engine = create_database_engine(Settings(env="test"))
|
||||
factory = create_session_factory(engine)
|
||||
async with session_scope(factory) as session:
|
||||
# Deliberately does not truncate control.workspaces: audit.audit_events
|
||||
# has a foreign key to workspace_id, and TRUNCATE ... CASCADE follows
|
||||
# that FK regardless of its ON DELETE action, tripping audit_events'
|
||||
# own unconditional append-only trigger (BEFORE TRUNCATE, migration
|
||||
# 0005) and erroring out this whole fixture. Every test using this
|
||||
# fixture already creates its own uniquely-id'd (uuid4()) workspace
|
||||
# and only reads back rows scoped to that id, so leaving prior test
|
||||
# runs' workspace rows in place is harmless and does not affect
|
||||
# correctness.
|
||||
await session.execute(
|
||||
text(
|
||||
"TRUNCATE control.grants, control.policies, control.applications, "
|
||||
"control.spaces CASCADE"
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
yield ControlPlaneRepository(session)
|
||||
await session.rollback()
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_workspace_slug_and_space_slug_are_unique(repository: ControlPlaneRepository) -> None:
|
||||
first = Workspace(uuid4(), "First", "shared")
|
||||
second = Workspace(uuid4(), "Second", "shared")
|
||||
await repository.create_workspace(first)
|
||||
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_workspace(second)
|
||||
await repository._session.rollback()
|
||||
|
||||
workspace = Workspace(uuid4(), "Spaces", "spaces")
|
||||
await repository.create_workspace(workspace)
|
||||
await repository.create_space(
|
||||
workspace.create_knowledge_space(id=uuid4(), name="One", slug="docs")
|
||||
)
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_space(
|
||||
workspace.create_knowledge_space(id=uuid4(), name="Two", slug="docs")
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_repository_reads_are_workspace_scoped(repository: ControlPlaneRepository) -> None:
|
||||
first = Workspace(uuid4(), "First", "first")
|
||||
second = Workspace(uuid4(), "Second", "second")
|
||||
await repository.create_workspace(first)
|
||||
await repository.create_workspace(second)
|
||||
await repository.create_application(
|
||||
first.create_application(id=uuid4(), name="Client", scopes={Scope.SEARCH})
|
||||
)
|
||||
await repository.create_application(
|
||||
second.create_application(id=uuid4(), name="Client", scopes={Scope.SEARCH})
|
||||
)
|
||||
|
||||
assert len(await repository.list_applications(first.id)) == 1
|
||||
assert len(await repository.list_applications(second.id)) == 1
|
||||
assert (await repository.list_applications(first.id))[0].workspace_id == first.id
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_composite_foreign_keys_reject_cross_workspace_grants(
|
||||
repository: ControlPlaneRepository,
|
||||
) -> None:
|
||||
first = Workspace(uuid4(), "First", "first")
|
||||
second = Workspace(uuid4(), "Second", "second")
|
||||
await repository.create_workspace(first)
|
||||
await repository.create_workspace(second)
|
||||
application = first.create_application(id=uuid4(), name="Client", scopes={Scope.SEARCH})
|
||||
space = second.create_knowledge_space(id=uuid4(), name="Private", slug="private")
|
||||
await repository.create_application(application)
|
||||
await repository.create_space(space)
|
||||
|
||||
cross_workspace_grant = Grant(
|
||||
id=uuid4(),
|
||||
workspace_id=second.id,
|
||||
application_id=application.id,
|
||||
knowledge_space_id=space.id,
|
||||
role=Role.VIEWER,
|
||||
)
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_grant(cross_workspace_grant)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_application_and_workspace_policy_uniqueness(
|
||||
repository: ControlPlaneRepository,
|
||||
) -> None:
|
||||
workspace = Workspace(uuid4(), "Control", "control")
|
||||
await repository.create_workspace(workspace)
|
||||
await repository.create_application(
|
||||
workspace.create_application(id=uuid4(), name="One", slug="client", scopes={Scope.SEARCH})
|
||||
)
|
||||
await repository._session.commit()
|
||||
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_application(
|
||||
workspace.create_application(
|
||||
id=uuid4(), name="Two", slug="client", scopes={Scope.SEARCH}
|
||||
)
|
||||
)
|
||||
await repository._session.rollback()
|
||||
|
||||
await repository.create_policy(workspace.create_policy(id=uuid4(), operation="search"))
|
||||
await repository._session.commit()
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_policy(workspace.create_policy(id=uuid4(), operation="search"))
|
||||
await repository._session.rollback()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_space_and_policy_reads_are_workspace_scoped(
|
||||
repository: ControlPlaneRepository,
|
||||
) -> None:
|
||||
first = Workspace(uuid4(), "First", "first")
|
||||
second = Workspace(uuid4(), "Second", "second")
|
||||
await repository.create_workspace(first)
|
||||
await repository.create_workspace(second)
|
||||
await repository.create_space(
|
||||
first.create_knowledge_space(id=uuid4(), name="First space", slug="first-space")
|
||||
)
|
||||
await repository.create_space(
|
||||
second.create_knowledge_space(id=uuid4(), name="Second space", slug="second-space")
|
||||
)
|
||||
await repository.create_policy(first.create_policy(id=uuid4(), operation="search"))
|
||||
await repository.create_policy(second.create_policy(id=uuid4(), operation="search"))
|
||||
|
||||
assert len(await repository.list_spaces(first.id)) == 1
|
||||
assert len(await repository.list_spaces(second.id)) == 1
|
||||
assert len(await repository.list_policies(first.id)) == 1
|
||||
assert len(await repository.list_policies(second.id)) == 1
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_composite_foreign_keys_reject_cross_workspace_spaces_and_policies(
|
||||
repository: ControlPlaneRepository,
|
||||
) -> None:
|
||||
first = Workspace(uuid4(), "First", "first")
|
||||
second = Workspace(uuid4(), "Second", "second")
|
||||
await repository.create_workspace(first)
|
||||
await repository.create_workspace(second)
|
||||
await repository._session.commit()
|
||||
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_space(
|
||||
KnowledgeSpace(id=uuid4(), workspace_id=uuid4(), name="Orphan", slug="orphan")
|
||||
)
|
||||
await repository._session.rollback()
|
||||
|
||||
second_space = second.create_knowledge_space(id=uuid4(), name="Private", slug="private")
|
||||
await repository.create_space(second_space)
|
||||
with pytest.raises(IntegrityError):
|
||||
await repository.create_policy(
|
||||
Policy(
|
||||
id=uuid4(),
|
||||
workspace_id=first.id,
|
||||
knowledge_space_id=second_space.id,
|
||||
operation="search",
|
||||
)
|
||||
)
|
||||
await repository._session.rollback()
|
||||
|
||||
|
||||
def test_empty_database_migration_round_trip() -> None:
|
||||
config = Config(str(REPOSITORY_ROOT / "alembic.ini"))
|
||||
command.downgrade(config, "base")
|
||||
command.upgrade(config, "head")
|
||||
|
||||
async def check_schema() -> None:
|
||||
engine = create_database_engine(Settings(env="test"))
|
||||
async with engine.connect() as connection:
|
||||
result = await connection.execute(
|
||||
text(
|
||||
"SELECT table_name FROM information_schema.tables "
|
||||
"WHERE table_schema = 'control'"
|
||||
)
|
||||
)
|
||||
assert {row[0] for row in result} == {
|
||||
"workspaces",
|
||||
"applications",
|
||||
"application_default_spaces",
|
||||
"spaces",
|
||||
"grants",
|
||||
"policies",
|
||||
"service_accounts",
|
||||
"user_grants",
|
||||
}
|
||||
await engine.dispose()
|
||||
|
||||
asyncio.run(check_schema())
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.parametrize("data_policy", list(DataPolicy))
|
||||
async def test_space_data_policy_round_trips_through_repository(
|
||||
repository: ControlPlaneRepository, data_policy: DataPolicy
|
||||
) -> None:
|
||||
workspace = Workspace(uuid4(), "Policies", "policies")
|
||||
await repository.create_workspace(workspace)
|
||||
space = workspace.create_knowledge_space(
|
||||
id=uuid4(), name="Policy space", slug="policy-space", data_policy=data_policy
|
||||
)
|
||||
|
||||
await repository.create_space(space)
|
||||
|
||||
stored = (await repository.list_spaces(workspace.id))[0]
|
||||
assert stored.data_policy is data_policy
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_default_knowledge_space_ids_round_trip_through_a_new_session(
|
||||
repository: ControlPlaneRepository,
|
||||
) -> None:
|
||||
workspace = Workspace(uuid4(), "Defaults", "defaults")
|
||||
await repository.create_workspace(workspace)
|
||||
space_one = workspace.create_knowledge_space(id=uuid4(), name="Space One", slug="space-one")
|
||||
space_two = workspace.create_knowledge_space(id=uuid4(), name="Space Two", slug="space-two")
|
||||
await repository.create_space(space_one)
|
||||
await repository.create_space(space_two)
|
||||
|
||||
application = workspace.create_application(
|
||||
id=uuid4(),
|
||||
name="Defaults Client",
|
||||
slug="defaults-client",
|
||||
default_knowledge_space_ids=frozenset({space_one.id, space_two.id}),
|
||||
)
|
||||
created = await repository.create_application(application)
|
||||
assert created.default_knowledge_space_ids == frozenset({space_one.id, space_two.id})
|
||||
# This repository is Core (not ORM): every select() below is a real
|
||||
# round trip with no identity-map caching, so re-reading through the
|
||||
# same session already proves the value is genuinely in the database,
|
||||
# not merely held on the Python-side entity that created() returned.
|
||||
# The shared fixture session never commits mid-test on its own (see its
|
||||
# own docstring) -- commit explicitly so this row would also survive
|
||||
# into a genuinely separate connection, not just this transaction.
|
||||
await repository._session.commit()
|
||||
|
||||
reloaded = await repository.get_application(application.id, workspace.id)
|
||||
assert reloaded is not None
|
||||
assert reloaded.default_knowledge_space_ids == frozenset({space_one.id, space_two.id})
|
||||
|
||||
listed = await repository.list_applications(workspace.id)
|
||||
(listed_application,) = [app for app in listed if app.id == application.id]
|
||||
assert listed_application.default_knowledge_space_ids == frozenset(
|
||||
{space_one.id, space_two.id}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_application_use_case_rejects_an_unauthorized_default_space(
|
||||
repository: ControlPlaneRepository,
|
||||
) -> None:
|
||||
workspace = Workspace(uuid4(), "Defaults Reject", "defaults-reject")
|
||||
await repository.create_workspace(workspace)
|
||||
allowed_space = workspace.create_knowledge_space(
|
||||
id=uuid4(), name="Allowed", slug="allowed-space"
|
||||
)
|
||||
await repository.create_space(allowed_space)
|
||||
foreign_space_id = uuid4() # belongs to no workspace at all
|
||||
|
||||
control_application = ControlPlaneApplication(repository, AuthorizationService())
|
||||
principal = Principal(
|
||||
uuid4(), workspace.id, PrincipalType.HUMAN, frozenset({Role.WORKSPACE_ADMIN})
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="default knowledge spaces must belong"):
|
||||
await control_application.create_application(
|
||||
principal,
|
||||
slug="rejected-client",
|
||||
display_name="Rejected Client",
|
||||
default_space_ids=frozenset({allowed_space.id, foreign_space_id}),
|
||||
)
|
||||
|
||||
assert await repository.list_applications(workspace.id) == ()
|
||||
Reference in new issue
Block a user