You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_db_manager.py 7.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import asyncio
  2. import pytest
  3. from sqlmodel import Session, text, select
  4. from typing import Generator
  5. from autogenstudio.database import DatabaseManager
  6. from autogen_agentchat.agents import AssistantAgent
  7. from autogen_agentchat.teams import RoundRobinGroupChat
  8. from autogen_ext.models.openai import OpenAIChatCompletionClient
  9. from autogen_agentchat.conditions import TextMentionTermination
  10. from autogenstudio.datamodel.db import Team, Session as SessionModel, Run, Message, RunStatus, MessageConfig
  11. @pytest.fixture
  12. def test_db(tmp_path) -> Generator[DatabaseManager, None, None]:
  13. """Fixture for test database using temporary paths"""
  14. db_path = tmp_path / "test.db"
  15. db = DatabaseManager(f"sqlite:///{db_path}", base_dir=tmp_path)
  16. db.reset_db()
  17. # Initialize database instead of create_db_and_tables
  18. db.initialize_database(auto_upgrade=False)
  19. yield db
  20. # Clean up
  21. asyncio.run(db.close())
  22. db.reset_db()
  23. # No need to manually remove files - tmp_path is cleaned up automatically
  24. @pytest.fixture
  25. def test_user() -> str:
  26. return "test_user@example.com"
  27. @pytest.fixture
  28. def sample_team(test_user: str) -> Team:
  29. """Create a sample team with proper config"""
  30. agent = AssistantAgent(
  31. name="weather_agent",
  32. model_client=OpenAIChatCompletionClient(
  33. model="gpt-4.1-nano",
  34. ),
  35. )
  36. agent_team = RoundRobinGroupChat([agent], termination_condition=TextMentionTermination("TERMINATE"))
  37. team_component = agent_team.dump_component()
  38. return Team(
  39. user_id=test_user,
  40. component=team_component.model_dump(),
  41. )
  42. class TestDatabaseOperations:
  43. def test_basic_setup(self, test_db: DatabaseManager):
  44. """Test basic database setup and connection"""
  45. with Session(test_db.engine) as session:
  46. result = session.exec(text("SELECT 1")).first() # type: ignore
  47. assert result[0] == 1
  48. result = session.exec(select(1)).first()
  49. assert result == 1
  50. def test_basic_entity_creation(self, test_db: DatabaseManager, sample_team: Team):
  51. """Test creating all entity types with proper configs"""
  52. # Use upsert instead of raw session
  53. response = test_db.upsert(sample_team)
  54. assert response.status is True
  55. with Session(test_db.engine) as session:
  56. saved_team = session.get(Team, sample_team.id)
  57. assert saved_team is not None
  58. def test_upsert_operations(self, test_db: DatabaseManager, sample_team: Team):
  59. """Test upsert for both create and update scenarios"""
  60. # Test Create
  61. response = test_db.upsert(sample_team)
  62. assert response.status is True
  63. assert "Created Successfully" in response.message
  64. # Test Update
  65. team_id = sample_team.id
  66. sample_team.version = "0.0.2"
  67. response = test_db.upsert(sample_team)
  68. assert response.status is True
  69. # Verify Update
  70. result = test_db.get(Team, {"id": team_id})
  71. assert result.status is True
  72. assert result.data and result.data[0].version == "0.0.2"
  73. def test_delete_operations(self, test_db: DatabaseManager, sample_team: Team):
  74. """Test delete with various filters"""
  75. # First insert the model
  76. response = test_db.upsert(sample_team)
  77. assert response.status is True # Verify insert worked
  78. # Get the ID that was actually saved
  79. team_id = sample_team.id
  80. # Test deletion by id
  81. response = test_db.delete(Team, {"id": team_id})
  82. assert response.status is True
  83. assert "Deleted Successfully" in response.message
  84. # Verify deletion
  85. result = test_db.get(Team, {"id": team_id})
  86. if result.data:
  87. assert len(result.data) == 0
  88. def test_cascade_delete(self, test_db: DatabaseManager, test_user: str):
  89. """Test all levels of cascade delete"""
  90. # Enable foreign keys for SQLite (crucial for cascade delete)
  91. with Session(test_db.engine) as session:
  92. session.execute(text("PRAGMA foreign_keys=ON"))
  93. session.commit()
  94. # Test Run -> Message cascade
  95. team1 = Team(user_id=test_user, component={"name": "Team1", "type": "team"})
  96. test_db.upsert(team1)
  97. session1 = SessionModel(user_id=test_user, team_id=team1.id, name="Session1")
  98. test_db.upsert(session1)
  99. run1_id = 1
  100. test_db.upsert(Run(
  101. id=run1_id,
  102. user_id=test_user,
  103. session_id=session1.id,
  104. status=RunStatus.COMPLETE,
  105. task=MessageConfig(content="Task1", source="user").model_dump()
  106. ))
  107. test_db.upsert(Message(
  108. user_id=test_user,
  109. session_id=session1.id,
  110. run_id=run1_id,
  111. config=MessageConfig(content="Message1", source="assistant").model_dump()
  112. ))
  113. test_db.delete(Run, {"id": run1_id})
  114. db_message = test_db.get(Message, {"run_id": run1_id})
  115. if db_message.data:
  116. assert len(db_message.data) == 0, "Run->Message cascade failed"
  117. # Test Session -> Run -> Message cascade
  118. session2 = SessionModel(user_id=test_user, team_id=team1.id, name="Session2")
  119. test_db.upsert(session2)
  120. run2_id = 2
  121. test_db.upsert(Run(
  122. id=run2_id,
  123. user_id=test_user,
  124. session_id=session2.id,
  125. status=RunStatus.COMPLETE,
  126. task=MessageConfig(content="Task2", source="user").model_dump()
  127. ))
  128. test_db.upsert(Message(
  129. user_id=test_user,
  130. session_id=session2.id,
  131. run_id=run2_id,
  132. config=MessageConfig(content="Message2", source="assistant").model_dump()
  133. ))
  134. test_db.delete(SessionModel, {"id": session2.id})
  135. session = test_db.get(SessionModel, {"id": session2.id})
  136. run = test_db.get(Run, {"id": run2_id})
  137. if session.data:
  138. assert len(session.data) == 0, "Session->Run cascade failed"
  139. if run.data:
  140. assert len(run.data) == 0, "Session->Run->Message cascade failed"
  141. # Clean up
  142. test_db.delete(Team, {"id": team1.id})
  143. def test_initialize_database_scenarios(self, tmp_path, monkeypatch):
  144. """Test different initialize_database parameters"""
  145. db_path = tmp_path / "test_init.db"
  146. db = DatabaseManager(f"sqlite:///{db_path}", base_dir=tmp_path)
  147. # Mock the schema manager's check_schema_status to avoid migration issues
  148. monkeypatch.setattr(db.schema_manager, "check_schema_status", lambda: (False, None))
  149. monkeypatch.setattr(db.schema_manager, "ensure_schema_up_to_date", lambda: True)
  150. try:
  151. # Test basic initialization
  152. response = db.initialize_database()
  153. assert response.status is True
  154. # Test with auto_upgrade
  155. response = db.initialize_database(auto_upgrade=True)
  156. assert response.status is True
  157. finally:
  158. asyncio.run(db.close())
  159. db.reset_db()