cgcardona / muse public
test_code_query.py python
173 lines 5.8 KB
766ee24d feat: code domain leverages core invariants, query engine, manifests, p… Gabriel Cardona <gabriel@tellurstori.com> 1d ago
1 """Tests for the code domain query evaluator."""
2 from __future__ import annotations
3
4 import pathlib
5
6 import pytest
7
8 from muse.domain import SemVerBump
9 from muse.plugins.code._code_query import (
10 AndExpr,
11 Comparison,
12 OrExpr,
13 build_evaluator,
14 _parse_query,
15 )
16
17
18 # ---------------------------------------------------------------------------
19 # Parser
20 # ---------------------------------------------------------------------------
21
22
23 class TestParseQuery:
24 def test_simple_equality(self) -> None:
25 q = _parse_query("author == 'gabriel'")
26 assert isinstance(q, OrExpr)
27 assert len(q.clauses) == 1
28 and_expr = q.clauses[0]
29 assert isinstance(and_expr, AndExpr)
30 cmp = and_expr.clauses[0]
31 assert cmp.field == "author"
32 assert cmp.op == "=="
33 assert cmp.value == "gabriel"
34
35 def test_and_expression(self) -> None:
36 q = _parse_query("author == 'x' and language == 'Python'")
37 assert len(q.clauses[0].clauses) == 2
38
39 def test_or_expression(self) -> None:
40 q = _parse_query("author == 'x' or author == 'y'")
41 assert len(q.clauses) == 2
42
43 def test_contains_operator(self) -> None:
44 q = _parse_query("agent_id contains claude")
45 cmp = q.clauses[0].clauses[0]
46 assert cmp.op == "contains"
47 assert cmp.value == "claude"
48
49 def test_startswith_operator(self) -> None:
50 q = _parse_query("symbol startswith my_")
51 cmp = q.clauses[0].clauses[0]
52 assert cmp.op == "startswith"
53
54 def test_unknown_field_raises(self) -> None:
55 with pytest.raises(ValueError, match="Unknown field"):
56 _parse_query("nonexistent == 'x'")
57
58 def test_double_quoted_string(self) -> None:
59 q = _parse_query('author == "gabriel"')
60 cmp = q.clauses[0].clauses[0]
61 assert cmp.value == "gabriel"
62
63
64 # ---------------------------------------------------------------------------
65 # build_evaluator + evaluator logic
66 # ---------------------------------------------------------------------------
67
68
69 import datetime
70 from muse.core.query_engine import QueryMatch
71 from muse.core.store import CommitRecord
72 from muse.domain import StructuredDelta, InsertOp
73
74
75 def _make_commit(
76 author: str = "alice",
77 agent_id: str = "",
78 model_id: str = "",
79 branch: str = "main",
80 sem_ver_bump: SemVerBump = "none",
81 delta: StructuredDelta | None = None,
82 ) -> CommitRecord:
83 return CommitRecord(
84 commit_id="abc1234",
85 repo_id="repo",
86 branch=branch,
87 snapshot_id="snap1",
88 message="test commit",
89 committed_at=datetime.datetime.now(datetime.timezone.utc),
90 author=author,
91 agent_id=agent_id,
92 model_id=model_id,
93 sem_ver_bump=sem_ver_bump,
94 structured_delta=delta,
95 )
96
97
98 class TestBuildEvaluator:
99 def test_author_match(self) -> None:
100 evaluator = build_evaluator("author == 'alice'")
101 commit = _make_commit(author="alice")
102 results = evaluator(commit, {}, pathlib.Path("."))
103 assert len(results) == 1
104
105 def test_author_no_match(self) -> None:
106 evaluator = build_evaluator("author == 'bob'")
107 commit = _make_commit(author="alice")
108 results = evaluator(commit, {}, pathlib.Path("."))
109 assert results == []
110
111 def test_agent_id_contains(self) -> None:
112 evaluator = build_evaluator("agent_id contains claude")
113 commit = _make_commit(agent_id="claude-v4")
114 results = evaluator(commit, {}, pathlib.Path("."))
115 assert len(results) == 1
116
117 def test_sem_ver_bump_match(self) -> None:
118 evaluator = build_evaluator("sem_ver_bump == 'major'")
119 commit = _make_commit(sem_ver_bump="major")
120 results = evaluator(commit, {}, pathlib.Path("."))
121 assert len(results) == 1
122
123 def test_and_both_must_match(self) -> None:
124 evaluator = build_evaluator("author == 'alice' and agent_id == 'bot'")
125 # Only author matches, not agent_id.
126 commit = _make_commit(author="alice", agent_id="human")
127 results = evaluator(commit, {}, pathlib.Path("."))
128 assert results == []
129
130 def test_or_one_match_sufficient(self) -> None:
131 evaluator = build_evaluator("author == 'alice' or author == 'bob'")
132 commit_alice = _make_commit(author="alice")
133 commit_bob = _make_commit(author="bob")
134 assert len(evaluator(commit_alice, {}, pathlib.Path("."))) >= 1
135 assert len(evaluator(commit_bob, {}, pathlib.Path("."))) >= 1
136
137 def test_branch_match(self) -> None:
138 evaluator = build_evaluator("branch == 'dev'")
139 commit = _make_commit(branch="dev")
140 assert len(evaluator(commit, {}, pathlib.Path("."))) >= 1
141
142 def test_symbol_match_from_delta(self) -> None:
143 op = InsertOp(
144 op="insert",
145 address="src/utils.py::my_func",
146 position=None,
147 content_id="hash1",
148 content_summary="added my_func",
149 )
150 delta = StructuredDelta(domain="code", ops=[op], summary="1 symbol added")
151 commit = _make_commit(delta=delta)
152 evaluator = build_evaluator("symbol == 'my_func'")
153 results = evaluator(commit, {}, pathlib.Path("."))
154 assert len(results) >= 1
155 assert any("my_func" in r.get("detail", "") for r in results)
156
157 def test_change_added_match(self) -> None:
158 op = InsertOp(
159 op="insert",
160 address="src/foo.py::bar",
161 position=None,
162 content_id="h1",
163 content_summary="bar added",
164 )
165 delta = StructuredDelta(domain="code", ops=[op], summary="added bar")
166 commit = _make_commit(delta=delta)
167 evaluator = build_evaluator("change == 'added'")
168 results = evaluator(commit, {}, pathlib.Path("."))
169 assert len(results) >= 1
170
171 def test_invalid_query_raises(self) -> None:
172 with pytest.raises(ValueError):
173 build_evaluator("badfield == 'x'")