test(windows): close final native coverage gaps
This commit is contained in:
@@ -314,9 +314,10 @@ describe.skipIf(!hasPwsh)('PwshLocalExecutor.start (background process handles)'
|
||||
env: { BG_VAR: 'bg-env' },
|
||||
dshEnv: { DSH_BG_VAR: 'bg-dsh-env' },
|
||||
}))
|
||||
const output = await readUntil(proc, '[bg-env][bg-dsh-env]')
|
||||
expect(output).toBe('bg-stdin\n[bg-env][bg-dsh-env]\n')
|
||||
const partialOutput = await readUntil(proc, '[bg-env][bg-dsh-env]')
|
||||
await proc.done
|
||||
const output = partialOutput + lf(proc.readOutput().delta)
|
||||
expect(output).toBe('bg-stdin\n[bg-env][bg-dsh-env]\n')
|
||||
expect(proc.exitCode).toBe(0)
|
||||
})
|
||||
|
||||
|
||||
@@ -4113,11 +4113,12 @@ describe('dynamic nested workspace context injection', () => {
|
||||
await ctx.plugin(RecordingFileSystem)
|
||||
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
|
||||
const fs = ctx.fs as RecordingFileSystem
|
||||
const agent = stubAgent('/')
|
||||
const root = resolve('/')
|
||||
const agent = stubAgent(root)
|
||||
const failure = new Error('projection failed')
|
||||
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
|
||||
fs.entries.set('/.git', { type: 'directory' })
|
||||
fs.entries.set('/AGENTS.md', { type: 'file', content: 'workspace rule' })
|
||||
fs.entries.set(join(root, '.git'), { type: 'directory' })
|
||||
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'workspace rule' })
|
||||
vi.spyOn(agent.inbox, 'prepend').mockImplementationOnce(() => { throw failure })
|
||||
|
||||
ctx.emit('tools/result', stubToolExecution({
|
||||
|
||||
@@ -22,7 +22,7 @@ import BrowseDirectoryPicker from '@deepseek-ai/dsh-host-directory-picker-browse
|
||||
import NativeDirectoryPicker from '@deepseek-ai/dsh-host-directory-picker-native'
|
||||
import * as DirectoryPickerAuto from '../src/index.ts'
|
||||
|
||||
const renameControl = vi.hoisted(() => ({ attempts: 0, remainingFailures: 0 }))
|
||||
const renameControl = vi.hoisted(() => ({ attempts: 0, injectedFailures: 0, remainingFailures: 0 }))
|
||||
|
||||
vi.mock('node:fs/promises', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('node:fs/promises')>()
|
||||
@@ -32,6 +32,7 @@ vi.mock('node:fs/promises', async (importOriginal) => {
|
||||
renameControl.attempts++
|
||||
if (renameControl.remainingFailures > 0) {
|
||||
renameControl.remainingFailures--
|
||||
renameControl.injectedFailures++
|
||||
throw Object.assign(new Error(`transient rename failure for ${newPath}`), { code: 'EPERM' })
|
||||
}
|
||||
await actual.rename(oldPath, newPath)
|
||||
@@ -59,6 +60,7 @@ afterEach(async () => {
|
||||
root = undefined
|
||||
fakeBin = undefined
|
||||
renameControl.attempts = 0
|
||||
renameControl.injectedFailures = 0
|
||||
renameControl.remainingFailures = 0
|
||||
})
|
||||
|
||||
@@ -187,6 +189,8 @@ describe('real Loader composition', () => {
|
||||
expect(entryNames(ctx)).not.toContain(NATIVE)
|
||||
// Same self-dispose persistence as above: let the write land before teardown.
|
||||
await expect.poll(async () => await readFile(configPath, 'utf8')).toContain('disabled: true')
|
||||
expect(renameControl.attempts).toBe(2)
|
||||
expect(renameControl.injectedFailures).toBe(1)
|
||||
expect(renameControl.remainingFailures).toBe(0)
|
||||
expect(renameControl.attempts).toBeGreaterThanOrEqual(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -272,7 +272,7 @@ describe('PiAiAdapter provider routing', () => {
|
||||
await Promise.race([
|
||||
server.responseClosed,
|
||||
new Promise<never>((_resolve, reject) => {
|
||||
setTimeout(() => { reject(new Error('SDK request did not close after idle timeout')) }, 100)
|
||||
setTimeout(() => { reject(new Error('SDK request did not close after idle timeout')) }, 1_000)
|
||||
}),
|
||||
])
|
||||
|
||||
|
||||
@@ -38,9 +38,9 @@ describe('lsp-local provider resolution', () => {
|
||||
// A tiny executable script placed on a custom PATH dir: the load-time resolver must find it.
|
||||
const bin = join(root, 'bin')
|
||||
await mkdir(bin)
|
||||
const exe = join(bin, 'fake-lsp')
|
||||
await writeFile(exe, '#!/bin/sh\nexit 0\n')
|
||||
await chmod(exe, 0o755)
|
||||
const exe = join(bin, process.platform === 'win32' ? 'fake-lsp.cmd' : 'fake-lsp')
|
||||
await writeFile(exe, process.platform === 'win32' ? '@exit /b 0\r\n' : '#!/bin/sh\nexit 0\n')
|
||||
if (process.platform !== 'win32') await chmod(exe, 0o755)
|
||||
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(Lsp)
|
||||
@@ -49,7 +49,7 @@ describe('lsp-local provider resolution', () => {
|
||||
await expect(ctx.plugin(LspLocal, config('onpath', {
|
||||
command: 'fake-lsp',
|
||||
args: [],
|
||||
env: { PATH: bin },
|
||||
env: { PATH: bin, ...process.platform === 'win32' ? { PATHEXT: '.CMD' } : {} },
|
||||
extensionToLanguage: { '.ts': 'typescript' },
|
||||
}))).resolves.toBeDefined()
|
||||
await ctx.fiber.dispose()
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('renderUri', () => {
|
||||
it('returns an absolute path for a file: URI outside the workspace', () => {
|
||||
const outside = resolve(WS, '..', 'other', 'lib', 'b.ts')
|
||||
const uri = pathToFileURL(outside).href
|
||||
expect(renderUri(uri, WS_URI)).toBe(outside)
|
||||
expect(renderUri(uri, WS_URI)).toBe(outside.replaceAll('\\', '/'))
|
||||
})
|
||||
|
||||
it('renders the workspace root itself as "."', () => {
|
||||
@@ -83,7 +83,7 @@ describe('renderUri', () => {
|
||||
})
|
||||
|
||||
it('preserves backslashes as ordinary POSIX filename characters', () => {
|
||||
expect(renderUri('file:///home/u/proj/dir%5Cname/a.ts', WS_URI)).toBe('dir\\name/a.ts')
|
||||
expect(renderUri('file:///home/u/proj/dir%5Cname/a.ts', 'file:///home/u/proj')).toBe('dir\\name/a.ts')
|
||||
})
|
||||
|
||||
it('keeps malformed or mismatched URI coordinates verbatim', () => {
|
||||
|
||||
@@ -174,18 +174,32 @@ describe('real @openai/codex 0.147.0 product', () => {
|
||||
}, 60_000)
|
||||
|
||||
it('cancels a real app-server command approval without executing the command', async () => {
|
||||
const { harness, fixture } = await realHarness([
|
||||
const command = process.platform === 'win32'
|
||||
? 'cmd /c type nul > approval-side-effect'
|
||||
: 'touch approval-side-effect'
|
||||
const commandCalls = [
|
||||
{
|
||||
kind: 'functionCall',
|
||||
name: 'exec_command',
|
||||
arguments: {
|
||||
cmd: process.platform === 'win32'
|
||||
? 'cmd /c type nul > approval-side-effect'
|
||||
: 'touch approval-side-effect',
|
||||
cmd: command,
|
||||
sandbox_permissions: 'require_escalated',
|
||||
justification: 'exercise the unattended approval boundary',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'shell_command',
|
||||
arguments: {
|
||||
command,
|
||||
sandbox_permissions: 'require_escalated',
|
||||
justification: 'exercise the unattended approval boundary',
|
||||
},
|
||||
},
|
||||
] as const
|
||||
const { harness, fixture } = await realHarness([
|
||||
{
|
||||
kind: 'advertisedFunctionCall',
|
||||
choices: commandCalls,
|
||||
},
|
||||
])
|
||||
const sideEffect = join(harness.workspace, 'approval-side-effect')
|
||||
const run = await harness.ctx.subagents.start('codex', {
|
||||
@@ -202,9 +216,9 @@ describe('real @openai/codex 0.147.0 product', () => {
|
||||
expect(existsSync(sideEffect)).toBe(false)
|
||||
expect(fixture.requests).toHaveLength(1)
|
||||
const tools = fixture.requests[0]!.body.tools as Array<Record<string, unknown>>
|
||||
expect(tools).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ type: 'function', name: 'exec_command' }),
|
||||
]))
|
||||
expect(commandCalls.some(call => tools.some(tool => (
|
||||
tool.type === 'function' && tool.name === call.name
|
||||
)))).toBe(true)
|
||||
expect(fixture.requests.every(requestEntry =>
|
||||
requestEntry.headers.authorization === 'Bearer dsh-fake-openai-key',
|
||||
)).toBe(true)
|
||||
|
||||
@@ -22,6 +22,13 @@ export type ResponsesBehavior =
|
||||
readonly name: string
|
||||
readonly arguments: Record<string, unknown>
|
||||
}
|
||||
| {
|
||||
readonly kind: 'advertisedFunctionCall'
|
||||
readonly choices: readonly {
|
||||
readonly name: string
|
||||
readonly arguments: Record<string, unknown>
|
||||
}[]
|
||||
}
|
||||
| { readonly kind: 'hold' }
|
||||
|
||||
/** Running package-private Responses fixture. */
|
||||
@@ -218,6 +225,18 @@ function closeServer(server: Server): Promise<void> {
|
||||
})
|
||||
}
|
||||
|
||||
function advertisedFunctionNames(body: Record<string, unknown>): Set<string> {
|
||||
if (!Array.isArray(body.tools)) return new Set()
|
||||
return new Set(body.tools.flatMap((tool): string[] => (
|
||||
tool !== null
|
||||
&& typeof tool === 'object'
|
||||
&& (tool as Record<string, unknown>).type === 'function'
|
||||
&& typeof (tool as Record<string, unknown>).name === 'string'
|
||||
? [(tool as Record<string, unknown>).name as string]
|
||||
: []
|
||||
)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a loopback-only Responses SSE fixture.
|
||||
* @param script - one behavior per expected Responses request.
|
||||
@@ -234,11 +253,12 @@ export async function startResponsesFixture(
|
||||
openResponses.add(response)
|
||||
response.on('close', () => { openResponses.delete(response) })
|
||||
void readRequest(request).then((body) => {
|
||||
const parsedBody = JSON.parse(body) as Record<string, unknown>
|
||||
requests.push({
|
||||
method: request.method,
|
||||
path: request.url,
|
||||
headers: request.headers,
|
||||
body: JSON.parse(body) as Record<string, unknown>,
|
||||
body: parsedBody,
|
||||
})
|
||||
started.resolve(undefined)
|
||||
const behavior = behaviors.shift()
|
||||
@@ -247,6 +267,14 @@ export async function startResponsesFixture(
|
||||
response.end(JSON.stringify({ error: { message: 'fixture script exhausted' } }))
|
||||
return
|
||||
}
|
||||
const advertisedCall = behavior.kind === 'advertisedFunctionCall'
|
||||
? behavior.choices.find(choice => advertisedFunctionNames(parsedBody).has(choice.name))
|
||||
: undefined
|
||||
if (behavior.kind === 'advertisedFunctionCall' && advertisedCall === undefined) {
|
||||
response.writeHead(500, { 'content-type': 'application/json' })
|
||||
response.end(JSON.stringify({ error: { message: 'none of the fixture function calls was advertised' } }))
|
||||
return
|
||||
}
|
||||
response.writeHead(200, {
|
||||
'content-type': 'text/event-stream',
|
||||
'cache-control': 'no-cache',
|
||||
@@ -254,9 +282,15 @@ export async function startResponsesFixture(
|
||||
'x-request-id': 'req_fixture',
|
||||
})
|
||||
if (behavior.kind === 'hold') return
|
||||
const events = behavior.kind === 'complete'
|
||||
? completeResponsesEvents(behavior.text)
|
||||
: functionCallEvents(behavior.name, behavior.arguments)
|
||||
let events: Record<string, unknown>[]
|
||||
if (behavior.kind === 'complete') {
|
||||
events = completeResponsesEvents(behavior.text)
|
||||
} else {
|
||||
const call = behavior.kind === 'functionCall'
|
||||
? behavior
|
||||
: advertisedCall!
|
||||
events = functionCallEvents(call.name, call.arguments)
|
||||
}
|
||||
for (const event of events) {
|
||||
response.write(`data: ${JSON.stringify(event)}\n\n`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user