Source-candidate: poe d9a564e6d20f5f0fd75ce45b12cbbf8bb4c9d568\nSource-candidate: magica38ba5762b06716db1dee00d0afec162656488f5c\nValidated321whole-file phase plus whole bus1349/model-proxy100/webui490; final gate tooling checks pending, not landed.
78 lines
6.1 KiB
JavaScript
78 lines
6.1 KiB
JavaScript
// Native page behavior. HTTP authorization is covered by proxy-page.test.mjs;
|
|
// this stand-in serves the real page/script and records only route names/counts.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import http from 'node:http';
|
|
import { readFileSync } from 'node:fs';
|
|
import { browser } from './browser.mjs';
|
|
async function setup(t, readonly = false) {
|
|
const state = { routes: [], loginStatus: 'wait', receivedKey: false, release: null, priority: 0 };
|
|
const server = http.createServer(async (req, res) => {
|
|
const send = value => { res.setHeader('content-type', 'application/json'); res.end(JSON.stringify(value)); };
|
|
if (req.url === '/api/config') return send({ send: !readonly });
|
|
if (req.url.startsWith('/api/proxy/')) {
|
|
state.routes.push(req.url);
|
|
const chunks = []; for await (const c of req) chunks.push(c);
|
|
const args = chunks.length ? JSON.parse(Buffer.concat(chunks)) : {};
|
|
if (req.url.endsWith('/accounts')) return send({ accounts: [{ accountId: 'a'.repeat(12), provider: 'claude', displayName: '<img src=x onerror="window.injected=true">', status: 'ready', priority: state.priority }] });
|
|
if (req.url.endsWith('/keys')) return send({ keys: [{ keyId: 'b'.repeat(12), management: 'sentinel', agent: null, issuedAt: null, state: null }] });
|
|
if (req.url.endsWith('/apikey/add')) {
|
|
state.receivedKey = args.key === 'p4-browser-key';
|
|
await new Promise(r => { state.release = r; }); return send({ added: true });
|
|
}
|
|
if (req.url.endsWith('/login/start')) return send({ url: 'https://claude.ai/oauth/authorize?state=p4-state', state: 'p4-state' });
|
|
if (req.url.endsWith('/login/status')) return send({ status: state.loginStatus });
|
|
if (req.url.endsWith('/login/submit')) return send({ status: 'wait' });
|
|
if (req.url.endsWith('/login/cancel')) return send({ status: 'cancelled' });
|
|
if (req.url.endsWith('/priority/set')) { state.priority = args.priority; return send({ priority: args.priority }); }
|
|
}
|
|
if (req.url === '/proxy') { res.setHeader('content-type', 'text/html'); return res.end(readFileSync(new URL('../src/public/proxy.html', import.meta.url))); }
|
|
if (req.url === '/proxy.js') { res.setHeader('content-type', 'text/javascript'); return res.end(readFileSync(new URL('../src/public/proxy.js', import.meta.url))); }
|
|
res.statusCode = 404; res.end();
|
|
});
|
|
await new Promise(r => server.listen(0, '127.0.0.1', r));
|
|
t.after(() => { state.release?.(); return new Promise(r => server.close(r)); });
|
|
const b = await browser({ machine: false }); t.after(() => b.close());
|
|
await b.call('Page.addScriptToEvaluateOnNewDocument', { source: "window.openFeatures=null;window.open=(url,target,features)=>{window.openFeatures=features;return null;};" });
|
|
await b.navigate(`http://127.0.0.1:${server.address().port}/proxy`);
|
|
const wait = expression => b.evaluate(`(async()=>{for(let n=0;n<200;n++){if(${expression})return true;await new Promise(r=>setTimeout(r,25));}throw Error('proxy UI condition timed out');})()`);
|
|
await wait('document.querySelector("#proxy-message").textContent.includes("loaded")');
|
|
return { b, state, wait };
|
|
}
|
|
test('proxy browser clears write-only key immediately, sends once and renders data as text', { timeout: 60000 }, async t => {
|
|
const { b, state, wait } = await setup(t);
|
|
assert.equal(await b.evaluate('window.injected===true'), false);
|
|
assert.equal(await b.evaluate('document.querySelectorAll("#proxy-accounts img").length'), 0);
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-apikey input").type'), 'password');
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-apikey input").autocomplete'), 'off');
|
|
await b.evaluate('document.querySelector("#proxy-apikey input").value="p4-browser-key";document.querySelector("#proxy-apikey").requestSubmit();true');
|
|
await wait('document.querySelector("#proxy-apikey input").value===""');
|
|
for (let n = 0; n < 100 && !state.release; n++) await new Promise(r => setTimeout(r, 20));
|
|
assert.equal(state.receivedKey, true);
|
|
state.release(); await wait('document.querySelector("#proxy-message").textContent.includes("added")');
|
|
assert.equal(state.routes.filter(r => r.endsWith('/apikey/add')).length, 1);
|
|
assert.equal(await b.evaluate('localStorage.length+sessionStorage.length'), 0);
|
|
assert.equal(await b.evaluate('document.body.textContent.includes("p4-browser-key")'), false);
|
|
});
|
|
test('proxy browser OAuth opens safely, callback wait stays pending, status ok completes', { timeout: 60000 }, async t => {
|
|
const { b, state, wait } = await setup(t);
|
|
await b.evaluate('document.querySelector("#proxy-login-start").requestSubmit();true');
|
|
await wait('!document.querySelector("#proxy-authorize").hidden');
|
|
assert.equal(await b.evaluate('window.openFeatures'), 'noopener,noreferrer');
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-authorize").rel'), 'noopener noreferrer');
|
|
await b.evaluate('document.querySelector("#proxy-login-submit input").value="http://localhost/callback?state=p4-state&code=p4-code";document.querySelector("#proxy-login-submit").requestSubmit();true');
|
|
await wait('document.querySelector("#proxy-login-message").textContent.includes("Waiting")');
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-login-message").textContent.includes("signed in")'), false);
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-login-submit input").value===""'), true);
|
|
state.loginStatus = 'ok';
|
|
await wait('document.querySelector("#proxy-login-message").textContent==="Account signed in."');
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-authorize").hasAttribute("href")'), false);
|
|
assert.equal(await b.evaluate('localStorage.length+sessionStorage.length'), 0);
|
|
});
|
|
test('proxy browser read-only disables account changes', { timeout: 60000 }, async t => {
|
|
const { b, state } = await setup(t, true);
|
|
assert.equal(await b.evaluate('[...document.querySelectorAll("[data-write]")].every(e=>e.disabled)'), true);
|
|
assert.equal(await b.evaluate('document.querySelector("#proxy-accounts input").disabled'), true);
|
|
assert.equal(state.routes.some(r => r.endsWith('/apikey/add') || r.endsWith('/login/start')), false);
|
|
});
|