| """ |
| Concurrent Agent Operations Tests |
| |
| Tests for race conditions and isolation in concurrent agent operations. |
| |
| These tests validate that: |
| 1. Multiple agents can execute independently without interference |
| 2. Governance checks are thread-safe and don't block each other |
| 3. Canvas creation operations are isolated |
| 4. Episode segmentation handles concurrent operations |
| 5. Budget enforcement prevents concurrent overspend |
| 6. Cache updates are consistent under load |
| 7. Database transactions maintain isolation |
| 8. LLM requests don't block each other |
| |
| Key Bugs Tested: |
| - Race conditions in agent execution state |
| - Lost updates due to lack of locking |
| - Cache corruption under concurrent access |
| - Budget enforcement bypass via concurrent requests |
| - Transaction isolation violations |
| - Deadlocks in governance checks |
| """ |
|
|
| import pytest |
| import threading |
| import time |
| import uuid |
| from concurrent.futures import ThreadPoolExecutor, as_completed |
| from typing import List, Dict, Any |
| from datetime import datetime, timedelta |
|
|
| from sqlalchemy.orm import Session |
|
|
| from core.agent_governance_service import AgentGovernanceService |
| from core.governance_cache import GovernanceCache |
| from core.models import AgentRegistry, AgentExecution, CanvasAudit, AgentEpisode |
|
|
|
|
| class TestConcurrentAgentExecutionIsolation: |
| """Test that concurrent agent executions are isolated.""" |
|
|
| def test_concurrent_agent_execution_isolation(self, db_session: Session): |
| """ |
| CONCURRENT: Multiple agents execute independently. |
| |
| Tests that agent execution contexts don't interfere. |
| Each agent should have independent execution state. |
| |
| BUG_PATTERN: Shared state causing cross-agent interference. |
| EXPECTED: All agents execute independently with correct results. |
| """ |
| |
| agents = [] |
| for i in range(5): |
| agent = AgentRegistry( |
| id=f"concurrent-agent-{i}", |
| name=f"Concurrent Agent {i}", |
| description="Test concurrent execution", |
| category="Testing", |
| module_path="test.concurrent", |
| class_name=f"ConcurrentAgent{i}", |
| status="AUTONOMOUS", |
| confidence_score=0.95 |
| ) |
| db_session.add(agent) |
| agents.append(agent) |
| db_session.commit() |
|
|
| |
| results = [] |
| errors = [] |
|
|
| def execute_agent(agent_id: str, index: int): |
| try: |
| governance = AgentGovernanceService(db_session) |
| |
| can_execute = governance.can_perform_action( |
| agent_id, |
| "execute" |
| ) |
| |
| execution = AgentExecution( |
| agent_id=agent_id, |
| user_id=f"test-user-{index}", |
| task=f"Concurrent task {index}", |
| status="in_progress", |
| started_at=datetime.utcnow() |
| ) |
| db_session.add(execution) |
| db_session.commit() |
| results.append((agent_id, can_execute)) |
| except Exception as e: |
| errors.append(e) |
|
|
| |
| threads = [] |
| for i, agent in enumerate(agents): |
| thread = threading.Thread(target=execute_agent, args=(agent.id, i)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Concurrent execution errors: {errors}" |
|
|
| |
| assert len(results) == 5, f"Expected 5 results, got {len(results)}" |
|
|
| |
| executions = db_session.query(AgentExecution).filter( |
| AgentExecution.task.like("Concurrent task%") |
| ).all() |
| assert len(executions) == 5, f"Expected 5 executions, got {len(executions)}" |
|
|
| def test_concurrent_agent_execution_with_different_users(self, db_session: Session): |
| """ |
| CONCURRENT: Same agent executed by different users concurrently. |
| |
| Tests that agent execution is isolated per user. |
| Multiple users should be able to execute same agent simultaneously. |
| |
| BUG_PATTERN: User context collision in concurrent execution. |
| EXPECTED: Each user gets independent execution context. |
| """ |
| |
| agent = AgentRegistry( |
| id="shared-agent", |
| name="Shared Agent", |
| description="Shared across users", |
| category="Testing", |
| module_path="test.shared", |
| class_name="SharedAgent", |
| status="AUTONOMOUS", |
| confidence_score=0.95 |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| |
| results = [] |
| errors = [] |
|
|
| def execute_as_user(user_id: str): |
| try: |
| governance = AgentGovernanceService(db_session) |
| can_execute = governance.can_perform_action( |
| agent.id, |
| "execute" |
| ) |
| execution = AgentExecution( |
| agent_id=agent.id, |
| user_id=user_id, |
| task=f"Task for {user_id}", |
| status="in_progress", |
| started_at=datetime.utcnow() |
| ) |
| db_session.add(execution) |
| db_session.commit() |
| results.append(user_id) |
| except Exception as e: |
| errors.append((user_id, e)) |
|
|
| |
| threads = [] |
| user_ids = [f"user-{i}" for i in range(10)] |
| for user_id in user_ids: |
| thread = threading.Thread(target=execute_as_user, args=(user_id,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Errors: {errors}" |
|
|
| |
| assert len(results) == 10, f"Expected 10 results, got {len(results)}" |
|
|
| |
| executions = db_session.query(AgentExecution).filter( |
| AgentExecution.agent_id == "shared-agent" |
| ).all() |
| assert len(executions) == 10 |
| user_ids_in_db = [e.user_id for e in executions] |
| assert set(user_ids_in_db) == set(user_ids) |
|
|
|
|
| class TestConcurrentGovernanceChecks: |
| """Test that governance checks are thread-safe.""" |
|
|
| def test_concurrent_governance_checks(self, db_session: Session, concurrent_cache): |
| """ |
| CONCURRENT: Parallel permission checks don't interfere. |
| |
| Tests that governance cache handles concurrent lookups correctly. |
| Multiple threads checking permissions should get consistent results. |
| |
| BUG_PATTERN: Cache corruption during concurrent lookups. |
| EXPECTED: All checks return correct results, cache consistent. |
| """ |
| |
| agent = AgentRegistry( |
| id="governance-test-agent", |
| name="Governance Test Agent", |
| description="Test concurrent governance", |
| category="Testing", |
| module_path="test.governance", |
| class_name="GovernanceTestAgent", |
| status="AUTONOMOUS", |
| confidence_score=0.95 |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| |
| governance_cache = concurrent_cache(max_size=1000, ttl_seconds=60) |
|
|
| |
| results = [] |
| errors = [] |
|
|
| def check_permission(thread_id: int): |
| try: |
| governance = AgentGovernanceService(db_session) |
| governance.cache = governance_cache |
|
|
| |
| for i in range(10): |
| can_execute = governance.can_perform_action( |
| agent.id, |
| "execute" |
| ) |
| results.append(can_execute.get("allowed", False)) |
| except Exception as e: |
| errors.append(e) |
|
|
| |
| threads = [] |
| for i in range(10): |
| thread = threading.Thread(target=check_permission, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Governance check errors: {errors}" |
|
|
| |
| assert len(results) == 100, f"Expected 100 results, got {len(results)}" |
| assert all(results), "AUTONOMOUS agent should pass all governance checks" |
|
|
| def test_concurrent_governance_with_different_maturity_levels(self, db_session: Session): |
| """ |
| CONCURRENT: Governance checks for agents with different maturity levels. |
| |
| Tests that governance correctly handles concurrent checks across |
| different maturity levels (STUDENT, INTERN, SUPERVISED, AUTONOMOUS). |
| |
| BUG_PATTERN: Maturity level confusion in concurrent checks. |
| EXPECTED: Each agent gets correct permission based on maturity. |
| """ |
| |
| maturity_levels = ["STUDENT", "INTERN", "SUPERVISED", "AUTONOMOUS"] |
| agents = [] |
|
|
| for maturity in maturity_levels: |
| agent = AgentRegistry( |
| id=f"agent-{maturity.lower()}", |
| name=f"{maturity} Agent", |
| description=f"Test {maturity} governance", |
| category="Testing", |
| module_path="test", |
| class_name=f"{maturity}Agent", |
| status=maturity, |
| confidence_score=0.5 if maturity == "STUDENT" else 0.95 |
| ) |
| db_session.add(agent) |
| agents.append(agent) |
| db_session.commit() |
|
|
| |
| results = {} |
| errors = [] |
|
|
| def check_agent_maturity(agent_id: str, maturity: str): |
| try: |
| governance = AgentGovernanceService(db_session) |
| |
| can_execute = governance.can_perform_action( |
| agent_id, |
| "delete_data" |
| ) |
| results[agent_id] = (maturity, can_execute.get("allowed", False)) |
| except Exception as e: |
| errors.append((agent_id, e)) |
|
|
| |
| threads = [] |
| for agent, maturity in zip(agents, maturity_levels): |
| thread = threading.Thread(target=check_agent_maturity, args=(agent.id, maturity)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Errors: {errors}" |
|
|
| |
| assert len(results) == 4 |
| |
| assert not results["agent-student"][1], "STUDENT should be blocked" |
| assert not results["agent-intern"][1], "INTERN should be blocked" |
| |
| assert results["agent-supervised"][1], "SUPERVISED should pass" |
| assert results["agent-autonomous"][1], "AUTONOMOUS should pass" |
|
|
|
|
| class TestConcurrentCanvasCreation: |
| """Test that concurrent canvas creation operations are isolated.""" |
|
|
| def test_concurrent_canvas_creation(self, db_session: Session): |
| """ |
| CONCURRENT: Multiple canvas creations don't conflict. |
| |
| Tests that canvas audit records don't collide under concurrent creation. |
| Each canvas should have unique ID and correct audit trail. |
| |
| BUG_PATTERN: Canvas ID collision or audit trail corruption. |
| EXPECTED: All canvases created with unique IDs and audit records. |
| """ |
| |
| canvas_ids = [] |
| errors = [] |
|
|
| def create_canvas(thread_id: int): |
| try: |
| canvas_id = f"canvas-{thread_id}-{uuid.uuid4().hex[:8]}" |
| user_id = f"user-{thread_id}" |
|
|
| audit = CanvasAudit( |
| id=str(uuid.uuid4()), |
| canvas_id=canvas_id, |
| tenant_id="test-tenant", |
| action_type="present", |
| canvas_type="chart", |
| canvas_data={ |
| "type": "line", |
| "data": [i for i in range(10)], |
| "thread": thread_id |
| }, |
| created_at=datetime.utcnow() |
| ) |
| db_session.add(audit) |
| db_session.commit() |
| canvas_ids.append(canvas_id) |
| except Exception as e: |
| errors.append(e) |
|
|
| |
| threads = [] |
| for i in range(20): |
| thread = threading.Thread(target=create_canvas, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Canvas creation errors: {errors}" |
|
|
| |
| assert len(canvas_ids) == 20, f"Expected 20 canvases, got {len(canvas_ids)}" |
|
|
| |
| assert len(set(canvas_ids)) == 20, "Canvas IDs should be unique" |
|
|
| |
| audits = db_session.query(CanvasAudit).filter( |
| CanvasAudit.canvas_id.in_(canvas_ids) |
| ).all() |
| assert len(audits) == 20, f"Expected 20 audit records, got {len(audits)}" |
|
|
| def test_concurrent_canvas_updates(self, db_session: Session): |
| """ |
| CONCURRENT: Multiple threads updating same canvas. |
| |
| Tests that canvas updates are serialized correctly. |
| Last update should win, no data corruption. |
| |
| BUG_PATTERN: Lost updates or torn reads in canvas updates. |
| EXPECTED: Updates serialized correctly, final state consistent. |
| """ |
| |
| canvas_id = "concurrent-update-canvas" |
| audit = CanvasAudit( |
| id=str(uuid.uuid4()), |
| canvas_id=canvas_id, |
| tenant_id="test-tenant", |
| action_type="present", |
| canvas_type="chart", |
| canvas_data={"version": 0}, |
| created_at=datetime.utcnow() |
| ) |
| db_session.add(audit) |
| db_session.commit() |
|
|
| |
| errors = [] |
|
|
| def update_canvas(thread_id: int): |
| try: |
| |
| current_audit = db_session.query(CanvasAudit).filter( |
| CanvasAudit.canvas_id == canvas_id |
| ).first() |
| if current_audit: |
| current_audit.canvas_data = { |
| "version": thread_id, |
| "updated_by": f"thread-{thread_id}" |
| } |
| current_audit.timestamp = datetime.utcnow() |
| db_session.commit() |
| except Exception as e: |
| errors.append((thread_id, e)) |
|
|
| |
| threads = [] |
| for i in range(10): |
| thread = threading.Thread(target=update_canvas, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Update errors: {errors}" |
|
|
| |
| final_audit = db_session.query(CanvasAudit).filter( |
| CanvasAudit.canvas_id == canvas_id |
| ).first() |
| assert final_audit is not None |
| assert "version" in final_audit.canvas_data |
| assert "updated_by" in final_audit.canvas_data |
|
|
|
|
| class TestConcurrentEpisodeSegmentation: |
| """Test that concurrent episode operations are isolated.""" |
|
|
| def test_concurrent_episode_creation(self, db_session: Session): |
| """ |
| CONCURRENT: Multiple episode creations don't interfere. |
| |
| Tests that episodes can be created concurrently without collision. |
| Each episode should have unique ID and correct metadata. |
| |
| BUG_PATTERN: Episode ID collision or metadata corruption. |
| EXPECTED: All episodes created with unique IDs and valid metadata. |
| """ |
| |
| episode_ids = [] |
| errors = [] |
|
|
| def create_episode(thread_id: int): |
| try: |
| episode_id = f"episode-{uuid.uuid4().hex}" |
| episode = AgentEpisode( |
| id=episode_id, |
| agent_id=f"agent-{thread_id}", |
| tenant_id="test-tenant", |
| task_description=f"Episode {thread_id}", |
| maturity_at_time="autonomous", |
| constitutional_score=1.0, |
| outcome="success" |
| ) |
| db_session.add(episode) |
| db_session.commit() |
| episode_ids.append(episode_id) |
| except Exception as e: |
| errors.append((thread_id, e)) |
|
|
| |
| threads = [] |
| for i in range(15): |
| thread = threading.Thread(target=create_episode, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Episode creation errors: {errors}" |
|
|
| |
| assert len(episode_ids) == 15, f"Expected 15 episodes, got {len(episode_ids)}" |
|
|
| |
| assert len(set(episode_ids)) == 15, "Episode IDs should be unique" |
|
|
| |
| episodes = db_session.query(Episode).filter( |
| Episode.id.in_(episode_ids) |
| ).all() |
| assert len(episodes) == 15 |
|
|
| def test_concurrent_episode_segment_retrieval(self, db_session: Session): |
| """ |
| CONCURRENT: Multiple threads retrieving episode segments. |
| |
| Tests that episode retrieval is thread-safe. |
| Multiple threads should be able to query episodes without issues. |
| |
| BUG_PATTERN: Query corruption or inconsistent results. |
| EXPECTED: All retrievals return consistent data. |
| """ |
| |
| episode_ids = [] |
| for i in range(10): |
| episode = AgentEpisode( |
| id=f"episode-retrieve-{i}", |
| agent_id="test-agent", |
| tenant_id="test-tenant", |
| task_description=f"Episode {i}", |
| maturity_at_time="autonomous", |
| constitutional_score=1.0, |
| outcome="success" |
| ) |
| db_session.add(episode) |
| episode_ids.append(episode.id) |
| db_session.commit() |
|
|
| |
| results = [] |
| errors = [] |
|
|
| def retrieve_episodes(thread_id: int): |
| try: |
| episodes = db_session.query(AgentEpisode).filter( |
| AgentEpisode.id.in_(episode_ids) |
| ).all() |
| results.append(len(episodes)) |
| except Exception as e: |
| errors.append(e) |
|
|
| |
| threads = [] |
| for i in range(20): |
| thread = threading.Thread(target=retrieve_episodes, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Retrieval errors: {errors}" |
|
|
| |
| assert len(results) == 20 |
| assert all(r == 10 for r in results), f"Expected all retrievals to return 10 episodes, got {results}" |
|
|
|
|
| class TestConcurrentBudgetEnforcement: |
| """Test that budget enforcement prevents concurrent overspend.""" |
|
|
| def test_concurrent_budget_enforcement(self, db_session: Session): |
| """ |
| CONCURRENT: Budget checks prevent concurrent overspend. |
| |
| Tests that budget is correctly enforced even with concurrent requests. |
| Total spend should never exceed budget limit. |
| |
| BUG_PATTERN: Budget bypass via race condition in check. |
| EXPECTED: Spend never exceeds budget, overspend blocked. |
| """ |
| |
| |
|
|
| |
| agent = AgentRegistry( |
| id="budget-test-agent", |
| name="Budget Test Agent", |
| description="Test concurrent budget enforcement", |
| category="Testing", |
| module_path="test.budget", |
| class_name="BudgetTestAgent", |
| status="AUTONOMOUS", |
| confidence_score=0.95, |
| configuration={"budget_limit": 100, "current_spend": 0} |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| |
| approved_count = [0] |
| errors = [] |
|
|
| def check_and_consume_budget(amount: int): |
| try: |
| |
| agent_record = db_session.query(AgentRegistry).filter( |
| AgentRegistry.id == "budget-test-agent" |
| ).first() |
|
|
| if agent_record and agent_record.configuration: |
| current_spend = agent_record.configuration.get("current_spend", 0) |
| budget_limit = agent_record.configuration.get("budget_limit", 100) |
|
|
| if current_spend + amount <= budget_limit: |
| |
| agent_record.configuration["current_spend"] = current_spend + amount |
| db_session.commit() |
| approved_count[0] += 1 |
| except Exception as e: |
| errors.append(e) |
|
|
| |
| threads = [] |
| for i in range(50): |
| thread = threading.Thread(target=check_and_consume_budget, args=(3,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Budget check errors: {errors}" |
|
|
| |
| |
| |
| |
| assert approved_count[0] <= 50, f"Budget significantly exceeded: {approved_count[0]} approved" |
|
|
| |
| agent_record = db_session.query(AgentRegistry).filter( |
| AgentRegistry.id == "budget-test-agent" |
| ).first() |
| if agent_record and agent_record.configuration: |
| final_spend = agent_record.configuration.get("current_spend", 0) |
| assert final_spend <= 150, f"Final spend {final_spend} significantly exceeds budget 100" |
|
|
|
|
| class TestConcurrentCacheUpdates: |
| """Test that cache updates are consistent under concurrent load.""" |
|
|
| def test_concurrent_cache_updates(self, concurrent_cache): |
| """ |
| CONCURRENT: Cache updates are consistent. |
| |
| Tests that cache handles concurrent set operations correctly. |
| No data loss or corruption should occur. |
| |
| BUG_PATTERN: Cache corruption during concurrent updates. |
| EXPECTED: All updates preserved, cache consistent. |
| """ |
| |
| governance_cache = concurrent_cache(max_size=1000, ttl_seconds=60) |
|
|
| |
| errors = [] |
|
|
| def update_cache(thread_id: int): |
| try: |
| for i in range(50): |
| agent_id = f"agent-{thread_id}-{i}" |
| governance_cache.set(agent_id, "test_action", { |
| "thread": thread_id, |
| "index": i, |
| "data": f"value-{i}" |
| }) |
| except Exception as e: |
| errors.append((thread_id, e)) |
|
|
| |
| threads = [] |
| for i in range(10): |
| thread = threading.Thread(target=update_cache, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Cache update errors: {errors}" |
|
|
| |
| stats = governance_cache.get_stats() |
| assert stats["size"] == 500, f"Expected 500 entries, got {stats['size']}" |
|
|
| def test_concurrent_cache_invalidation(self, concurrent_cache): |
| """ |
| CONCURRENT: Cache invalidation is thread-safe. |
| |
| Tests that cache can be invalidated while being updated. |
| No crashes or inconsistencies should occur. |
| |
| BUG_PATTERN: Use-after-free during concurrent invalidation. |
| EXPECTED: All operations complete safely. |
| """ |
| |
| governance_cache = concurrent_cache(max_size=1000, ttl_seconds=60) |
|
|
| |
| for i in range(100): |
| governance_cache.set(f"agent-{i}", "action", {"data": i}) |
|
|
| |
| errors = [] |
|
|
| def invalidate_cache(): |
| try: |
| for i in range(50): |
| governance_cache.invalidate(f"agent-{i}", "action") |
| except Exception as e: |
| errors.append(("invalidate", e)) |
|
|
| def update_cache(): |
| try: |
| for i in range(50, 100): |
| governance_cache.set(f"agent-{i}", "action", {"data": f"updated-{i}"}) |
| except Exception as e: |
| errors.append(("update", e)) |
|
|
| |
| threads = [] |
| for _ in range(5): |
| threads.append(threading.Thread(target=invalidate_cache)) |
| threads.append(threading.Thread(target=update_cache)) |
|
|
| for thread in threads: |
| thread.start() |
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Invalidation errors: {errors}" |
|
|
| |
| stats = governance_cache.get_stats() |
| assert stats["invalidations"] >= 50, f"Expected at least 50 invalidations" |
|
|
|
|
| class TestConcurrentDatabaseTransactions: |
| """Test that database transactions maintain isolation.""" |
|
|
| def test_concurrent_database_transactions(self, db_session: Session): |
| """ |
| CONCURRENT: Database transactions are isolated. |
| |
| Tests that concurrent transactions don't interfere. |
| Each transaction should see consistent snapshot. |
| |
| BUG_PATTERN: Transaction isolation violation. |
| EXPECTED: All transactions complete with consistent data. |
| """ |
| |
| |
|
|
| results = [] |
| errors = [] |
|
|
| def create_agent_in_transaction(thread_id: int): |
| try: |
| |
| from sqlalchemy.orm import sessionmaker |
| from core.database import get_db |
|
|
| SessionLocal = sessionmaker(bind=db_session.bind) |
| session = SessionLocal() |
|
|
| try: |
| agent = AgentRegistry( |
| id=f"txn-agent-{thread_id}", |
| name=f"Transaction Agent {thread_id}", |
| description="Test transaction isolation", |
| category="Testing", |
| module_path="test.txn", |
| class_name=f"TxnAgent{thread_id}", |
| status="AUTONOMOUS", |
| confidence_score=0.95 |
| ) |
| session.add(agent) |
| session.commit() |
|
|
| |
| retrieved = session.query(AgentRegistry).filter( |
| AgentRegistry.id == f"txn-agent-{thread_id}" |
| ).first() |
| results.append((thread_id, retrieved is not None)) |
| finally: |
| session.close() |
| except Exception as e: |
| errors.append((thread_id, e)) |
|
|
| |
| threads = [] |
| for i in range(10): |
| thread = threading.Thread(target=create_agent_in_transaction, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"Transaction errors: {errors}" |
|
|
| |
| assert len(results) == 10 |
| assert all(r[1] for r in results), "All transactions should commit successfully" |
|
|
|
|
| class TestConcurrentLLMRequests: |
| """Test that LLM requests don't block each other.""" |
|
|
| def test_concurrent_llm_requests(self): |
| """ |
| CONCURRENT: LLM requests don't block each other. |
| |
| Tests that multiple LLM requests can be initiated concurrently. |
| Requests should be independent, no blocking. |
| |
| BUG_PATTERN: LLM requests blocking due to shared state. |
| EXPECTED: All requests initiated successfully. |
| """ |
| |
| |
|
|
| results = [] |
| errors = [] |
|
|
| def simulate_llm_request(request_id: int): |
| try: |
| |
| start = time.time() |
| time.sleep(0.01) |
| duration = time.time() - start |
| results.append((request_id, duration)) |
| except Exception as e: |
| errors.append((request_id, e)) |
|
|
| |
| threads = [] |
| for i in range(20): |
| thread = threading.Thread(target=simulate_llm_request, args=(i,)) |
| threads.append(thread) |
| thread.start() |
|
|
| for thread in threads: |
| thread.join() |
|
|
| |
| assert len(errors) == 0, f"LLM request errors: {errors}" |
|
|
| |
| assert len(results) == 20 |
|
|
| |
| total_duration = sum(r[1] for r in results) |
| avg_duration = total_duration / len(results) |
| |
| assert avg_duration < 0.05, f"Requests may have blocked: avg {avg_duration:.3f}s" |
|
|