feat: create follow-up and continue updates (Closes #665)
This commit is contained in:
1 parent
681e10c7db
commit
2553e7121b
6 files changed
+175
-8
No files matched your search
@@ -58,6 +58,87 @@ process.stdout.write(JSON.stringify(controller.draft({{
|
||||
assert len(output["body"]) <= 9500
|
||||
|
||||
|
||||
def test_follow_up_continuation_is_account_bound_and_survives_reload():
|
||||
script = f"""
|
||||
const createFollowUp = require({json.dumps(str(UPDATE_FOLLOW_UP))});
|
||||
const values = new Map();
|
||||
const storage = {{
|
||||
getItem:key => values.has(key) ? values.get(key) : null,
|
||||
setItem:(key,value) => values.set(key,value), removeItem:key => values.delete(key)
|
||||
}};
|
||||
let login = 'timmy';
|
||||
const first = createFollowUp({{storage, getLogin:()=>login}});
|
||||
const staged = first.stageSource({{
|
||||
notification_id: 42, repository:'stackchain/stackchain-dashboard', number:665,
|
||||
title:'Unread follow-up source', kind:'update', has_update:true
|
||||
}});
|
||||
const restored = createFollowUp({{storage, getLogin:()=>login}}).source();
|
||||
login = 'alexander';
|
||||
const isolated = createFollowUp({{storage, getLogin:()=>login}}).source();
|
||||
process.stdout.write(JSON.stringify({{staged, restored, isolated}}));
|
||||
"""
|
||||
output = run_node(script)
|
||||
assert output["staged"]["notification_id"] == 42
|
||||
assert output["restored"] == output["staged"]
|
||||
assert output["isolated"] is None
|
||||
|
||||
|
||||
def test_follow_up_completion_admits_issue_before_durable_read_and_advances_once():
|
||||
script = f"""
|
||||
const createFollowUp = require({json.dumps(str(UPDATE_FOLLOW_UP))});
|
||||
const values = new Map();
|
||||
const storage = {{
|
||||
getItem:key => values.has(key) ? values.get(key) : null,
|
||||
setItem:(key,value) => values.set(key,value), removeItem:key => values.delete(key)
|
||||
}};
|
||||
const order = [];
|
||||
const controller = createFollowUp({{storage, getLogin:()=> 'timmy'}});
|
||||
controller.stageSource({{notification_id:42, repository:'o/r', number:7, title:'Source'}});
|
||||
(async () => {{
|
||||
const result = await controller.complete({{
|
||||
admit:async()=>{{ order.push('issue'); return {{item:{{id:'issue:1'}}}}; }},
|
||||
queueRead:async id=>{{ order.push('read:' + id); return {{item:{{id:'read:42'}}}}; }},
|
||||
advance:async source=>{{ order.push('next:' + source.notification_id); return true; }},
|
||||
}});
|
||||
process.stdout.write(JSON.stringify({{order, result, pending:controller.source()}}));
|
||||
}})();
|
||||
"""
|
||||
output = run_node(script)
|
||||
assert output == {
|
||||
"order": ["issue", "read:42", "next:42"],
|
||||
"result": {"admission": {"item": {"id": "issue:1"}}, "advanced": True},
|
||||
"pending": None,
|
||||
}
|
||||
|
||||
|
||||
def test_follow_up_completion_preserves_source_when_issue_admission_fails():
|
||||
script = f"""
|
||||
const createFollowUp = require({json.dumps(str(UPDATE_FOLLOW_UP))});
|
||||
const values = new Map();
|
||||
const storage = {{
|
||||
getItem:key => values.has(key) ? values.get(key) : null,
|
||||
setItem:(key,value) => values.set(key,value), removeItem:key => values.delete(key)
|
||||
}};
|
||||
const order = [];
|
||||
const controller = createFollowUp({{storage, getLogin:()=> 'timmy'}});
|
||||
controller.stageSource({{notification_id:42, repository:'o/r', number:7, title:'Source'}});
|
||||
(async () => {{
|
||||
try {{
|
||||
await controller.complete({{
|
||||
admit:async()=>{{ order.push('issue'); throw new Error('disk full'); }},
|
||||
queueRead:async()=>order.push('read'), advance:async()=>order.push('next'),
|
||||
}});
|
||||
}} catch (error) {{
|
||||
process.stdout.write(JSON.stringify({{order, error:error.message, pending:controller.source()}}));
|
||||
}}
|
||||
}})();
|
||||
"""
|
||||
output = run_node(script)
|
||||
assert output["order"] == ["issue"]
|
||||
assert output["error"] == "disk full"
|
||||
assert output["pending"]["notification_id"] == 42
|
||||
|
||||
|
||||
def test_follow_up_staging_never_silently_overwrites_an_existing_capture():
|
||||
script = f"""
|
||||
const createCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
|
||||
@@ -95,10 +176,23 @@ process.stdout.write(JSON.stringify({{state, before, accepted, pending:capture.p
|
||||
async def test_update_sheet_wires_phone_safe_follow_up_without_marking_read():
|
||||
html = await dashboard()
|
||||
|
||||
assert '<script src="static/update-follow-up.js"></script>' in html
|
||||
assert 'name="stackchain-feature-issue-capture"' in html
|
||||
assert 'id="create-update-follow-up"' in html
|
||||
assert '>Create follow-up</button>' in html
|
||||
assert "issueCapture.stageFollowUp(updateFollowUp.draft(selectedUpdateDetail))" in html
|
||||
assert "qs('#create-update-follow-up').addEventListener('click'" in html
|
||||
assert "markNotificationRead" not in html.split("qs('#create-update-follow-up').addEventListener('click'", 1)[1].split("});", 1)[0]
|
||||
assert '.update-sheet-actions button, .update-sheet-actions a { min-height:44px;' in html
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_follow_up_capture_offers_durable_create_and_next_flow():
|
||||
html = await dashboard()
|
||||
|
||||
assert 'id="create-follow-up-next"' in html
|
||||
assert '>Create follow-up & next</button>' in html
|
||||
assert "updateFollowUp.stageSource(source.item)" in html
|
||||
assert "event.submitter?.id === 'create-follow-up-next'" in html
|
||||
assert "queueRead: notificationId => notificationReadOutbox.enqueueDurably(notificationId)" in html
|
||||
assert "advance: source => notificationReader.acceptReadAndNext(lastMyWork, source)" in html
|
||||
assert ".create-issue-actions button { min-height:44px;" in html
|
||||
Reference in new issue
Block a user