Skip to content

Commit b5a1df6

Browse files
mihailtclaude
andcommitted
test(remote): remote --report must upload to the deployed diagnostics Worker
An integration test (npm run test:integration) for the upload, against the live services: 1. remote --report exits 0, saves the zip in the home folder, and prints "Sent to Desktop Commander support. Report id: <8 chars>" with the Worker's alphabet (no 0, 1, I or O). 2. remote --report --no-upload saves the zip and sends nothing. 3. A body that isn't a zip, POSTed straight to the Worker, gets 415. It runs with a temporary HOME / USERPROFILE whose device.json looks like a real sign-in, with ids fixed and reserved for tests (never real Supabase ids): device 00000000-0000-0000-0000-000000000001 and a fake, unsigned access token whose sub is 00000000-0000-0000-0000-000000000000. The CLI only reads sub; nothing signs in or refreshes. Its uploads land like any report, under <user>/<device>/<YYYY-MM-DD-HHMM>-<id>.zip. The upload goes to the default address; DC_DIAGNOSTICS_URL and MCP_SERVER_URL can point it elsewhere. The report id is printed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 6237343 commit b5a1df6

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Integration test: `remote --report` uploads its zip to the deployed
3+
* diagnostics Worker.
4+
*
5+
* Runs the built CLI against the live services: the report's network checks go
6+
* to mcp.desktopcommander.app (MCP_SERVER_URL can point elsewhere), and the
7+
* upload to the Worker's default address (DC_DIAGNOSTICS_URL can point
8+
* elsewhere). It never uses the real home folder: a temporary HOME /
9+
* USERPROFILE holds a device.json that looks like a real sign-in, with ids
10+
* fixed and reserved for tests, never real Supabase ids: device
11+
* 00000000-0000-0000-0000-000000000001, and a fake, unsigned access token
12+
* whose payload has sub 00000000-0000-0000-0000-000000000000 (the CLI only
13+
* reads `sub`; nothing signs in or refreshes). Uploads land like any report,
14+
* under <user>/<device>/<YYYY-MM-DD-HHMM>-<id>.zip.
15+
*
16+
* 1. remote --report: exit 0, the zip in the temporary home, and
17+
* "Sent to Desktop Commander support. Report id: <8 chars>".
18+
* 2. remote --report --no-upload: the zip, no "Sent" line.
19+
* 3. A non-zip POSTed straight to the Worker: 415.
20+
*
21+
* Needs a built dist/ (npm run test:integration builds it). Prints the report
22+
* id so it can be found in the bucket.
23+
*/
24+
25+
import assert from 'assert';
26+
import { spawn } from 'child_process';
27+
import fs from 'fs';
28+
import os from 'os';
29+
import path from 'path';
30+
import { fileURLToPath } from 'url';
31+
32+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
33+
const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
34+
const CLI = path.join(PROJECT_ROOT, 'dist', 'index.js');
35+
/** Reserved for tests: never a real Supabase user or device. */
36+
const TEST_USER_ID = '00000000-0000-0000-0000-000000000000';
37+
const TEST_DEVICE_ID = '00000000-0000-0000-0000-000000000001';
38+
/** The Worker's report ids: 8 characters without 0, 1, I and O. */
39+
const REPORT_ID = /\nSent to Desktop Commander support\. Report id: ([23456789ABCDEFGHJKLMNPQRSTUVWXYZ]{8})\n/;
40+
const RUN_TIMEOUT_MS = 120_000;
41+
42+
/** A JWT-shaped token: the CLI decodes the payload's `sub` and never verifies it. */
43+
function fakeAccessToken(sub) {
44+
return [{ alg: 'none', typ: 'JWT' }, { sub, exp: 1 }]
45+
.map((part) => Buffer.from(JSON.stringify(part)).toString('base64url'))
46+
.join('.') + '.unsigned';
47+
}
48+
49+
function makeHome() {
50+
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'dc-integration-report-home-'));
51+
const deviceDir = path.join(home, '.desktop-commander-device');
52+
fs.mkdirSync(deviceDir, { recursive: true });
53+
fs.writeFileSync(path.join(deviceDir, 'device.json'), JSON.stringify({
54+
deviceId: TEST_DEVICE_ID,
55+
session: { access_token: fakeAccessToken(TEST_USER_ID), refresh_token: 'test-refresh-token' },
56+
}, null, 2));
57+
return home;
58+
}
59+
60+
function runReport(home, args = []) {
61+
return new Promise((resolve) => {
62+
const { FORCE_COLOR, ...inherited } = process.env;
63+
const child = spawn(process.execPath, [CLI, 'remote', '--report', ...args], {
64+
cwd: home,
65+
env: {
66+
...inherited,
67+
HOME: home,
68+
USERPROFILE: home,
69+
DESKTOP_COMMANDER_DISABLE_TELEMETRY: '1',
70+
DC_FLAG_URL: 'http://127.0.0.1:9/',
71+
},
72+
stdio: ['ignore', 'pipe', 'pipe'],
73+
});
74+
let output = '';
75+
child.stdout.on('data', (d) => { output += d; });
76+
child.stderr.on('data', (d) => { output += d; });
77+
const timer = setTimeout(() => child.kill(), RUN_TIMEOUT_MS);
78+
child.on('close', (code) => {
79+
clearTimeout(timer);
80+
resolve({ code, output });
81+
});
82+
});
83+
}
84+
85+
function savedZip(result, home) {
86+
const saved = result.output.match(/Saved: (.+\.zip) \(/)?.[1];
87+
assert.ok(saved, `a "Saved:" line:\n${result.output}`);
88+
assert.ok(saved.startsWith(home), `the zip is in the temporary home: ${saved}`);
89+
assert.ok(fs.existsSync(saved), `the zip exists: ${saved}`);
90+
return saved;
91+
}
92+
93+
async function main() {
94+
const { DEFAULT_DIAGNOSTICS_URL } = await import('../../dist/remote-device/diagnostics/upload.js');
95+
const workerUrl = process.env.DC_DIAGNOSTICS_URL || DEFAULT_DIAGNOSTICS_URL;
96+
console.log(`Worker: ${workerUrl}${process.env.MCP_SERVER_URL ? `, server: ${process.env.MCP_SERVER_URL}` : ''}`);
97+
const home = makeHome();
98+
try {
99+
console.log('\n[Case 1] remote --report uploads the zip');
100+
const sent = await runReport(home);
101+
console.log(sent.output.split('\n').filter((line) => /^(Saved|Sent|Give|Not sent)/.test(line)).join('\n'));
102+
assert.strictEqual(sent.code, 0, `exit code ${sent.code}:\n${sent.output}`);
103+
savedZip(sent, home);
104+
const id = sent.output.match(REPORT_ID)?.[1];
105+
assert.ok(id, `a report id from the Worker:\n${sent.output}`);
106+
console.log(`[Case 1] PASS - report id ${id}`);
107+
108+
console.log('\n[Case 2] remote --report --no-upload only saves the zip');
109+
const kept = await runReport(home, ['--no-upload']);
110+
assert.strictEqual(kept.code, 0, `exit code ${kept.code}:\n${kept.output}`);
111+
savedZip(kept, home);
112+
assert.ok(!/Sent to|Not sent/.test(kept.output), `no upload line:\n${kept.output}`);
113+
assert.match(kept.output, /Reply to your support conversation with this file attached\./);
114+
console.log('[Case 2] PASS - saved, not sent');
115+
116+
console.log('\n[Case 3] a non-zip POSTed straight to the Worker');
117+
const response = await fetch(workerUrl, {
118+
method: 'POST',
119+
headers: { 'Content-Type': 'application/zip', 'X-DC-User-Id': TEST_USER_ID, 'X-DC-Device-Id': TEST_DEVICE_ID },
120+
body: 'not a zip',
121+
signal: AbortSignal.timeout(30_000),
122+
});
123+
assert.strictEqual(response.status, 415, `415 for a body that doesn't start with PK, got ${response.status}`);
124+
console.log('[Case 3] PASS - 415');
125+
126+
console.log(`\nAll assertions passed. Report id: ${id}`);
127+
} finally {
128+
fs.rmSync(home, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
129+
}
130+
}
131+
132+
main().catch((err) => {
133+
console.error('\nTEST FAILED:', err && err.message ? err.message : err);
134+
process.exit(1);
135+
});

0 commit comments

Comments
 (0)