Jelajahi Sumber

test(windows): use host path semantics

imccyu 2 bulan lalu
induk
melakukan
7e620db8ba

+ 23 - 23
packages/context/workspace-context/tests/workspace-context.spec.ts

@@ -1,5 +1,5 @@
 import { chmod, mkdtemp, mkdir, rm, stat, symlink, utimes, writeFile } from 'node:fs/promises'
-import { dirname, join } from 'node:path'
+import { dirname, join, resolve } from 'node:path'
 import { tmpdir } from 'node:os'
 import { describe, expect, it, vi } from 'vitest'
 import { Context } from 'cordis'
@@ -61,7 +61,7 @@ class RecordingFileSystem extends FileSystem {
   override async resolve(path: string, opts?: { cwd?: string; signal?: AbortSignal }): Promise<FsTarget> {
     if (opts?.signal !== undefined) this.signals.push(opts.signal)
     opts?.signal?.throwIfAborted()
-    const absolute = join(opts?.cwd ?? '/', path)
+    const absolute = resolve(opts?.cwd ?? '/', path)
     return { targetKey: FsTargetKey(absolute), displayPath: absolute }
   }
 
@@ -277,8 +277,8 @@ describe('workspace context instruction discovery', () => {
       expect(files.map(file => file.displayPath)).toEqual([
         '$DSH_HOME/AGENTS.md',
         'AGENTS.md',
-        'packages/CLAUDE.md',
-        'packages/app/AGENTS.md',
+        join('packages', 'CLAUDE.md'),
+        join('packages', 'app', 'AGENTS.md'),
       ])
       expect(files.map(file => file.absolutePath)).not.toContain(join(root, 'CLAUDE.md'))
     } finally {
@@ -336,7 +336,7 @@ describe('workspace context instruction discovery', () => {
     }
   })
 
-  it('skips a file that becomes unreadable after discovery without failing the request', async () => {
+  it.skipIf(process.platform === 'win32')('skips a file that becomes unreadable after discovery without failing the request', async () => {
     const root = await tempRepo()
     const home = await tempRepo()
     try {
@@ -932,7 +932,7 @@ describe('workspace context request injection', () => {
       await composeBaselinePrefix(ctx, agent)
 
       expect(derivedText(agent)).toContain('omitted AGENTS.md')
-      expect(derivedText(agent)).toContain('Instructions from: pkg/AGENTS.md\n\npackage rule')
+      expect(derivedText(agent)).toContain(`Instructions from: ${join('pkg', 'AGENTS.md')}\n\npackage rule`)
     } finally {
       await rm(root, { recursive: true, force: true })
       await rm(home, { recursive: true, force: true })
@@ -1398,7 +1398,7 @@ describe('workspace context request injection', () => {
       await composeBaselinePrefix(ctx, agent)
 
       expect(derivedText(agent)).toContain('Instructions from: AGENTS.md\n\nroot schema default rule')
-      expect(derivedText(agent)).toContain('Instructions from: child/AGENTS.md\n\nchild schema default rule')
+      expect(derivedText(agent)).toContain(`Instructions from: ${join('child', 'AGENTS.md')}\n\nchild schema default rule`)
       await ctx.fiber.dispose()
     } finally {
       await rm(root, { recursive: true, force: true })
@@ -1700,7 +1700,7 @@ describe('dynamic nested workspace context injection', () => {
         changes: [{
           action: 'set',
           scope: 'pkg',
-          path: 'pkg/AGENTS.md',
+          path: join('pkg', 'AGENTS.md'),
         }],
       })
       const meta = workspaceContextOf(result)?.meta
@@ -1714,7 +1714,7 @@ describe('dynamic nested workspace context injection', () => {
       const text = blocksText(workspaceContextOf(result)?.content)
       expect(text).toBe([
         '<system-reminder>',
-        'Additional instructions from: pkg/AGENTS.md',
+        `Additional instructions from: ${join('pkg', 'AGENTS.md')}`,
         '',
         'These instructions apply to work under `pkg`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.',
         '',
@@ -1752,7 +1752,7 @@ describe('dynamic nested workspace context injection', () => {
       })
 
       const text = blocksText(workspaceContextOf(result)?.content)
-      expect(text).toContain('Additional instructions from: pkg/CLAUDE.local.md')
+      expect(text).toContain(`Additional instructions from: ${join('pkg', 'CLAUDE.local.md')}`)
       expect(text).toContain('local package rule')
       expect(text).not.toContain('native package rule')
     } finally {
@@ -1922,11 +1922,11 @@ describe('dynamic nested workspace context injection', () => {
 
       expect(workspaceContextOf(changed)?.meta).toMatchObject({
         kind: 'workspace-instructions',
-        changes: [{ action: 'replace', scope: 'pkg', path: 'pkg/AGENTS.md' }],
+        changes: [{ action: 'replace', scope: 'pkg', path: join('pkg', 'AGENTS.md') }],
       })
       expect(blocksText(workspaceContextOf(changed)?.content)).toBe([
         '<system-reminder>',
-        'Updated instructions from: pkg/AGENTS.md',
+        `Updated instructions from: ${join('pkg', 'AGENTS.md')}`,
         '',
         'This file changed after it was loaded. Use the following content instead of the previously loaded instructions from this file.',
         '',
@@ -1966,11 +1966,11 @@ describe('dynamic nested workspace context injection', () => {
 
       expect(workspaceContextOf(changed)?.meta).toMatchObject({
         changes: [{
-          action: 'replace', scope: 'pkg', path: 'pkg/CLAUDE.md', previousPath: 'pkg/AGENTS.md',
+          action: 'replace', scope: 'pkg', path: join('pkg', 'CLAUDE.md'), previousPath: join('pkg', 'AGENTS.md'),
         }],
       })
-      expect(blocksText(workspaceContextOf(changed)?.content)).toContain('Updated instructions from: pkg/CLAUDE.md')
-      expect(blocksText(workspaceContextOf(changed)?.content)).toContain('The instructions previously loaded from `pkg/AGENTS.md` no longer apply. Use the following content for `pkg` instead.')
+      expect(blocksText(workspaceContextOf(changed)?.content)).toContain(`Updated instructions from: ${join('pkg', 'CLAUDE.md')}`)
+      expect(blocksText(workspaceContextOf(changed)?.content)).toContain(`The instructions previously loaded from \`${join('pkg', 'AGENTS.md')}\` no longer apply. Use the following content for \`pkg\` instead.`)
       expect(blocksText(workspaceContextOf(changed)?.content)).toContain('fallback package rule')
       expect(unchanged.additionalContexts).toBeUndefined()
     } finally {
@@ -2002,11 +2002,11 @@ describe('dynamic nested workspace context injection', () => {
       expect(workspaceContextOf(removed)?.meta).toEqual({
         kind: 'workspace-instructions',
         version: 1,
-        changes: [{ action: 'remove', scope: 'pkg', path: 'pkg/AGENTS.md' }],
+        changes: [{ action: 'remove', scope: 'pkg', path: join('pkg', 'AGENTS.md') }],
       })
       expect(blocksText(workspaceContextOf(removed)?.content)).toBe([
         '<system-reminder>',
-        'Instructions removed: pkg/AGENTS.md',
+        `Instructions removed: ${join('pkg', 'AGENTS.md')}`,
         '',
         'The previously loaded instructions from this file no longer apply.',
         '</system-reminder>',
@@ -2044,9 +2044,9 @@ describe('dynamic nested workspace context injection', () => {
       })
 
       expect(workspaceContextOf(restored)?.meta).toMatchObject({
-        changes: [{ action: 'set', scope: 'pkg', path: 'pkg/AGENTS.md' }],
+        changes: [{ action: 'set', scope: 'pkg', path: join('pkg', 'AGENTS.md') }],
       })
-      expect(blocksText(workspaceContextOf(restored)?.content)).toContain('Additional instructions from: pkg/AGENTS.md')
+      expect(blocksText(workspaceContextOf(restored)?.content)).toContain(`Additional instructions from: ${join('pkg', 'AGENTS.md')}`)
       expect(blocksText(workspaceContextOf(restored)?.content)).toContain('restored package rule')
     } finally {
       await rm(root, { recursive: true, force: true })
@@ -2146,7 +2146,7 @@ describe('dynamic nested workspace context injection', () => {
 
       const update = resumed.session.events.findLast(event => event.type === 'context/message')
       expect(update?.type === 'context/message' && update.data.meta).toMatchObject({
-        changes: [{ action: 'replace', scope: 'pkg', path: 'pkg/AGENTS.md' }],
+        changes: [{ action: 'replace', scope: 'pkg', path: join('pkg', 'AGENTS.md') }],
       })
       expect(update?.type === 'context/message' && blocksText(update.data.content)).toContain('new nested rule after resume')
     } finally {
@@ -2267,8 +2267,8 @@ describe('dynamic nested workspace context injection', () => {
       })
 
       const firstText = blocksText(workspaceContextOf(first)?.content)
-      expect(firstText).toContain('omitted pkg/AGENTS.md')
-      expect(firstText).not.toContain('## pkg/AGENTS.md')
+      expect(firstText).toContain(`omitted ${join('pkg', 'AGENTS.md')}`)
+      expect(firstText).not.toContain(`## ${join('pkg', 'AGENTS.md')}`)
       expect(firstText).toContain('subtree rule')
       expect(blocksText(workspaceContextOf(second)?.content)).toContain('parent rule')
     } finally {
@@ -2462,7 +2462,7 @@ describe('dynamic nested workspace context injection', () => {
       expect(workspaceContextOf(result)?.envelope).toBe('raw')
       expect(workspaceContextOf(result)?.meta).toMatchObject({
         kind: 'workspace-instructions',
-        changes: [{ action: 'set', scope: 'pkg', path: 'pkg/AGENTS.md' }],
+        changes: [{ action: 'set', scope: 'pkg', path: join('pkg', 'AGENTS.md') }],
       })
       expect(blocksText(workspaceContextOf(result)?.content)).toContain('nested package rule')
       expect(blocksText(workspaceContextOf(result)?.content)).not.toContain('downstream context')

+ 4 - 3
packages/fs/tool-fs-search/tests/tools.spec.ts

@@ -12,6 +12,7 @@
 
 import { describe, expect, it } from 'vitest'
 import { Context } from 'cordis'
+import { join } from 'node:path'
 import { CallId } from '@deepseek-ai/dsh-llm'
 import SystemPrompt, { renderPrompt } from '@deepseek-ai/dsh-system-prompt'
 import ToolRegistry from '@deepseek-ai/dsh-tools'
@@ -416,7 +417,7 @@ describe('glob results', () => {
     const { ctx, bash } = await setup()
     bash.handler = () => runResult('/sessions/s1/src/a.ts\n/elsewhere/b.ts\nrel/c.ts\n')
     const result = await call(ctx, 'glob', { pattern: '*' }, { agent: agent('/sessions/s1') })
-    expect(text(result)).toBe('src/a.ts\n/elsewhere/b.ts\nrel/c.ts')
+    expect(text(result)).toBe(`${join('src', 'a.ts')}\n/elsewhere/b.ts\nrel/c.ts`)
   })
 
   it('validates arguments (blank pattern, blank path)', async () => {
@@ -498,7 +499,7 @@ describe('grep results', () => {
     const { ctx, bash } = await setup()
     bash.handler = () => runResult(`${matchLine('/sessions/s1/deep/a.ts', 2, 'hit')}\n`)
     const result = await call(ctx, 'grep', { pattern: 'hit', path: '/sessions/s1' }, { agent: agent('/sessions/s1') })
-    expect(text(result)).toContain('deep/a.ts\nLine 2: hit')
+    expect(text(result)).toContain(`${join('deep', 'a.ts')}\nLine 2: hit`)
   })
 
   it('previews a long matched line at grepMaxLineBytes preserving UTF-8', async () => {
@@ -608,7 +609,7 @@ describe('presentation', () => {
 
 describe('helpers', () => {
   it('toWorkdirRelative maps inside-workdir absolutes and passes everything else through', () => {
-    expect(toWorkdirRelative('/w/a/b.ts', '/w')).toBe('a/b.ts')
+    expect(toWorkdirRelative('/w/a/b.ts', '/w')).toBe(join('a', 'b.ts'))
     expect(toWorkdirRelative('/w', '/w')).toBe('.')
     expect(toWorkdirRelative('/other/b.ts', '/w')).toBe('/other/b.ts')
     expect(toWorkdirRelative('/w-sibling/b.ts', '/w')).toBe('/w-sibling/b.ts')

+ 4 - 3
packages/spill/spill-local/tests/spill-local.spec.ts

@@ -10,7 +10,7 @@ import { describe, expect, it, beforeEach, afterEach } from 'vitest'
 import { Context } from 'cordis'
 import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs'
 import { tmpdir } from 'node:os'
-import { dirname, isAbsolute, join } from 'node:path'
+import { basename, dirname, isAbsolute, join, normalize } from 'node:path'
 import { CallId } from '@deepseek-ai/dsh-llm'
 import { SessionId } from '@deepseek-ai/dsh-session'
 import type { SaveTextSpill } from '@deepseek-ai/dsh-spill'
@@ -63,7 +63,8 @@ describe('sessionDir', () => {
   it('is a stable per-session hash under the root', () => {
     const dir = sessionDir('/spill', 'sess-1')
     expect(dir).toBe(sessionDir('/spill', 'sess-1'))
-    expect(dir).toMatch(/\/spill\/session-[0-9a-f]{12}$/)
+    expect(dirname(dir)).toBe(normalize('/spill'))
+    expect(basename(dir)).toMatch(/^session-[0-9a-f]{12}$/)
     expect(sessionDir('/spill', 'sess-2')).not.toBe(dir)
   })
 })
@@ -74,7 +75,7 @@ describe('saveTextFile', () => {
     expect(readFileSync(saved.path, 'utf8')).toBe('héllo')
     expect(saved.bytes).toBe(Buffer.byteLength('héllo', 'utf8'))
     expect(dirname(saved.path)).toBe(sessionDir(root, 'sess-1'))
-    expect(saved.path).toMatch(/\/[0-9a-f]{12}-r\.txt$/)
+    expect(basename(saved.path)).toMatch(/^[0-9a-f]{12}-r\.txt$/)
   })
 
   it('sanitizes a traversal-shaped suggested name into one segment', async () => {

+ 2 - 2
packages/util/paths/tests/paths.spec.ts

@@ -1,5 +1,5 @@
 import { homedir } from 'node:os'
-import { join } from 'node:path'
+import { join, resolve } from 'node:path'
 import { describe, expect, it } from 'vitest'
 import {
   DEFAULT_DSH_HOME_DISPLAY,
@@ -28,7 +28,7 @@ describe('dsh path helpers', () => {
     const envHome = join(homedir(), 'env-dsh')
 
     expect(resolveDshHome(undefined, { DSH_HOME: '~/env-dsh' })).toBe(envHome)
-    expect(resolveDshHome('/tmp/explicit-dsh', { DSH_HOME: '~/env-dsh' })).toBe('/tmp/explicit-dsh')
+    expect(resolveDshHome('/tmp/explicit-dsh', { DSH_HOME: '~/env-dsh' })).toBe(resolve('/tmp/explicit-dsh'))
     expect(resolveDshHome(undefined, {})).toBe(defaultDshHome())
   })
 })