From e7e75e79722576e291b90d481a9d634320d55cb9 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sun, 9 Aug 2026 03:50:06 +0800 Subject: [PATCH] test(windows): cover final native branches --- ...8-native-windows-pull-request-ci.i18n.yaml | 4 +-- ...26-08-08-native-windows-pull-request-ci.md | 2 ++ ...08-08-native-windows-pull-request-ci.zh.md | 2 ++ .../credentials-local/tests/watcher.spec.ts | 35 +++++++++++++++++++ .../tool-bash-persistent/tests/tools.spec.ts | 2 +- 5 files changed, 42 insertions(+), 3 deletions(-) diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml index ddb1e7a1e4..512779f7a7 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md -2026-08-08-native-windows-pull-request-ci.md: 419d1138bbd3ab9296b38ed136e03146ceca686d -2026-08-08-native-windows-pull-request-ci.zh.md: 5353b21c5a69e6ad847065d124d1bddb4d793e56 +2026-08-08-native-windows-pull-request-ci.md: 0d85517e877694a09adf454a9acad3da78ebf0e1 +2026-08-08-native-windows-pull-request-ci.zh.md: 4c7dfe6f1ec290a1dee4cb91f7e78ea7a52f32bb diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md index 419d1138bb..0d85517e87 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md @@ -44,6 +44,8 @@ The project-skill composition fixture had one separate eventual-consistency race The next exact-head run passed all 10,933 instrumented tests but correctly failed the per-file threshold at 99.95%, exposing five branches that Linux happened to cover. Deterministic cross-platform fixtures now exercise backward PTY scrollback pagination, a settings document that names a directory, an invalid SQLite filename, and an atomic-writer lock beneath a regular-file parent. The credentials provider's remaining `stat` and mode-enforcement arm is intrinsically POSIX, so it carries the same narrow annotated peer ignore used by the durable JSONL and storage backends; its behavior test remains enforced on POSIX. The threshold and source-file inventory remain unchanged. +The follow-up exact-head run passed all 10,937 instrumented tests and narrowed the threshold result to 99.99%. Its two remaining lines showed that the first PTY fixture had reached the page-offset helper but only supplied two pages, and that Windows path canonicalization rejected the real invalid-path fixture before `readFile` reached the reload-policy branch. The PTY fixture now supplies three backward pages, while the watcher fixture injects one non-absence read failure after the real permission check; both retain the observable output or last-good-snapshot assertions they exist to prove. + POSIX mode bits, chmod-based unreadability, and chmod-based writer-lock refusal do not exist as equivalent Windows facilities. Those acceptance cases remain enforced on POSIX and are skipped on Windows; content, atomic replacement, symlink safety, rollback and recovery through platform-independent filesystem conflicts, and native Windows long-path behavior remain covered. Only intrinsically POSIX source arms carry narrow, explained denominator ignores; no source file or platform-independent branch is excluded from Windows coverage to accommodate these differences. Wine-only infrastructure is absent from the supported workflow: there is no apt-cache producer, compatibility script, hoisted snapshot install, Windows Node download, or local `check:windows-wine` command. The [archived Wine experiment](../../archived/process/2026-07-27-wine-windows-gates-experiment.md) remains historical evidence for its measured latency and fidelity trade-offs, not a current execution path. diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md index 5353b21c5a..4c7dfe6f1e 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md @@ -44,6 +44,8 @@ Status: implemented 下一次分支头精确运行通过了全部 10,933 项插桩测试,但逐文件阈值仍在 99.95% 正确失败,从而暴露出 5 个此前恰由 Linux 覆盖的分支。新增的确定性跨平台 fixture 会分别覆盖 PTY 向后翻页拼接 scrollback、以目录作为 settings 文档、非法 SQLite 文件名,以及 regular file(普通文件)父级之下的原子写入锁。credentials provider 剩余的 `stat` 与模式位强制分支本质上只属于 POSIX,因此采用与持久 JSONL、storage backend 相同的窄范围、带说明的对等分支忽略;其行为测试仍会在 POSIX 上强制执行。阈值与源码文件清单均未改变。 +后续分支头精确运行通过了全部 10,937 项插桩测试,并把阈值结果收窄到 99.99%。剩余的两行表明,首版 PTY fixture 已到达页偏移 helper,却只提供了两页数据;Windows 路径规范化还会在 `readFile` 到达 reload policy(重载策略)分支前,先拒绝真实的非法路径 fixture。PTY fixture 现在会提供 3 个向后翻页页面;watcher fixture 则会在真实权限检查之后注入一次非“文件不存在”的读取失败。两项 fixture 仍保留其本来要证明的可观察输出或 last-good snapshot(最后有效快照)断言。 + POSIX 模式位、基于 chmod 的不可读状态和基于 chmod 的 writer lock 拒绝在 Windows 上没有等价机制。这些验收场景继续在 POSIX 上强制执行,并在 Windows 上跳过;内容、原子替换、符号链接安全、通过平台无关文件系统冲突验证的回滚与恢复,以及原生 Windows 长路径行为仍保有覆盖。只有本质上属于 POSIX 的源码分支带有窄范围且说明明确的分母忽略;没有任何源码文件或平台无关分支为适应这些差异而从 Windows 覆盖率中排除。 受支持的工作流不含 Wine 专属基础设施:不存在 apt 缓存生产者、兼容性脚本、对仓库快照执行的 hoisted 安装、Windows Node 下载或本地 `check:windows-wine` 命令。[已归档的 Wine 实验](../../archived/process/2026-07-27-wine-windows-gates-experiment.md)仍作为其实测延迟与保真度取舍的历史证据,而非当前执行路径。 diff --git a/packages/credentials/credentials-local/tests/watcher.spec.ts b/packages/credentials/credentials-local/tests/watcher.spec.ts index 8c216e6976..e49f1421b4 100644 --- a/packages/credentials/credentials-local/tests/watcher.spec.ts +++ b/packages/credentials/credentials-local/tests/watcher.spec.ts @@ -6,6 +6,25 @@ import { join } from 'node:path' import { credentialRef } from '@deepseek-ai/dsh-credentials' import { CredentialsLocal } from '../src/index.ts' +const fsHarness = vi.hoisted(() => ({ + nextReadError: undefined as NodeJS.ErrnoException | undefined, +})) + +vi.mock('node:fs/promises', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + readFile: (async (path: unknown, ...rest: never[]) => { + const error = fsHarness.nextReadError + if (error !== undefined) { + fsHarness.nextReadError = undefined + throw error + } + return (actual.readFile as (path: unknown, ...args: never[]) => Promise)(path, ...rest) + }) as typeof actual.readFile, + } +}) + /** Credential documents are seeded owner-only, exactly as the provider creates them. */ function writeCredentials(file: string, text: string): Promise { return writeFile(file, text, { mode: 0o600 }) @@ -48,6 +67,7 @@ const KEY = credentialRef('DSH_CRED_PIPE') const cleanups: Array<() => Promise> = [] afterEach(async () => { + fsHarness.nextReadError = undefined while (cleanups.length > 0) await cleanups.pop()!() ;(await fakeInstances()).length = 0 }) @@ -107,6 +127,21 @@ describe('watcher pipeline', () => { expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'good', source: 'file' }) }) + it('keeps the last good snapshot when the read fails after its permission check', async () => { + const dir = await tempDir() + const path = join(dir, '.credentials.yaml') + await writeCredentials(path, 'DSH_CRED_PIPE: good\n') + const ctx = await boot({ path, debounceMs: 5 }) + fsHarness.nextReadError = Object.assign(new Error('EACCES: injected read failure'), { code: 'EACCES' }) + + const [instance] = await fakeInstances() + instance!.watcher.emit('all', 'change', path) + await vi.waitFor(() => { + expect(fsHarness.nextReadError).toBeUndefined() + }) + expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'good', source: 'file' }) + }) + it('keeps the reload queue alive after an invariant violation escapes the fan-out', async () => { const dir = await tempDir() const path = join(dir, '.credentials.yaml') diff --git a/packages/pty/tool-bash-persistent/tests/tools.spec.ts b/packages/pty/tool-bash-persistent/tests/tools.spec.ts index 73eeeeb69e..bcff35b2a4 100644 --- a/packages/pty/tool-bash-persistent/tests/tools.spec.ts +++ b/packages/pty/tool-bash-persistent/tests/tools.spec.ts @@ -421,7 +421,7 @@ describe('tool-bash-persistent', () => { await call(ctx, owner, 'warm up') const session = stub.sessions[0]! session.mode = 'paged-scrollback' - session.scrollback = '' + session.scrollback = 'older one\nolder two\nolder three\nolder four\n' expect(text(await call(ctx, owner, 'paged output'))).toBe('hello from stub') })