From 7e7af82af42f73cabdf598a48ffb56b15edbfdf2 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 12 Sep 2026 22:29:36 -0700 Subject: [PATCH] test(source-scan): unit-test the stat fallback via an extracted helper The fabricated-Dirent readdir mock could not satisfy both gates at once: vi.mocked(readdirSync) resolves to Node's Dirent overload, so the mock needed a type assertion, and #19462's casting gate rejects new ones on changed lines. Removing the cast then failed tsc. Extract directoryEntryNeedsStat and test it directly with a structural probe. No mock, no cast, no top-level await, and the DT_UNKNOWN case is pinned: removing the fallback fails 'stats an entry whose type readdir could not report'. --- src/shared/source-scan/source-tree-scan.ts | 23 +++++++-- .../source-scan/source-tree-walk.test.ts | 51 +++++++++---------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/shared/source-scan/source-tree-scan.ts b/src/shared/source-scan/source-tree-scan.ts index 64ef28be77d..dcd6050799b 100644 --- a/src/shared/source-scan/source-tree-scan.ts +++ b/src/shared/source-scan/source-tree-scan.ts @@ -29,6 +29,22 @@ export function isTestFile(relativePath: string): boolean { export type ScannedFile = { path: string; relativePath: string; source: string } +/** The three readdir type predicates the walk consults. */ +type DirentTypeProbe = { + isSymbolicLink(): boolean + isFile(): boolean + isDirectory(): boolean +} + +/** + * Links need a stat to follow them, and so does DT_UNKNOWN (every predicate + * false) -- filesystems that do not report d_type would otherwise have a real + * directory silently dropped from the scan. + */ +export function directoryEntryNeedsStat(entry: DirentTypeProbe): boolean { + return entry.isSymbolicLink() || (!entry.isFile() && !entry.isDirectory()) +} + /** * Every `.ts`/`.tsx` file under `root`, with its text. * @@ -52,11 +68,8 @@ export function scanSourceTree( continue } const path = join(directory, name) - // Ordinary entries carry their type from readdir. Links need a stat to follow - // them, and so does DT_UNKNOWN (every predicate false), or a real directory - // would be silently dropped from the scan. - const needsStat = entry.isSymbolicLink() || (!entry.isFile() && !entry.isDirectory()) - if (needsStat ? statSync(path).isDirectory() : entry.isDirectory()) { + // Ordinary entries carry their type from readdir. + if (directoryEntryNeedsStat(entry) ? statSync(path).isDirectory() : entry.isDirectory()) { visit(path) continue } diff --git a/src/shared/source-scan/source-tree-walk.test.ts b/src/shared/source-scan/source-tree-walk.test.ts index 6807ffa847d..afc7c8dba88 100644 --- a/src/shared/source-scan/source-tree-walk.test.ts +++ b/src/shared/source-scan/source-tree-walk.test.ts @@ -11,7 +11,7 @@ import type * as Fs from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { scanSourceTree } from './source-tree-scan' +import { directoryEntryNeedsStat, scanSourceTree } from './source-tree-scan' vi.mock('node:fs', async (importOriginal) => { const actual = await importOriginal() @@ -22,8 +22,6 @@ vi.mock('node:fs', async (importOriginal) => { } }) -const actualFs = await vi.importActual('node:fs') - let root: string beforeEach(() => { @@ -110,30 +108,6 @@ describe('scanSourceTree filesystem traversal', () => { expect(statSync).toHaveBeenCalledExactlyOnceWith(join(root, 'alias')) }) - it('stats an entry whose type readdir could not report instead of dropping its subtree', () => { - mkdirSync(join(root, 'nested')) - file(join('nested', 'inner.ts'), 'inner source') - // Filesystems without d_type yield a Dirent where every predicate is false. - vi.mocked(readdirSync).mockImplementationOnce(((directory: Fs.PathLike) => - actualFs.readdirSync(directory, { withFileTypes: true }).map((entry) => - entry.name === 'nested' - ? Object.assign(Object.create(Object.getPrototypeOf(entry)), entry, { - isFile: () => false, - isDirectory: () => false, - isSymbolicLink: () => false - }) - : entry - )) as typeof readdirSync) - - expect(scanSourceTree(root)).toEqual([ - { - path: join(root, 'nested', 'inner.ts'), - relativePath: 'nested/inner.ts', - source: 'inner source' - } - ]) - expect(statSync).toHaveBeenCalledExactlyOnceWith(join(root, 'nested')) - }) it('still reports a broken link instead of silently dropping it', () => { const target = join(root, '.target') @@ -145,3 +119,26 @@ describe('scanSourceTree filesystem traversal', () => { expect(statSync).toHaveBeenCalledExactlyOnceWith(join(root, 'alias')) }) }) + +describe('directoryEntryNeedsStat', () => { + const probe = (kind: 'file' | 'dir' | 'link' | 'unknown') => ({ + isFile: () => kind === 'file', + isDirectory: () => kind === 'dir', + isSymbolicLink: () => kind === 'link' + }) + + it('skips the stat for entries readdir already typed', () => { + expect(directoryEntryNeedsStat(probe('file'))).toBe(false) + expect(directoryEntryNeedsStat(probe('dir'))).toBe(false) + }) + + it('stats links so they are followed', () => { + expect(directoryEntryNeedsStat(probe('link'))).toBe(true) + }) + + // Filesystems without d_type report DT_UNKNOWN: every predicate is false, and + // without the stat a real directory's whole subtree is silently dropped. + it('stats an entry whose type readdir could not report', () => { + expect(directoryEntryNeedsStat(probe('unknown'))).toBe(true) + }) +})