Skip to content

Commit 5db74e9

Browse files
authored
CG-10520: Copy over test for runner module (#144)
# Motivation <!-- Why is this change necessary? --> # Content <!-- Please include a summary of the change --> # Testing <!-- How was the change tested? --> # Please check the following before marking your PR as ready for review - [x] I have added tests for my changes - [x] I have updated the documentation or added new documentation as needed - [x] I have read and agree to the [Contributor License Agreement](../CLA.md) ---------
1 parent b785199 commit 5db74e9

File tree

12 files changed

+790
-0
lines changed

12 files changed

+790
-0
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ dev-dependencies = [
112112
# "scalene>=1.5.45",
113113
"filelock<4.0.0,>=3.15.4",
114114
"pytest>=8.3.3",
115+
"pytest-asyncio>=0.23.5",
115116
"pytest-cov>=6.0.0,<6.0.1",
116117
"ruff>=0.6.8",
117118
"mypy[mypyc,faster-cache]>=1.13.0",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from collections.abc import Generator
2+
from unittest.mock import patch
3+
4+
import pytest
5+
6+
from codegen.git.repo_operator.local_repo_operator import LocalRepoOperator
7+
from codegen.git.schemas.repo_config import RepoConfig
8+
from codegen.runner.models.configs import RunnerFeatureFlags
9+
from codegen.runner.sandbox.executor import SandboxExecutor
10+
from codegen.runner.sandbox.runner import SandboxRunner
11+
from codegen.sdk.codebase.config import CodebaseConfig, GSFeatureFlags, ProjectConfig
12+
from codegen.sdk.core.codebase import Codebase
13+
from codegen.sdk.enums import ProgrammingLanguage
14+
from codegen.sdk.secrets import Secrets
15+
16+
17+
@pytest.fixture
18+
def codebase(tmpdir, request) -> Codebase:
19+
repo_id = getattr(request, "param", 1)
20+
repo_config = RepoConfig(id=repo_id, name="test-repo", full_name="test-org/test-repo", organization_id=1, organization_name="test-org")
21+
op = LocalRepoOperator.create_from_files(repo_path=tmpdir, files={"test.py": "a = 1"}, bot_commit=True, repo_config=repo_config)
22+
projects = [ProjectConfig(repo_operator=op, programming_language=ProgrammingLanguage.PYTHON)]
23+
codebase = Codebase(projects=projects)
24+
return codebase
25+
26+
27+
@pytest.fixture
28+
def executor(codebase: Codebase) -> Generator[SandboxExecutor]:
29+
with patch("codegen.runner.sandbox.executor.get_runner_feature_flags") as mock_ff:
30+
mock_ff.return_value = RunnerFeatureFlags(syntax_highlight=False)
31+
32+
yield SandboxExecutor(codebase)
33+
34+
35+
@pytest.fixture
36+
def runner(codebase: Codebase, tmpdir):
37+
with patch("codegen.runner.sandbox.runner.RemoteRepoOperator") as mock_op:
38+
with patch.object(SandboxRunner, "_build_graph") as mock_init_codebase:
39+
mock_init_codebase.return_value = codebase
40+
mock_op.return_value = codebase.op
41+
42+
yield SandboxRunner(container_id="ta-123", repo_config=codebase.op.repo_config)
43+
44+
45+
@pytest.fixture(autouse=True)
46+
def mock_runner_flags():
47+
with patch("codegen.runner.sandbox.executor.get_runner_feature_flags") as mock_ff:
48+
mock_ff.return_value = RunnerFeatureFlags(syntax_highlight=False)
49+
yield mock_ff
50+
51+
52+
@pytest.fixture(autouse=True)
53+
def mock_codebase_config():
54+
with patch("codegen.runner.sandbox.runner.get_codebase_config") as mock_config:
55+
gs_ffs = GSFeatureFlags(**RunnerFeatureFlags().model_dump())
56+
secrets = Secrets(openai_key="test-key")
57+
mock_config.return_value = CodebaseConfig(secrets=secrets, feature_flags=gs_ffs)
58+
yield mock_config
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
from __future__ import annotations
2+
3+
from unittest.mock import MagicMock
4+
5+
import pytest
6+
7+
from codegen.git.models.codemod_context import CodemodContext
8+
from codegen.runner.models.codemod import GroupingConfig
9+
from codegen.runner.sandbox.executor import SandboxExecutor
10+
from codegen.sdk.codebase.config import SessionOptions
11+
from codegen.sdk.codebase.flagging.code_flag import CodeFlag
12+
from codegen.sdk.codebase.flagging.groupers.enums import GroupBy
13+
from codegen.shared.compilation.string_to_code import create_execute_function_from_codeblock
14+
15+
16+
@pytest.mark.asyncio
17+
async def test_execute_func_pass_in_codemod_context_takes_priority(executor: SandboxExecutor):
18+
codemod_context = CodemodContext(
19+
CODEMOD_LINK="http://codegen.sh/codemod/5678",
20+
)
21+
mock_source = """
22+
print(context.CODEMOD_LINK)
23+
"""
24+
custom_scope = {"context": codemod_context}
25+
code_to_exec = create_execute_function_from_codeblock(codeblock=mock_source, custom_scope=custom_scope)
26+
mock_log = MagicMock()
27+
executor.codebase.log = mock_log
28+
29+
result = await executor.execute(code_to_exec)
30+
31+
assert result is not None
32+
33+
assert mock_log.call_count == 1
34+
assert mock_log.call_args_list[0][0][0] == "http://codegen.sh/codemod/5678"
35+
36+
37+
# @pytest.mark.asyncio
38+
# async def test_init_execute_func_with_pull_request_context(executor: SandboxExecutor):
39+
# mock_source = """
40+
# print(context.PULL_REQUEST.head.ref)
41+
# print(context.PULL_REQUEST.base.ref)
42+
# """
43+
# mock_cm_run = MagicMock(epic=MagicMock(id=1234, link="link", user=MagicMock(github_username="user")), codemod_version=MagicMock(source=mock_source))
44+
# mock_pull = MagicMock(spec=GithubWebhookPR, head=MagicMock(ref="test-head"), base=MagicMock(ref="test-base"))
45+
# codemod_context = get_codemod_context(cm_run=mock_cm_run, pull_request=mock_pull)
46+
# custom_scope = {"context": codemod_context}
47+
# code_to_exec = create_execute_function_from_codeblock(codeblock=mock_source, custom_scope=custom_scope)
48+
# mock_log = MagicMock()
49+
# executor.codebase.log = mock_log
50+
#
51+
# result = await executor.execute(code_to_exec)
52+
#
53+
# assert result is not None
54+
# assert mock_log.call_count == 2
55+
# assert mock_log.call_args_list[0][0][0] == "test-head"
56+
# assert mock_log.call_args_list[1][0][0] == "test-base"
57+
#
58+
#
59+
# @pytest.mark.asyncio
60+
# async def test_init_execute_func_with_pull_request_context_mock_codebase(executor: SandboxExecutor):
61+
# mock_source = """
62+
# print(context.PULL_REQUEST.head.ref)
63+
# print(context.PULL_REQUEST.base.ref)
64+
# """
65+
# mock_cm_run = MagicMock(epic=MagicMock(id=1234, link="link", user=MagicMock(github_username="user")), codemod_version=MagicMock(source=mock_source))
66+
# mock_pull = MagicMock(spec=GithubWebhookPR, head=MagicMock(ref="test-head"), base=MagicMock(ref="test-base"))
67+
# codemod_context = get_codemod_context(cm_run=mock_cm_run, pull_request=mock_pull)
68+
# custom_scope = {"context": codemod_context}
69+
# code_to_exec = create_execute_function_from_codeblock(codeblock=mock_source, custom_scope=custom_scope)
70+
#
71+
# result = await executor.execute(code_to_exec)
72+
#
73+
# # validate
74+
# assert result is not None
75+
# assert (
76+
# result.logs
77+
# == """
78+
# test-head
79+
# test-base
80+
# """.lstrip()
81+
# )
82+
83+
84+
@pytest.mark.asyncio
85+
async def test_run_max_preview_time_exceeded_sets_observation_meta(executor: SandboxExecutor):
86+
mock_source = """
87+
codebase.files[0].edit("a = 2")
88+
"""
89+
code_to_exec = create_execute_function_from_codeblock(codeblock=mock_source)
90+
result = await executor.execute(code_to_exec, session_options=SessionOptions(max_seconds=0))
91+
92+
assert result.is_complete
93+
assert result.observation_meta == {"flags": [], "stop_codemod_exception_type": "MaxPreviewTimeExceeded", "threshold": 0}
94+
95+
96+
@pytest.mark.asyncio
97+
async def test_run_max_ai_requests_error_sets_observation_meta(executor: SandboxExecutor):
98+
mock_source = """
99+
codebase.ai("tell me a joke")
100+
"""
101+
code_to_exec = create_execute_function_from_codeblock(codeblock=mock_source)
102+
result = await executor.execute(code_to_exec, session_options=SessionOptions(max_ai_requests=0))
103+
104+
assert result.is_complete
105+
assert result.observation_meta == {"flags": [], "stop_codemod_exception_type": "MaxAIRequestsError", "threshold": 0}
106+
107+
108+
@pytest.mark.asyncio
109+
async def test_run_max_transactions_exceeded_sets_observation_meta(executor: SandboxExecutor):
110+
mock_source = """
111+
codebase.files[0].edit("a = 2")
112+
"""
113+
114+
code_to_exec = create_execute_function_from_codeblock(codeblock=mock_source)
115+
result = await executor.execute(code_to_exec, session_options=SessionOptions(max_transactions=0))
116+
117+
assert result.is_complete
118+
assert result.observation_meta == {"flags": [], "stop_codemod_exception_type": "MaxTransactionsExceeded", "threshold": 0}
119+
120+
121+
@pytest.mark.asyncio
122+
async def test_find_flag_groups_with_subdirectories(executor: SandboxExecutor):
123+
groups = await executor.find_flag_groups(
124+
code_flags=[
125+
CodeFlag(
126+
symbol=MagicMock(file=MagicMock(filepath="subdir1/file1.py")),
127+
message="message",
128+
),
129+
CodeFlag(
130+
symbol=MagicMock(file=MagicMock(filepath="subdir2/file1.py")),
131+
message="message",
132+
),
133+
CodeFlag(
134+
symbol=MagicMock(file=MagicMock(filepath="subdir3/file1.py")),
135+
message="message",
136+
),
137+
CodeFlag(
138+
symbol=MagicMock(file=MagicMock(filepath="subdir3/file2.py")),
139+
message="message",
140+
),
141+
],
142+
grouping_config=GroupingConfig(subdirectories=["subdir1", "subdir2"]),
143+
)
144+
assert len(groups) == 1
145+
assert len(groups[0].flags) == 2
146+
assert groups[0].flags[0].filepath == "subdir1/file1.py"
147+
assert groups[0].flags[1].filepath == "subdir2/file1.py"
148+
149+
150+
@pytest.mark.asyncio
151+
async def test_find_flag_groups_with_group_by(executor: SandboxExecutor):
152+
groups = await executor.find_flag_groups(
153+
code_flags=[
154+
CodeFlag(
155+
symbol=MagicMock(file=MagicMock(filepath="subdir1/file1.py")),
156+
message="message",
157+
),
158+
CodeFlag(
159+
symbol=MagicMock(file=MagicMock(filepath="subdir2/file1.py")),
160+
message="message",
161+
),
162+
CodeFlag(
163+
symbol=MagicMock(file=MagicMock(filepath="subdir3/file1.py")),
164+
message="message",
165+
),
166+
CodeFlag(
167+
symbol=MagicMock(file=MagicMock(filepath="subdir1/file1.py")),
168+
message="message",
169+
),
170+
],
171+
grouping_config=GroupingConfig(group_by=GroupBy.FILE),
172+
)
173+
assert len(groups) == 3
174+
assert groups[0].segment == "subdir1/file1.py"
175+
assert groups[1].segment == "subdir2/file1.py"
176+
assert groups[2].segment == "subdir3/file1.py"
177+
178+
assert len(groups[0].flags) == 2
179+
assert len(groups[1].flags) == 1
180+
assert len(groups[2].flags) == 1
181+
182+
183+
@pytest.mark.asyncio
184+
@pytest.mark.parametrize("codebase", [121], indirect=True)
185+
async def test_find_flag_groups_with_group_by_app(executor: SandboxExecutor):
186+
groups = await executor.find_flag_groups(
187+
code_flags=[
188+
CodeFlag(
189+
symbol=MagicMock(file=MagicMock(filepath="a/b/app1/test1.py")),
190+
message="message",
191+
),
192+
CodeFlag(
193+
symbol=MagicMock(file=MagicMock(filepath="a/b/app2/test1.py")),
194+
message="message",
195+
),
196+
CodeFlag(
197+
symbol=MagicMock(file=MagicMock(filepath="a/b/app3/test1.py")),
198+
message="message",
199+
),
200+
CodeFlag(
201+
symbol=MagicMock(file=MagicMock(filepath="a/b/app2/test2.py")),
202+
message="message",
203+
),
204+
],
205+
grouping_config=GroupingConfig(group_by=GroupBy.APP),
206+
)
207+
count_by_segment = {group.segment: len(group.flags) for group in groups}
208+
assert count_by_segment == {"a/b/app1": 1, "a/b/app2": 2, "a/b/app3": 1}
209+
210+
211+
@pytest.mark.skip(reason="TODO: add max_prs as part of find_flag_groups")
212+
@pytest.mark.asyncio
213+
async def test_find_flag_groups_with_max_prs(executor: SandboxExecutor):
214+
groups = await executor.find_flag_groups(
215+
code_flags=[
216+
CodeFlag(
217+
symbol=MagicMock(file=MagicMock(filepath="subdir1/file1.py")),
218+
message="message",
219+
),
220+
CodeFlag(
221+
symbol=MagicMock(file=MagicMock(filepath="subdir2/file1.py")),
222+
message="message",
223+
),
224+
],
225+
grouping_config=GroupingConfig(group_by=GroupBy.FILE, max_prs=0),
226+
)
227+
assert len(groups) == 0

0 commit comments

Comments
 (0)