From 2b3075d6070a23cde92c0b8f0e6c751d054cb2a5 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:09:38 +0000 Subject: [PATCH 01/10] Add integration tests for evidence uniqueness --- tests/integration/test_evidence_uniqueness.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/integration/test_evidence_uniqueness.py diff --git a/tests/integration/test_evidence_uniqueness.py b/tests/integration/test_evidence_uniqueness.py new file mode 100644 index 00000000..0b269ad9 --- /dev/null +++ b/tests/integration/test_evidence_uniqueness.py @@ -0,0 +1,91 @@ +import pytest +from prometheus_swarm.database.models import Evidence +from prometheus_swarm.database.database import SessionLocal +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from typing import List + +def test_evidence_uniqueness(): + """ + Integration test to verify evidence uniqueness across the system. + + This test ensures that: + 1. Duplicate evidence cannot be inserted + 2. Each evidence entry has a unique identifier + 3. Constraints prevent duplicate evidence submissions + """ + # Create a test database session + engine = create_engine('sqlite:///:memory:') + TestingSessionLocal = sessionmaker(bind=engine) + + # Create tables + Evidence.__table__.create(bind=engine) + + def create_evidence(session, content: str, type: str) -> Evidence: + """Helper function to create evidence entries""" + evidence = Evidence(content=content, type=type) + session.add(evidence) + session.commit() + return evidence + + # Test 1: Basic uniqueness + with TestingSessionLocal() as session: + # First evidence submission should succeed + evidence1 = create_evidence(session, "Test Content 1", "test_type") + assert evidence1.id is not None + + # Attempt to insert identical evidence should fail + with pytest.raises(Exception) as excinfo: + create_evidence(session, "Test Content 1", "test_type") + + assert "unique constraint" in str(excinfo.value).lower() + +def test_evidence_multiple_types(): + """ + Test that different types of evidence can have the same content + """ + engine = create_engine('sqlite:///:memory:') + TestingSessionLocal = sessionmaker(bind=engine) + + Evidence.__table__.create(bind=engine) + + with TestingSessionLocal() as session: + # Create evidence with same content, different types + evidence1 = Evidence(content="Shared Content", type="type1") + evidence2 = Evidence(content="Shared Content", type="type2") + + session.add(evidence1) + session.add(evidence2) + session.commit() + + # Verify both evidences were saved + saved_evidences = session.query(Evidence).filter_by(content="Shared Content").all() + assert len(saved_evidences) == 2 + assert {e.type for e in saved_evidences} == {"type1", "type2"} + +def test_evidence_content_validation(): + """ + Test validation of evidence content + """ + engine = create_engine('sqlite:///:memory:') + TestingSessionLocal = sessionmaker(bind=engine) + + Evidence.__table__.create(bind=engine) + + with TestingSessionLocal() as session: + # Test empty content + with pytest.raises(Exception) as excinfo: + empty_evidence = Evidence(content="", type="test_type") + session.add(empty_evidence) + session.commit() + + assert "empty" in str(excinfo.value).lower() + + # Test very long content + long_content = "a" * 10001 # Assuming a max length constraint + with pytest.raises(Exception) as excinfo: + long_evidence = Evidence(content=long_content, type="test_type") + session.add(long_evidence) + session.commit() + + assert "length" in str(excinfo.value).lower() \ No newline at end of file From 876dfce9692ca28232f6e1833a05b4423e8a3599 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:10:14 +0000 Subject: [PATCH 02/10] Add Evidence model with uniqueness constraints --- .../prometheus_swarm/database/models.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/agent-framework/prometheus_swarm/database/models.py b/agent-framework/prometheus_swarm/database/models.py index 5a4785aa..a04ae3bd 100644 --- a/agent-framework/prometheus_swarm/database/models.py +++ b/agent-framework/prometheus_swarm/database/models.py @@ -2,8 +2,8 @@ from datetime import datetime from typing import Optional, List -from sqlmodel import SQLModel, Field, Relationship - +from sqlmodel import SQLModel, Field, Relationship, Column, UniqueConstraint +from sqlalchemy import CheckConstraint class Conversation(SQLModel, table=True): """Conversation model.""" @@ -42,3 +42,19 @@ class Log(SQLModel, table=True): stack_trace: Optional[str] = None request_id: Optional[str] = None additional_data: Optional[str] = None + + +class Evidence(SQLModel, table=True): + """Evidence model to ensure uniqueness.""" + + __tablename__ = 'evidence' + + id: Optional[int] = Field(default=None, primary_key=True) + content: str = Field(min_length=1, max_length=10000) + type: str = Field(index=True) + created_at: datetime = Field(default_factory=datetime.utcnow) + + __table_args__ = ( + UniqueConstraint('content', 'type', name='uix_evidence_content_type'), + CheckConstraint('length(content) > 0', name='check_content_not_empty') + ) \ No newline at end of file From d0288021548bdcf7e34479ce24e18c21c5104a29 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:10:30 +0000 Subject: [PATCH 03/10] Update test_evidence_uniqueness tests for new Evidence model --- tests/integration/test_evidence_uniqueness.py | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/tests/integration/test_evidence_uniqueness.py b/tests/integration/test_evidence_uniqueness.py index 0b269ad9..226d5aaf 100644 --- a/tests/integration/test_evidence_uniqueness.py +++ b/tests/integration/test_evidence_uniqueness.py @@ -1,35 +1,33 @@ import pytest +from sqlmodel import SQLModel, create_engine, Session from prometheus_swarm.database.models import Evidence -from prometheus_swarm.database.database import SessionLocal -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from typing import List + +def create_test_engine(): + """Create an in-memory SQLite engine for testing.""" + # Using SQLite in-memory database for testing + return create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False}) def test_evidence_uniqueness(): """ Integration test to verify evidence uniqueness across the system. This test ensures that: - 1. Duplicate evidence cannot be inserted + 1. Duplicate evidence with same type cannot be inserted 2. Each evidence entry has a unique identifier 3. Constraints prevent duplicate evidence submissions """ - # Create a test database session - engine = create_engine('sqlite:///:memory:') - TestingSessionLocal = sessionmaker(bind=engine) - - # Create tables - Evidence.__table__.create(bind=engine) + # Create test engine and tables + engine = create_test_engine() + SQLModel.metadata.create_all(engine) - def create_evidence(session, content: str, type: str) -> Evidence: + def create_evidence(session: Session, content: str, type: str) -> Evidence: """Helper function to create evidence entries""" evidence = Evidence(content=content, type=type) session.add(evidence) session.commit() return evidence - # Test 1: Basic uniqueness - with TestingSessionLocal() as session: + with Session(engine) as session: # First evidence submission should succeed evidence1 = create_evidence(session, "Test Content 1", "test_type") assert evidence1.id is not None @@ -38,18 +36,16 @@ def create_evidence(session, content: str, type: str) -> Evidence: with pytest.raises(Exception) as excinfo: create_evidence(session, "Test Content 1", "test_type") - assert "unique constraint" in str(excinfo.value).lower() + assert "UNIQUE constraint" in str(excinfo.value) def test_evidence_multiple_types(): """ Test that different types of evidence can have the same content """ - engine = create_engine('sqlite:///:memory:') - TestingSessionLocal = sessionmaker(bind=engine) + engine = create_test_engine() + SQLModel.metadata.create_all(engine) - Evidence.__table__.create(bind=engine) - - with TestingSessionLocal() as session: + with Session(engine) as session: # Create evidence with same content, different types evidence1 = Evidence(content="Shared Content", type="type1") evidence2 = Evidence(content="Shared Content", type="type2") @@ -67,22 +63,20 @@ def test_evidence_content_validation(): """ Test validation of evidence content """ - engine = create_engine('sqlite:///:memory:') - TestingSessionLocal = sessionmaker(bind=engine) - - Evidence.__table__.create(bind=engine) + engine = create_test_engine() + SQLModel.metadata.create_all(engine) - with TestingSessionLocal() as session: + with Session(engine) as session: # Test empty content with pytest.raises(Exception) as excinfo: empty_evidence = Evidence(content="", type="test_type") session.add(empty_evidence) session.commit() - assert "empty" in str(excinfo.value).lower() + assert "content" in str(excinfo.value).lower() # Test very long content - long_content = "a" * 10001 # Assuming a max length constraint + long_content = "a" * 10001 # Exceeding max length with pytest.raises(Exception) as excinfo: long_evidence = Evidence(content=long_content, type="test_type") session.add(long_evidence) From 0fc6d1f30cc79b6e2925cbe97b540761b1ab310f Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:12:00 +0000 Subject: [PATCH 04/10] Improve evidence uniqueness test validation --- .../prometheus_swarm/tests/__init__.py | 1 + .../tests/integration/__init__.py | 1 + .../integration/test_evidence_uniqueness.py | 21 ++++++++----------- 3 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 agent-framework/prometheus_swarm/tests/__init__.py create mode 100644 agent-framework/prometheus_swarm/tests/integration/__init__.py rename {tests => agent-framework/prometheus_swarm/tests}/integration/test_evidence_uniqueness.py (79%) diff --git a/agent-framework/prometheus_swarm/tests/__init__.py b/agent-framework/prometheus_swarm/tests/__init__.py new file mode 100644 index 00000000..739954cb --- /dev/null +++ b/agent-framework/prometheus_swarm/tests/__init__.py @@ -0,0 +1 @@ +# Tests package \ No newline at end of file diff --git a/agent-framework/prometheus_swarm/tests/integration/__init__.py b/agent-framework/prometheus_swarm/tests/integration/__init__.py new file mode 100644 index 00000000..e27cd7ab --- /dev/null +++ b/agent-framework/prometheus_swarm/tests/integration/__init__.py @@ -0,0 +1 @@ +# Integration tests package \ No newline at end of file diff --git a/tests/integration/test_evidence_uniqueness.py b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py similarity index 79% rename from tests/integration/test_evidence_uniqueness.py rename to agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py index 226d5aaf..a5e13f8b 100644 --- a/tests/integration/test_evidence_uniqueness.py +++ b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py @@ -1,6 +1,7 @@ import pytest -from sqlmodel import SQLModel, create_engine, Session +from sqlmodel import SQLModel, create_engine, Session, select from prometheus_swarm.database.models import Evidence +from pydantic import ValidationError def create_test_engine(): """Create an in-memory SQLite engine for testing.""" @@ -55,7 +56,7 @@ def test_evidence_multiple_types(): session.commit() # Verify both evidences were saved - saved_evidences = session.query(Evidence).filter_by(content="Shared Content").all() + saved_evidences = session.exec(select(Evidence).where(Evidence.content == "Shared Content")).all() assert len(saved_evidences) == 2 assert {e.type for e in saved_evidences} == {"type1", "type2"} @@ -68,18 +69,14 @@ def test_evidence_content_validation(): with Session(engine) as session: # Test empty content - with pytest.raises(Exception) as excinfo: - empty_evidence = Evidence(content="", type="test_type") - session.add(empty_evidence) - session.commit() + with pytest.raises(ValidationError) as excinfo: + Evidence(content="", type="test_type") - assert "content" in str(excinfo.value).lower() + assert "min_length" in str(excinfo.value) # Test very long content long_content = "a" * 10001 # Exceeding max length - with pytest.raises(Exception) as excinfo: - long_evidence = Evidence(content=long_content, type="test_type") - session.add(long_evidence) - session.commit() + with pytest.raises(ValidationError) as excinfo: + Evidence(content=long_content, type="test_type") - assert "length" in str(excinfo.value).lower() \ No newline at end of file + assert "max_length" in str(excinfo.value) \ No newline at end of file From 7b605170a1cebf2a73e7759d156300ad883ff200 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:12:41 +0000 Subject: [PATCH 05/10] Update Evidence model with Pydantic validation --- .../prometheus_swarm/database/models.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/agent-framework/prometheus_swarm/database/models.py b/agent-framework/prometheus_swarm/database/models.py index a04ae3bd..1dbc86e6 100644 --- a/agent-framework/prometheus_swarm/database/models.py +++ b/agent-framework/prometheus_swarm/database/models.py @@ -4,6 +4,7 @@ from typing import Optional, List from sqlmodel import SQLModel, Field, Relationship, Column, UniqueConstraint from sqlalchemy import CheckConstraint +from pydantic import validator, constr class Conversation(SQLModel, table=True): """Conversation model.""" @@ -50,11 +51,18 @@ class Evidence(SQLModel, table=True): __tablename__ = 'evidence' id: Optional[int] = Field(default=None, primary_key=True) - content: str = Field(min_length=1, max_length=10000) + content: constr(strip_whitespace=True, min_length=1, max_length=10000) = Field() type: str = Field(index=True) created_at: datetime = Field(default_factory=datetime.utcnow) __table_args__ = ( UniqueConstraint('content', 'type', name='uix_evidence_content_type'), - CheckConstraint('length(content) > 0', name='check_content_not_empty') - ) \ No newline at end of file + ) + + @validator('content') + def validate_non_empty(cls, v): + """Validate content is not just whitespace""" + v = v.strip() + if not v: + raise ValueError("Content cannot be empty or just whitespace") + return v \ No newline at end of file From 00ae9a7c49bbccde5f0ac0078cc21c4450a87e48 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:12:59 +0000 Subject: [PATCH 06/10] Update evidence uniqueness tests for Pydantic validation --- .../integration/test_evidence_uniqueness.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py index a5e13f8b..94727865 100644 --- a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py +++ b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py @@ -64,19 +64,19 @@ def test_evidence_content_validation(): """ Test validation of evidence content """ - engine = create_test_engine() - SQLModel.metadata.create_all(engine) + # Test empty content + with pytest.raises(ValidationError) as excinfo: + Evidence(content="", type="test_type") - with Session(engine) as session: - # Test empty content - with pytest.raises(ValidationError) as excinfo: - Evidence(content="", type="test_type") - - assert "min_length" in str(excinfo.value) - - # Test very long content - long_content = "a" * 10001 # Exceeding max length - with pytest.raises(ValidationError) as excinfo: - Evidence(content=long_content, type="test_type") - - assert "max_length" in str(excinfo.value) \ No newline at end of file + # Whitespace-only content + with pytest.raises(ValidationError) as excinfo: + Evidence(content=" ", type="test_type") + + # Test very long content + long_content = "a" * 10001 # Exceeding max length + with pytest.raises(ValidationError) as excinfo: + Evidence(content=long_content, type="test_type") + + # Validate content cannot be completely empty + assert "cannot be empty" in str(excinfo.value) + assert "max_length" in str(excinfo.value) \ No newline at end of file From f0680066d0054e469f6d29aa110ff7f074c8c848 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:13:39 +0000 Subject: [PATCH 07/10] Upgrade Evidence model to Pydantic v2 validation --- .../prometheus_swarm/database/models.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/agent-framework/prometheus_swarm/database/models.py b/agent-framework/prometheus_swarm/database/models.py index 1dbc86e6..58d30260 100644 --- a/agent-framework/prometheus_swarm/database/models.py +++ b/agent-framework/prometheus_swarm/database/models.py @@ -4,7 +4,7 @@ from typing import Optional, List from sqlmodel import SQLModel, Field, Relationship, Column, UniqueConstraint from sqlalchemy import CheckConstraint -from pydantic import validator, constr +from pydantic import field_validator, constr, ConfigDict class Conversation(SQLModel, table=True): """Conversation model.""" @@ -50,8 +50,13 @@ class Evidence(SQLModel, table=True): __tablename__ = 'evidence' + model_config = ConfigDict( + validate_assignment=True, + from_attributes=True + ) + id: Optional[int] = Field(default=None, primary_key=True) - content: constr(strip_whitespace=True, min_length=1, max_length=10000) = Field() + content: str = Field(min_length=1, max_length=10000) type: str = Field(index=True) created_at: datetime = Field(default_factory=datetime.utcnow) @@ -59,9 +64,12 @@ class Evidence(SQLModel, table=True): UniqueConstraint('content', 'type', name='uix_evidence_content_type'), ) - @validator('content') + @field_validator('content') + @classmethod def validate_non_empty(cls, v): """Validate content is not just whitespace""" + if not isinstance(v, str): + raise ValueError("Content must be a string") v = v.strip() if not v: raise ValueError("Content cannot be empty or just whitespace") From 11b9f606c4136374509a814aae4f678ba660737d Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:13:57 +0000 Subject: [PATCH 08/10] Update evidence uniqueness tests with Pydantic v2 validation --- .../tests/integration/test_evidence_uniqueness.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py index 94727865..d8b8639c 100644 --- a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py +++ b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py @@ -67,16 +67,17 @@ def test_evidence_content_validation(): # Test empty content with pytest.raises(ValidationError) as excinfo: Evidence(content="", type="test_type") + assert "Content cannot be empty or just whitespace" in str(excinfo.value) # Whitespace-only content with pytest.raises(ValidationError) as excinfo: Evidence(content=" ", type="test_type") + assert "Content cannot be empty or just whitespace" in str(excinfo.value) # Test very long content long_content = "a" * 10001 # Exceeding max length with pytest.raises(ValidationError) as excinfo: Evidence(content=long_content, type="test_type") - # Validate content cannot be completely empty - assert "cannot be empty" in str(excinfo.value) - assert "max_length" in str(excinfo.value) \ No newline at end of file + # Validate content cannot be completely empty or too long + assert "String must have at most" in str(excinfo.value) \ No newline at end of file From 525130aeda5e84a61547516948817e15afd76275 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:14:24 +0000 Subject: [PATCH 09/10] Update evidence content validation test assertions --- .../tests/integration/test_evidence_uniqueness.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py index d8b8639c..65eb3a1b 100644 --- a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py +++ b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py @@ -67,12 +67,12 @@ def test_evidence_content_validation(): # Test empty content with pytest.raises(ValidationError) as excinfo: Evidence(content="", type="test_type") - assert "Content cannot be empty or just whitespace" in str(excinfo.value) + assert "String should have at least 1 character" in str(excinfo.value) # Whitespace-only content with pytest.raises(ValidationError) as excinfo: Evidence(content=" ", type="test_type") - assert "Content cannot be empty or just whitespace" in str(excinfo.value) + assert "String should have at least 1 character" in str(excinfo.value) # Test very long content long_content = "a" * 10001 # Exceeding max length @@ -80,4 +80,4 @@ def test_evidence_content_validation(): Evidence(content=long_content, type="test_type") # Validate content cannot be completely empty or too long - assert "String must have at most" in str(excinfo.value) \ No newline at end of file + assert "String should have at most" in str(excinfo.value) \ No newline at end of file From 928f084bef056f802af4283b26c5192db6bdf561 Mon Sep 17 00:00:00 2001 From: Plentiful106 Date: Tue, 6 May 2025 22:15:10 +0000 Subject: [PATCH 10/10] Update evidence content validation test assertions --- .../tests/integration/test_evidence_uniqueness.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py index 65eb3a1b..2e22c1f0 100644 --- a/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py +++ b/agent-framework/prometheus_swarm/tests/integration/test_evidence_uniqueness.py @@ -72,7 +72,7 @@ def test_evidence_content_validation(): # Whitespace-only content with pytest.raises(ValidationError) as excinfo: Evidence(content=" ", type="test_type") - assert "String should have at least 1 character" in str(excinfo.value) + assert "Content cannot be empty or just whitespace" in str(excinfo.value) # Test very long content long_content = "a" * 10001 # Exceeding max length