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.0 kB

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