"use strict"; const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const http = require('node:http'); const { once } = require('node:events'); const { runtimePaths, validateRuntime, runtimeEnvironment } = require('../src/main/diagnostics/httpRuntime'); const { curlRequest, HttpDiagnostics } = require('../src/main/diagnostics/httpDiagnostics'); const { parseTarget, normalizeOptions } = require('../src/main/diagnostics/common'); test('bundled HTTP component detects missing, modified and incompatible files', t => { const root=fs.mkdtempSync(path.join(os.tmpdir(),'chj-runtime-test-'));t.after(()=>fs.rmSync(root,{recursive:true,force:true})); const options={resourcesPath:root};assert.throws(()=>validateRuntime(options),{code:'HTTP_RUNTIME_MISSING'}); const original=runtimePaths(),target=runtimePaths(options);fs.cpSync(original.root,target.root,{recursive:true});assert.equal(validateRuntime(options).arch,process.arch); fs.appendFileSync(target.caPath,'tampered');assert.throws(()=>validateRuntime(options),{code:'HTTP_RUNTIME_INVALID'}); fs.copyFileSync(original.caPath,target.caPath);const mf=path.join(target.root,'manifest.json');const m=JSON.parse(fs.readFileSync(mf));m.arch='wrong';fs.writeFileSync(mf,JSON.stringify(m));assert.throws(()=>validateRuntime(options),{code:'HTTP_RUNTIME_INVALID'}); }); test('HTTP subprocess excludes proxy, key logging, trust and library injection overrides',()=>{ for(const key of ['CURL_CA_BUNDLE','SSL_CERT_FILE','SSLKEYLOGFILE','HTTPS_PROXY','LD_PRELOAD','DYLD_INSERT_LIBRARIES','NODE_OPTIONS','HOME','PATH'])assert.equal(runtimeEnvironment()[key],undefined); }); test('bundled client measures real HTTP/1.1, validates redirects, bounds output and cancels', async t=>{ const sockets=new Set();const server=http.createServer((req,res)=>{ if(req.url==='/redirect'){res.writeHead(302,{location:'/ok'});return res.end();} if(req.url==='/loop'){res.writeHead(302,{location:'/loop'});return res.end();} if(req.url==='/large')return res.end(Buffer.alloc(2*1024*1024+1)); if(req.url==='/hang')return; res.setHeader('set-cookie','private=secret');res.end('actual body'); });server.on('connection',s=>{sockets.add(s);s.on('close',()=>sockets.delete(s));});server.listen(0,'127.0.0.1');await once(server,'listening');t.after(async()=>{sockets.forEach(s=>s.destroy());await new Promise(r=>server.close(r));}); const base=`http://localhost:${server.address().port}`,settings={timeoutMs:3000},signal=new AbortController().signal; const result=await curlRequest(new URL(base),'127.0.0.1','1.1',settings,signal);assert.equal(result.status,'success');assert.equal(result.downloadedBytes,11);assert.equal(result.headers['set-cookie'],'[redacted]');assert.equal(result.reused,false);assert.ok(result.capability.http3); const service=new HttpDiagnostics({resolve:async()=>[{address:'127.0.0.1',family:4}]});const options=normalizeOptions({target:base+'/redirect',tools:['http'],protocols:['1.1']});const resources=service.resources();t.after(()=>service.dispose(resources)); assert.equal((await service.request(parseTarget(options.target.url),'127.0.0.1','1.1',options,signal,resources)).redirectCount,1); await assert.rejects(service.request(parseTarget(base+'/loop'),'127.0.0.1','1.1',options,signal,resources),{code:'REDIRECT_LOOP'}); assert.equal((await curlRequest(new URL(base+'/large'),'127.0.0.1','1.1',settings,signal)).code,'BODY_LIMIT'); const control=new AbortController();const pending=curlRequest(new URL(base+'/hang'),'127.0.0.1','1.1',settings,control.signal);setTimeout(()=>control.abort(),40);await assert.rejects(pending,{code:'CANCELLED'}); }); test('curl transport failures remain distinct and do not assert server HTTP/3 support',async()=>{ for(const [exit,code] of [[6,'DNS_FAILED'],[7,'CONNECTION_REFUSED'],[28,'HTTP3_UDP_TIMEOUT'],[60,'TLS_CERT_INVALID'],[95,'HTTP3_UNVERIFIED']]){ const runner=async(_exe,args)=>args.includes('--version')?{code:0,stdout:Buffer.from('curl 8.22.0 OpenSSL/3.5.9\nFeatures: HTTP2 HTTP3 SSL\n')}:{code:exit,stdout:Buffer.alloc(0),stderr:'private debugging data'}; const r=await curlRequest(new URL('https://example.test:8443/'),'127.0.0.1','3',{timeoutMs:500},new AbortController().signal,runner);assert.equal(r.code,code);assert.equal(r.reason,undefined);assert.notEqual(r.status,'success'); } });