""" Dev B Test Suite - Run with: .venv/Scripts/python test_devb.py """ import asyncio import sys sys.path.insert(0, '.') from app.llm import generate_documentation, synthesize_answer, get_embedding from app.vectorstore import init_vectorstore, add_embedding, search, count_embeddings, delete_by_project # Test counters passed = 0 failed = 0 def test(name, condition, details=""): global passed, failed if condition: print(f" [PASS] {name}") passed += 1 else: print(f" [FAIL] {name} - {details}") failed += 1 async def test_llm(): print("\n[1/4] Testing LLM Client (llm.py)") print("-" * 40) # Test 1: get_embedding try: emb = await get_embedding("test embedding") test("get_embedding returns list", isinstance(emb, list)) test("get_embedding returns 768 dims", len(emb) == 768, f"got {len(emb)}") test("get_embedding returns floats", isinstance(emb[0], float)) except Exception as e: test("get_embedding", False, str(e)) # Test 2: generate_documentation try: doc = await generate_documentation( task_title="Setup API", what_i_did="Created REST endpoints", code_snippet="@app.get('/api')" ) test("generate_documentation returns dict", isinstance(doc, dict)) test("generate_documentation has summary", "summary" in doc) test("generate_documentation has details", "details" in doc) test("generate_documentation has tags", "tags" in doc and isinstance(doc["tags"], list)) except Exception as e: test("generate_documentation", False, str(e)) # Test 3: synthesize_answer try: answer = await synthesize_answer( context="Task completed: Built login system with JWT authentication", query="What authentication was implemented?" ) test("synthesize_answer returns string", isinstance(answer, str)) test("synthesize_answer not empty", len(answer) > 0) except Exception as e: test("synthesize_answer", False, str(e)) async def test_vectorstore(): print("\n[2/4] Testing Vector Store (vectorstore.py)") print("-" * 40) # Test 1: init_vectorstore try: init_vectorstore() test("init_vectorstore succeeds", True) except Exception as e: test("init_vectorstore", False, str(e)) return # Can't continue without init # Clean up test data first try: delete_by_project("test-project-xyz") except: pass # Test 2: add_embedding try: emb = await get_embedding("Test document about Python programming") add_embedding( log_entry_id="test-entry-1", text="Test document about Python programming", embedding=emb, metadata={ "project_id": "test-project-xyz", "user_id": "test-user", "task_id": "test-task", "created_at": "2024-01-01T00:00:00" } ) test("add_embedding succeeds", True) except Exception as e: test("add_embedding", False, str(e)) # Test 3: count_embeddings try: count = count_embeddings("test-project-xyz") test("count_embeddings returns int", isinstance(count, int)) test("count_embeddings >= 1", count >= 1, f"got {count}") except Exception as e: test("count_embeddings", False, str(e)) # Test 4: search try: query_emb = await get_embedding("Python") results = search(query_emb, "test-project-xyz", n_results=5) test("search returns list", isinstance(results, list)) test("search finds results", len(results) > 0, "no results found") if results: test("search result has id", "id" in results[0]) test("search result has metadata", "metadata" in results[0]) test("search result has distance", "distance" in results[0]) except Exception as e: test("search", False, str(e)) # Test 5: delete_by_project try: delete_by_project("test-project-xyz") count_after = count_embeddings("test-project-xyz") test("delete_by_project removes data", count_after == 0, f"still has {count_after}") except Exception as e: test("delete_by_project", False, str(e)) async def test_full_pipeline(): print("\n[3/4] Testing Full Pipeline") print("-" * 40) project_id = "pipeline-test-proj" # Clean up first try: delete_by_project(project_id) except: pass try: # Step 1: Generate documentation doc = await generate_documentation( task_title="Implement user registration", what_i_did="Added signup endpoint with email validation and password hashing", code_snippet="def register(email, password): ..." ) test("Pipeline: doc generation", "summary" in doc and "details" in doc) # Step 2: Create embedding from doc text_to_embed = f"{doc['summary']} {doc['details']}" embedding = await get_embedding(text_to_embed) test("Pipeline: embedding created", len(embedding) == 768) # Step 3: Store in vectorstore add_embedding( log_entry_id="pipeline-log-1", text=text_to_embed, embedding=embedding, metadata={"project_id": project_id, "user_id": "dev1"} ) test("Pipeline: stored in vectorstore", count_embeddings(project_id) == 1) # Step 4: Search for it query_emb = await get_embedding("user registration signup") results = search(query_emb, project_id) test("Pipeline: search finds it", len(results) > 0) # Step 5: Synthesize answer if results: context = results[0]["metadata"]["text"] answer = await synthesize_answer(context, "What was done for user registration?") test("Pipeline: answer synthesized", len(answer) > 20) # Cleanup delete_by_project(project_id) except Exception as e: test("Pipeline", False, str(e)) async def test_edge_cases(): print("\n[4/4] Testing Edge Cases") print("-" * 40) # Test empty search try: init_vectorstore() emb = await get_embedding("random query") results = search(emb, "nonexistent-project-12345") test("Empty search returns empty list", results == []) except Exception as e: test("Empty search", False, str(e)) # Test long text embedding try: long_text = "word " * 1000 # ~5000 chars emb = await get_embedding(long_text) test("Long text embedding works", len(emb) == 768) except Exception as e: test("Long text embedding", False, str(e)) # Test special characters try: special_text = "Code: `const x = 'hello';` // comment " emb = await get_embedding(special_text) test("Special chars embedding works", len(emb) == 768) except Exception as e: test("Special chars embedding", False, str(e)) async def main(): print("=" * 50) print(" DEV B TEST SUITE - Intelligence Layer") print("=" * 50) await test_llm() await test_vectorstore() await test_full_pipeline() await test_edge_cases() print("\n" + "=" * 50) print(f" RESULTS: {passed} passed, {failed} failed") print("=" * 50) if failed > 0: sys.exit(1) if __name__ == "__main__": asyncio.run(main())