gabriel / musehub public
test_intel_code_scale.py python
150 lines 5.8 KB
Raw
sha256:94ef169c149a452bff7c604ded8b280b19bd477c2dabcb56972780b0b784c7aa Merge 'fix/assignee-sigil-inline' into 'dev' — proposal: As… Human 1 day ago
1 """TDD — intel.code job scales cleanly at 1, 5, 50, 500 commits.
2
3 Each test creates a repo with N commits (each with structured_delta),
4 runs build_symbol_index, and asserts:
5 1. Completes without error
6 2. Returns code.intel_summary and code.intel_snapshot
7 3. Writes rows to musehub_symbol_intel
8
9 Scale ladder: 1 → 5 → 50 → 500
10 The first failing test tells us exactly where the ceiling is.
11 """
12 from __future__ import annotations
13
14 import datetime
15 import hashlib
16 import time
17
18 import pytest
19 from sqlalchemy import select
20 from sqlalchemy.ext.asyncio import AsyncSession
21
22 from muse.core.types import fake_id
23 from musehub.db.musehub_intel_models import MusehubSymbolHistoryEntry, MusehubSymbolIntel
24 from musehub.db.musehub_repo_models import MusehubCommit, MusehubCommitRef
25 from tests.factories import create_repo
26
27 # ---------------------------------------------------------------------------
28 # Helpers
29 # ---------------------------------------------------------------------------
30
31 def _structured_delta(n_symbols: int, seed: str = "") -> dict:
32 """Produce a realistic structured_delta for N symbols."""
33 return {
34 "ops": [
35 {
36 "op": "insert" if i % 3 != 1 else "replace",
37 "address": f"musehub/services/svc_{seed}_{i}.py::func_{i}",
38 "new_content_id": fake_id(f"{seed}-{i}-content"),
39 "symbol_kind": "function",
40 }
41 for i in range(n_symbols)
42 ]
43 }
44
45
46 async def _populate_repo(
47 db: AsyncSession,
48 repo_id: str,
49 n_commits: int,
50 symbols_per_commit: int,
51 ) -> str:
52 """Create n_commits on the repo, each touching symbols_per_commit symbols."""
53 parent_id: str | None = None
54 tip = ""
55 for i in range(n_commits):
56 commit_id = fake_id(f"{repo_id}-commit-{i}")
57 commit = MusehubCommit(
58 commit_id=commit_id,
59 branch="main",
60 parent_ids=[parent_id] if parent_id else [],
61 message=f"commit {i}",
62 author="gabriel",
63 timestamp=datetime.datetime.now(tz=datetime.timezone.utc),
64 structured_delta=_structured_delta(symbols_per_commit, seed=f"c{i}"),
65 )
66 db.add(commit)
67 db.add(MusehubCommitRef(repo_id=repo_id, commit_id=commit_id))
68 parent_id = commit_id
69 tip = commit_id
70 await db.flush()
71 return tip
72
73
74 def _assert_fast(elapsed: float, limit: float, label: str) -> None:
75 assert elapsed < limit, (
76 f"{label} took {elapsed:.2f}s — limit is {limit:.1f}s. "
77 f"Bottleneck is in build_symbol_index; use the [intel.code] timing logs to pinpoint."
78 )
79
80
81 # ---------------------------------------------------------------------------
82 # Scale tests
83 # ---------------------------------------------------------------------------
84
85 @pytest.mark.asyncio
86 async def test_intel_code_1_commit(db_session: AsyncSession) -> None:
87 """Baseline: 1 commit, 5 symbols — must complete in <2s."""
88 from musehub.services.musehub_symbol_indexer import build_symbol_index
89 repo = await create_repo(db_session, name="intel-scale-1", owner="gabriel", visibility="public")
90 tip = await _populate_repo(db_session, repo.repo_id, n_commits=1, symbols_per_commit=5)
91 await db_session.flush()
92
93 t0 = time.monotonic()
94 result = await build_symbol_index(db_session, repo.repo_id, tip)
95 elapsed = time.monotonic() - t0
96
97 assert any(t == "code.intel_summary" for t, _ in result), "must return code.intel_summary"
98 rows = (await db_session.execute(
99 select(MusehubSymbolIntel).where(MusehubSymbolIntel.repo_id == repo.repo_id)
100 )).scalars().all()
101 assert len(rows) == 5, f"expected 5 symbol_intel rows, got {len(rows)}"
102 _assert_fast(elapsed, 2.0, "1 commit / 5 symbols")
103
104
105 @pytest.mark.asyncio
106 async def test_intel_code_5_commits(db_session: AsyncSession) -> None:
107 """5 commits × 10 symbols each — must complete in <5s."""
108 from musehub.services.musehub_symbol_indexer import build_symbol_index
109 repo = await create_repo(db_session, name="intel-scale-5", owner="gabriel", visibility="public")
110 tip = await _populate_repo(db_session, repo.repo_id, n_commits=5, symbols_per_commit=10)
111 await db_session.flush()
112
113 t0 = time.monotonic()
114 result = await build_symbol_index(db_session, repo.repo_id, tip)
115 elapsed = time.monotonic() - t0
116
117 assert any(t == "code.intel_summary" for t, _ in result)
118 _assert_fast(elapsed, 5.0, "5 commits / 10 symbols each")
119
120
121 @pytest.mark.asyncio
122 async def test_intel_code_50_commits(db_session: AsyncSession) -> None:
123 """50 commits × 20 symbols each — must complete in <15s."""
124 from musehub.services.musehub_symbol_indexer import build_symbol_index
125 repo = await create_repo(db_session, name="intel-scale-50", owner="gabriel", visibility="public")
126 tip = await _populate_repo(db_session, repo.repo_id, n_commits=50, symbols_per_commit=20)
127 await db_session.flush()
128
129 t0 = time.monotonic()
130 result = await build_symbol_index(db_session, repo.repo_id, tip)
131 elapsed = time.monotonic() - t0
132
133 assert any(t == "code.intel_summary" for t, _ in result)
134 _assert_fast(elapsed, 15.0, "50 commits / 20 symbols each")
135
136
137 @pytest.mark.asyncio
138 async def test_intel_code_500_commits(db_session: AsyncSession) -> None:
139 """500 commits × 30 symbols each — must complete in <60s (production SLA)."""
140 from musehub.services.musehub_symbol_indexer import build_symbol_index
141 repo = await create_repo(db_session, name="intel-scale-500", owner="gabriel", visibility="public")
142 tip = await _populate_repo(db_session, repo.repo_id, n_commits=500, symbols_per_commit=30)
143 await db_session.flush()
144
145 t0 = time.monotonic()
146 result = await build_symbol_index(db_session, repo.repo_id, tip)
147 elapsed = time.monotonic() - t0
148
149 assert any(t == "code.intel_summary" for t, _ in result)
150 _assert_fast(elapsed, 60.0, "500 commits / 30 symbols each")
File History 1 commit
sha256:94ef169c149a452bff7c604ded8b280b19bd477c2dabcb56972780b0b784c7aa Merge 'fix/assignee-sigil-inline' into 'dev' — proposal: As… Human 1 day ago