mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 05:43:14 +00:00
Applies the plan's C5 step: git mv every test per the domain-reorg
move-map (movemap-m50.txt) into tests/{agent,analyzer,auth,dashboard,
github,middleware,models,reviewer,sandbox,slack,tools,webhooks}/, plus
the 13 fork-only placements from the scoping report §2c (Atlassian
webhook tests -> tests/webhooks/, test_atlassian_connect.py and
test_auth_error_leak.py -> tests/auth/, jira/confluence util tests ->
tests/tools/, test_repo_binding_isolation.py -> tests/sandbox/,
bot-identity/autofix tests -> tests/github/).
Path-only move: the only content edits are parents[1] -> parents[2]
fixes in test_e2b_integration.py and test_daytona_integration.py,
required because their __file__-relative ROOT path gained one more
directory level in the move.
Monkeypatch retargets for these files were already completed in C4;
none remained outstanding here.
155 lines
5.4 KiB
Python
155 lines
5.4 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from agent import server
|
|
from agent.dashboard import routes
|
|
from agent.dashboard.agent_instructions import (
|
|
create_agent_instructions,
|
|
get_repo_agent_instructions,
|
|
set_agent_instructions,
|
|
)
|
|
from agent.prompt import construct_system_prompt
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_repo_agent_instructions_returns_trimmed_text() -> None:
|
|
with patch(
|
|
"agent.dashboard.agent_instructions.get_agent_instructions",
|
|
new_callable=AsyncMock,
|
|
return_value={"instructions": " Always run mypy.\n"},
|
|
):
|
|
result = await get_repo_agent_instructions("acme", "repo")
|
|
assert result == "Always run mypy."
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_repo_agent_instructions_returns_none_when_empty() -> None:
|
|
with patch(
|
|
"agent.dashboard.agent_instructions.get_agent_instructions",
|
|
new_callable=AsyncMock,
|
|
return_value={"instructions": " "},
|
|
):
|
|
result = await get_repo_agent_instructions("acme", "repo")
|
|
assert result is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_agent_instructions_puts_new_record() -> None:
|
|
mock_put = AsyncMock()
|
|
with (
|
|
patch(
|
|
"agent.dashboard.agent_instructions._get_value",
|
|
new_callable=AsyncMock,
|
|
return_value=None,
|
|
),
|
|
patch("agent.dashboard.agent_instructions._client") as mock_client,
|
|
):
|
|
mock_client.return_value.store.put_item = mock_put
|
|
record = await create_agent_instructions("acme/repo", "octo")
|
|
assert record["full_name"] == "acme/repo"
|
|
assert record["instructions"] == ""
|
|
mock_put.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_set_agent_instructions_updates_store() -> None:
|
|
mock_put = AsyncMock()
|
|
with (
|
|
patch(
|
|
"agent.dashboard.agent_instructions.get_agent_instructions",
|
|
new_callable=AsyncMock,
|
|
return_value={"full_name": "acme/repo", "instructions": ""},
|
|
),
|
|
patch("agent.dashboard.agent_instructions._client") as mock_client,
|
|
):
|
|
mock_client.return_value.store.put_item = mock_put
|
|
record = await set_agent_instructions("acme/repo", "Use direct tone.")
|
|
assert record["instructions"] == "Use direct tone."
|
|
mock_put.assert_awaited_once()
|
|
|
|
|
|
def test_construct_system_prompt_appends_repo_instructions() -> None:
|
|
prompt = construct_system_prompt(
|
|
working_dir="/work",
|
|
repo_custom_instructions="Prefer pytest over unittest.",
|
|
)
|
|
assert "Repository-specific Custom Instructions" in prompt
|
|
assert "Prefer pytest over unittest." in prompt
|
|
|
|
|
|
def test_construct_system_prompt_without_repo_instructions() -> None:
|
|
prompt = construct_system_prompt(working_dir="/work")
|
|
assert "Repository-specific Custom Instructions" not in prompt
|
|
|
|
|
|
def test_resolve_repo_custom_instructions_returns_none_without_repo() -> None:
|
|
result = asyncio.run(server._resolve_repo_custom_instructions(None))
|
|
assert result is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_agent_instructions_filters_inaccessible_repos(monkeypatch) -> None:
|
|
monkeypatch.setattr(
|
|
routes,
|
|
"list_agent_instructions",
|
|
AsyncMock(
|
|
return_value=[
|
|
{"full_name": "acme/visible", "instructions": "visible"},
|
|
{"full_name": "acme/private", "instructions": "private"},
|
|
]
|
|
),
|
|
)
|
|
|
|
async def fake_require_repo_access_for_user(login: str, full_name: str) -> str:
|
|
if full_name == "acme/private":
|
|
raise HTTPException(403, "no access")
|
|
return "token"
|
|
|
|
monkeypatch.setattr(routes, "require_repo_access_for_user", fake_require_repo_access_for_user)
|
|
|
|
result = await routes.api_list_agent_instructions(session={"sub": "octocat"})
|
|
|
|
assert result == [{"full_name": "acme/visible", "instructions": "visible"}]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_agent_instructions_requires_repo_access(monkeypatch) -> None:
|
|
require_access = AsyncMock(return_value="token")
|
|
monkeypatch.setattr(
|
|
routes,
|
|
"get_agent_instructions",
|
|
AsyncMock(return_value={"full_name": "acme/repo", "instructions": "rules"}),
|
|
)
|
|
monkeypatch.setattr(routes, "require_repo_access_for_user", require_access)
|
|
|
|
result = await routes.api_get_agent_instructions(
|
|
"https://github.com/acme/repo", session={"sub": "octocat"}
|
|
)
|
|
|
|
assert result == {"full_name": "acme/repo", "instructions": "rules"}
|
|
require_access.assert_awaited_once_with("octocat", "acme/repo")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_agent_instructions_requires_repo_access_before_delete(monkeypatch) -> None:
|
|
delete_instructions = AsyncMock()
|
|
get_instructions = AsyncMock(return_value={"full_name": "acme/repo", "instructions": "rules"})
|
|
monkeypatch.setattr(routes, "get_agent_instructions", get_instructions)
|
|
monkeypatch.setattr(
|
|
routes,
|
|
"require_repo_access_for_user",
|
|
AsyncMock(side_effect=HTTPException(403, "no access")),
|
|
)
|
|
monkeypatch.setattr(routes, "delete_agent_instructions", delete_instructions)
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
await routes.api_delete_agent_instructions("acme/repo", session={"sub": "octocat"})
|
|
|
|
assert exc.value.status_code == 403
|
|
get_instructions.assert_not_awaited()
|
|
delete_instructions.assert_not_awaited()
|