Files
aiturk-hermes-ide/apps/desktop/electron/gateway-file-download-transport.test.ts
T

72 lines
3.0 KiB
TypeScript

/**
* Wiring coverage for the main.ts gateway download transports. These functions
* pull in main-process singletons (https/http, electronNet, the OAuth session,
* the save dialog), so we assert on their source shape — the same approach as
* oauth-session-request.test.ts — while gateway-file-download.test.ts unit-tests
* the extracted streaming/decoding logic behaviorally.
*/
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { test } from 'vitest'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const source = fs.readFileSync(path.join(__dirname, 'main.ts'), 'utf8')
function extract(startMarker: string, endMarker: string): string {
const start = source.indexOf(startMarker)
assert.notEqual(start, -1, `${startMarker} should exist`)
const end = source.indexOf(endMarker, start + startMarker.length)
assert.notEqual(end, -1, `boundary after ${startMarker} should exist`)
return source.slice(start, end)
}
test('token transport streams to disk instead of buffering the whole body', () => {
const fn = extract('function downloadViaTokenToFile', '\nfunction ')
// Delegates byte-moving to the streaming finalizer...
assert.match(fn, /finalizeGatewayDownload\(/)
// ...and must NOT accumulate the full response before writing.
assert.doesNotMatch(fn, /Buffer\.concat/)
assert.doesNotMatch(fn, /chunks\.push/)
// Idle timeout is dropped once headers arrive so the dialog/stream isn't killed.
assert.match(fn, /setTimeout\(0\)/)
})
test('oauth transport streams to disk instead of buffering the whole body', () => {
const fn = extract('function downloadViaOauthSessionToFile', '\nasync function finalizeGatewayDownload')
assert.match(fn, /electronNet\.request/)
assert.match(fn, /finalizeGatewayDownload\(/)
assert.doesNotMatch(fn, /Buffer\.concat/)
assert.doesNotMatch(fn, /chunks\.push/)
})
test('finalizeGatewayDownload prompts a save dialog then streams the response', () => {
const fn = extract('async function finalizeGatewayDownload', '\nfunction readGatewayErrorText')
assert.match(fn, /dialog\.showSaveDialog/)
assert.match(fn, /pumpStreamToFile\(/)
// Production deps come from one place so the streaming save and the data-URL
// fallback share the exclusive-create + rename contract (#96597).
assert.match(fn, /fsPumpDeps\(\)/)
assert.doesNotMatch(fn, /fs\.createWriteStream/)
// HTTP errors carry their status so a 404 can trigger the fallback.
assert.match(fn, /error\.statusCode = statusCode/)
})
test('data-URL fallback writes through the same failure-atomic primitive, never writeFile in place', () => {
const fn = extract('async function saveGatewayFileViaDataUrl', '\n// Mint a single-use WS ticket')
assert.match(fn, /dialog\.showSaveDialog/)
assert.match(fn, /writeBufferToFile\(/)
assert.match(fn, /fsPumpDeps\(\)/)
// A direct writeFile truncates an existing destination before the write
// completes; a mid-write failure would destroy it (#96597).
assert.doesNotMatch(fn, /fs\.promises\.writeFile/)
})