Stop quick setup on an unreadable reply and reset controls for a new draft
A 200 or 201 with a body that does not name the expected step as done now stops at that step as an unknown outcome, with no further writes. Starting a new draft re-enables the preset, thinking and run controls and clears the risk checkbox. Browser regression covers both (Sol review P1, P2).
This commit is contained in:
1 parent
c09570d696
commit
653b3e2e75
2 files changed
+38
-5
No files matched your search
@@ -226,6 +226,8 @@
|
||||
if (r.status !== 200) { banner(r.body.message || `Presets could not be read (${r.body.error || r.status}).`); return; }
|
||||
presets = r.body.presets;
|
||||
st.id = id; st.mode = 'create'; editorDirty = true;
|
||||
// A new draft starts with fresh controls; an earlier run locked them only for its own draft.
|
||||
$('org-preset').disabled = false; $('org-thinking').disabled = false; $('org-quick-run').disabled = !canWrite; $('org-risk-accept').checked = false;
|
||||
$('org-preset').innerHTML = presets.map(p => `<option value="${txt(p.name)}"${p.available.ok ? '' : ' disabled'}>${txt(p.label)}${p.available.ok ? '' : ` — ${txt(p.available.reason)}`}</option>`).join('');
|
||||
$('org-preset').value = presets.find(p => p.available.ok)?.name || '';
|
||||
updatePreset();
|
||||
@@ -274,17 +276,21 @@
|
||||
try {
|
||||
const template = await selectedTemplate();
|
||||
if (template.status !== 200) throw Error(template.body.message || 'Could not load the template.');
|
||||
if (!template.body.doc || typeof template.body.doc !== 'object') throw Error('The template reply could not be read.');
|
||||
const doc = template.body.doc; doc.projects = { [project]: {} };
|
||||
const requests = [
|
||||
['/api/orgs', { id, doc }],
|
||||
[`/api/orgs/${enc(id)}/setup/queue`, local],
|
||||
[`/api/orgs/${enc(id)}/setup/project/${enc(project)}`, remote ? { mode: 'remote', remote } : local],
|
||||
[`/api/orgs/${enc(id)}/setup/bus`, {}],
|
||||
['/api/orgs', { id, doc }, 'create'],
|
||||
[`/api/orgs/${enc(id)}/setup/queue`, local, 'queue'],
|
||||
[`/api/orgs/${enc(id)}/setup/project/${enc(project)}`, remote ? { mode: 'remote', remote } : local, 'project'],
|
||||
[`/api/orgs/${enc(id)}/setup/bus`, {}, 'bus'],
|
||||
];
|
||||
for (current = 0; current < requests.length; current++) {
|
||||
const card = list.children[current]; card.querySelector('.org-pill').textContent = 'In progress';
|
||||
const result = await api('POST', ...requests[current]);
|
||||
const [url, body, step] = requests[current];
|
||||
const result = await api('POST', url, body);
|
||||
if (result.status !== 200 && result.status !== 201) throw Error(result.body.message || result.body.errors?.map(e => e.message).join(' ') || `Refused (${result.body.error || result.status}).`);
|
||||
// A success status alone is not proof: an unreadable or unexpected reply leaves the outcome unknown, so stop here.
|
||||
if (result.body.outcome !== 'done' || result.body.step !== step) throw Error('The reply could not be read, so this step\'s outcome is unknown.');
|
||||
card.querySelector('.org-pill').textContent = 'Done'; card.classList.add('org-step-done');
|
||||
if (result.body.verb) card.querySelector('code').textContent = result.body.verb;
|
||||
}
|
||||
|
||||
@@ -52,3 +52,30 @@ for (const shouldFail of [true, false]) test(`quick setup creates real repositor
|
||||
assert.ok(existsSync(join(dataRoot, 'org-runtime/alpha/checkout/.git')));
|
||||
assert.ok(existsSync(join(dataRoot, 'org-runtime/alpha/projects/work/.git')));
|
||||
});
|
||||
|
||||
test('an unreadable success reply stops quick setup and a new draft gets fresh controls', { timeout: 45000 }, async t => {
|
||||
const admin = createOrgAdmin({ service, registry: { listOrgs: async () => [] }, context: async () => ({ configDir: '/cfg', dataRoot: '/data', instance: 'test' }) });
|
||||
const server = await startServer({ port: 0, sso: createDevOwner(), orgAdmin: admin, send: async () => ({}), board: 'http://127.0.0.1:9', log: () => {} }); t.after(() => close(server));
|
||||
const b = await browser({ machine: false }); t.after(() => b.close());
|
||||
const wait = expression => b.evaluate(`(async()=>{for(let i=0;i<300;i++){if(${expression})return;await new Promise(r=>setTimeout(r,30))}throw Error(${JSON.stringify(expression)})})()`);
|
||||
await b.navigate(`http://127.0.0.1:${server.address().port}/#/settings/organizations`);
|
||||
await wait('document.querySelector("#org-new-form [data-write]") && !document.querySelector("#org-new-form [data-write]").disabled');
|
||||
// The create reply is a truncated 201; every POST is recorded so the test can prove nothing follows it.
|
||||
await b.evaluate(`window.__posts=[];const real=window.fetch;window.fetch=(url,init={})=>{if((init.method||'GET')==='POST'){window.__posts.push(String(url));if(String(url)==='/api/orgs')return Promise.resolve(new Response('{"outcome":',{status:201,headers:{'content-type':'application/json'}}));}return real(url,init)}`);
|
||||
const draft = async id => {
|
||||
await b.evaluate(`document.querySelector('#org-new-id').value=${JSON.stringify(id)};document.querySelector('#org-new-form [data-write]').click()`);
|
||||
await wait(`document.querySelector("#org-quick-open") && !document.querySelector("#org-paths").hidden && !document.querySelector("#org-quick-open").disabled`);
|
||||
await b.evaluate(`document.querySelector('#org-quick-open').click()`);
|
||||
await wait('!document.querySelector("#org-quick").hidden');
|
||||
};
|
||||
const controls = () => b.evaluate('["#org-preset","#org-thinking","#org-quick-run"].map(s=>document.querySelector(s).disabled)');
|
||||
await draft('alpha');
|
||||
await b.evaluate(`document.querySelector('#org-risk-accept').click();document.querySelector('#org-quick-run').click()`);
|
||||
await wait(`document.querySelector('[data-quick-step="0"] .org-pill')?.textContent === 'Stopped'`);
|
||||
assert.match(await b.evaluate(`document.querySelector('[data-quick-step="0"]').textContent`), /outcome is unknown/);
|
||||
assert.deepEqual(await b.evaluate('window.__posts'), ['/api/orgs']);
|
||||
assert.deepEqual(await controls(), [true, true, true]);
|
||||
await draft('beta');
|
||||
assert.deepEqual(await controls(), [false, false, false]);
|
||||
assert.equal(await b.evaluate(`document.querySelector('#org-risk-accept').checked`), false);
|
||||
});
|
||||
Reference in new issue
Block a user