feat: revise pull review context from mobile (Closes #1332)
This commit is contained in:
1 parent
8c4969cc44
commit
8285b39e06
7 files changed
+389
-3
No files matched your search
@@ -7424,6 +7424,80 @@ Promise.all([first, duplicate]).then(async comments => {{
|
||||
assert len(output["comments"]) == 2 and len(output["merges"]) == 2
|
||||
|
||||
|
||||
def test_pull_sheet_preserves_context_draft_until_confirmed_update():
|
||||
script = f"""
|
||||
const createPullSheet = require({json.dumps(str(PULL_SHEET))});
|
||||
const values = new Map();
|
||||
const storage = {{getItem:k => values.get(k) || null, setItem:(k,v) => values.set(k,v), removeItem:k => values.delete(k)}};
|
||||
const calls = [];
|
||||
let fail = true;
|
||||
const controller = createPullSheet({{
|
||||
storage,
|
||||
fetchJson: async (url, options={{}}) => {{
|
||||
const body = JSON.parse(options.body);
|
||||
calls.push({{url, method:options.method, body}});
|
||||
if (fail) {{ fail = false; throw new Error('network down'); }}
|
||||
return {{number:7,title:body.title,body:body.body,head_sha:body.expected_head_sha,state:'open'}};
|
||||
}},
|
||||
}});
|
||||
const item = {{repository:'stackchain/api',number:7}};
|
||||
const draft = {{title:'Clear review brief',body:'Review mobile handoff.',expectedHeadSha:'abc1234'}};
|
||||
(async () => {{
|
||||
await controller.updateContent(item, draft).catch(() => null);
|
||||
const kept = controller.loadEditDraft(item);
|
||||
const result = await controller.updateContent(item, draft);
|
||||
process.stdout.write(JSON.stringify({{kept,result,after:controller.loadEditDraft(item),calls}}));
|
||||
}})().catch(error => {{ console.error(error); process.exit(1); }});
|
||||
"""
|
||||
result = subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True)
|
||||
output = json.loads(result.stdout)
|
||||
assert output["kept"] == {
|
||||
"title": "Clear review brief",
|
||||
"body": "Review mobile handoff.",
|
||||
"expectedHeadSha": "abc1234",
|
||||
}
|
||||
assert output["after"] is None
|
||||
assert output["result"]["head_sha"] == "abc1234"
|
||||
assert output["calls"] == [
|
||||
{
|
||||
"url": "api/v1/repos/stackchain/api/pulls/7/content",
|
||||
"method": "PATCH",
|
||||
"body": {
|
||||
"title": "Clear review brief",
|
||||
"body": "Review mobile handoff.",
|
||||
"expected_head_sha": "abc1234",
|
||||
},
|
||||
},
|
||||
{
|
||||
"url": "api/v1/repos/stackchain/api/pulls/7/content",
|
||||
"method": "PATCH",
|
||||
"body": {
|
||||
"title": "Clear review brief",
|
||||
"body": "Review mobile handoff.",
|
||||
"expected_head_sha": "abc1234",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pull_overview_exposes_author_only_context_editor_with_retry_safe_status():
|
||||
html = (Path(__file__).parents[1] / "frontend" / "index.html").read_text()
|
||||
source = await dashboard()
|
||||
pull_source = PULL_SHEET.read_text()
|
||||
|
||||
assert 'id="edit-pull-content"' in html
|
||||
assert 'id="pull-edit-form"' in html
|
||||
assert 'id="pull-edit-title"' in html
|
||||
assert 'id="pull-edit-body"' in html
|
||||
assert 'id="save-pull-content"' in html
|
||||
assert "detail.author === getLogin()" in pull_source
|
||||
assert "controller.loadEditDraft(item)" in pull_source
|
||||
assert "controller.updateContent(item" in pull_source
|
||||
assert "Your change is still here; retry." in pull_source
|
||||
assert "controller.setReviewDetail(detail)" in pull_source
|
||||
|
||||
|
||||
def test_pull_sheet_removes_one_pull_from_snapshot_without_touching_other_work():
|
||||
script = f"""
|
||||
const createPullSheet = require({json.dumps(str(PULL_SHEET))});
|
||||
@@ -7463,6 +7537,9 @@ const elements = new Map([
|
||||
['#load-pull-handoff', {{disabled:true}}],
|
||||
['#release-pull', {{disabled:true,textContent:''}}],
|
||||
['#pull-handoff-status', {{textContent:''}}],
|
||||
['#edit-pull-content', {{hidden:false}}],
|
||||
['#pull-edit-form', {{hidden:false}}],
|
||||
['#pull-edit-status', {{textContent:'stale'}}],
|
||||
]);
|
||||
const select = {{textContent:'stale', appendChild:node => children.push(node)}};
|
||||
const doc = {{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@@ -7,6 +8,90 @@ from src import gitea_proxy, main
|
||||
from src.security_event_store import SecurityEventStoreError
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pull_author_can_update_assigned_open_pull_context(monkeypatch):
|
||||
calls = []
|
||||
|
||||
async def update(repository, number, title, body, expected_head_sha):
|
||||
calls.append((repository, number, title, body, expected_head_sha))
|
||||
return {
|
||||
"repository": repository,
|
||||
"number": number,
|
||||
"title": title,
|
||||
"body": body,
|
||||
"head_sha": expected_head_sha,
|
||||
"state": "open",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
main.gitea_proxy, "update_authored_assigned_pull", update, raising=False
|
||||
)
|
||||
transport = httpx.ASGITransport(app=main.app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
response = await client.patch(
|
||||
"/api/v1/repos/stackchain/api/pulls/7/content",
|
||||
json={
|
||||
"title": "Clarify mobile handoff",
|
||||
"body": "Explain the reviewer path.",
|
||||
"expected_head_sha": "abc1234",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["title"] == "Clarify mobile handoff"
|
||||
assert calls == [
|
||||
(
|
||||
"stackchain/api",
|
||||
7,
|
||||
"Clarify mobile handoff",
|
||||
"Explain the reviewer path.",
|
||||
"abc1234",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_gitea_pull_context_update_requires_author_assignment_and_matching_head():
|
||||
requests = []
|
||||
|
||||
async def handler(request):
|
||||
requests.append((request.method, request.url.path, request.content))
|
||||
if request.url.path == "/api/v1/user":
|
||||
return httpx.Response(200, json={"login": "alex"})
|
||||
if request.url.path == "/api/v1/repos/stackchain/api/pulls/7" and request.method == "GET":
|
||||
return httpx.Response(200, json={
|
||||
"number": 7, "title": "Old context", "body": "Old body", "state": "open",
|
||||
"merged": False, "user": {"login": "alex"}, "assignees": [{"login": "alex"}],
|
||||
"head": {"sha": "abc1234"},
|
||||
"html_url": "https://forge.example/stackchain/api/pulls/7",
|
||||
})
|
||||
if request.url.path == "/api/v1/repos/stackchain/api/pulls/7" and request.method == "PATCH":
|
||||
assert json.loads(request.content) == {
|
||||
"title": "Clear review brief", "body": "Review the mobile handoff."
|
||||
}
|
||||
return httpx.Response(200, json={
|
||||
"number": 7, "title": "Clear review brief", "body": "Review the mobile handoff.",
|
||||
"state": "open", "head": {"sha": "abc1234"},
|
||||
"html_url": "https://forge.example/stackchain/api/pulls/7",
|
||||
})
|
||||
raise AssertionError(f"unexpected request: {request.method} {request.url.path}")
|
||||
|
||||
gitea_proxy.start_client(transport=httpx.MockTransport(handler))
|
||||
try:
|
||||
result = await gitea_proxy.update_authored_assigned_pull(
|
||||
"stackchain/api", 7, "Clear review brief", "Review the mobile handoff.", "abc1234"
|
||||
)
|
||||
finally:
|
||||
await gitea_proxy.stop_client()
|
||||
|
||||
assert result["title"] == "Clear review brief"
|
||||
assert [request[:2] for request in requests] == [
|
||||
("GET", "/api/v1/user"),
|
||||
("GET", "/api/v1/repos/stackchain/api/pulls/7"),
|
||||
("PATCH", "/api/v1/repos/stackchain/api/pulls/7"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_assigned_pull_detail_reports_completion_state(monkeypatch):
|
||||
async def assigned(repository, number):
|
||||
|
||||
Reference in new issue
Block a user