Files
Yuvomi-App/scripts/integration-server.mjs
T
Yuvomi Mobile Contributors 9ca11e4fb5 test: exercise native Android flows against pinned upstream HTTPS and OIDC
Add an ephemeral HTTPS proxy and standards-shaped RS256/S256-PKCE test IdP around the unmodified upstream checkout. Require unlocked devices and nonempty, failure-free JUnit evidence. The extended API 35 suite passes all seven cases, including native Blob/file transfer, session recreation, outage recovery, isolated OIDC and back navigation; eight evidence-verifier regressions also pass. API 37 and real-provider/device validation remain release gates.
2026-10-21 07:32:17 +02:00

54 lines
2.5 KiB
JavaScript

// Test-only reverse proxy around the unmodified pinned upstream application.
import fs from 'node:fs';
import http from 'node:http';
import https from 'node:https';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { randomBytes } from 'node:crypto';
import { once } from 'node:events';
import { startIdentityProvider } from './oidc-fixture.mjs';
const [upstream, directory] = process.argv.slice(2);
process.env.PORT = '0';
process.env.BIND_ADDRESS = '127.0.0.1';
process.env.DB_PATH = path.join(directory, 'fixture.db');
process.env.SESSION_SECRET = randomBytes(48).toString('hex');
process.env.SESSION_SECURE = 'true';
process.env.DB_ENCRYPTION_KEY = '';
process.env.NODE_ENV = 'test';
process.env.LOG_LEVEL = 'warn';
const tls = { key: fs.readFileSync(path.join(directory, 'key.pem')), cert: fs.readFileSync(path.join(directory, 'cert.pem')) };
const idp = await startIdentityProvider(tls);
process.env.OIDC_ISSUER = idp.issuer;
process.env.OIDC_CLIENT_ID = idp.clientId;
process.env.OIDC_CLIENT_SECRET = idp.secret;
const { server } = await import(pathToFileURL(path.join(upstream, 'server/index.js')));
if (!server.listening) await once(server, 'listening');
let networkAvailable = true;
const proxy = https.createServer(tls, (request, response) => {
// Fault injection lives only in this loopback fixture, never in upstream/app code.
if (request.method === 'POST' && ['/__mobile_test/network/off', '/__mobile_test/network/on'].includes(request.url)) {
networkAvailable = request.url.endsWith('/on');
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end('{}');
return;
}
if (!networkAvailable) { request.socket.destroy(); return; }
const backend = http.request({
hostname: '127.0.0.1', port: server.address().port,
method: request.method, path: request.url,
headers: { ...request.headers, 'x-forwarded-proto': 'https', 'x-forwarded-for': '127.0.0.1' },
}, incoming => {
response.writeHead(incoming.statusCode, incoming.headers);
incoming.pipe(response);
});
backend.on('error', () => { response.writeHead(502); response.end(); });
request.pipe(backend);
});
proxy.listen(0, '127.0.0.1');
await once(proxy, 'listening');
process.env.OIDC_REDIRECT_URI = `https://127.0.0.1:${proxy.address().port}/api/v1/auth/oidc/callback`;
idp.setRedirect(process.env.OIDC_REDIRECT_URI);
fs.writeFileSync(path.join(directory, 'ready.json'), JSON.stringify({ port: proxy.address().port, idpPort: idp.port }));
process.on('SIGTERM', () => { idp.close(); proxy.close(); server.close(); process.exit(0); });