diff --git a/src/test/local-file-system.test.ts b/src/test/local-file-system.test.ts index d3a48b7..aa04fef 100644 --- a/src/test/local-file-system.test.ts +++ b/src/test/local-file-system.test.ts @@ -8,25 +8,88 @@ describe('LocalFileSystem', () => { let tempDir: string; beforeEach(async () => { - tempDir = await fs.mkdtemp(path.join(tmpdir(), 'reporter-test-')); + tempDir = await fs.mkdtemp(path.join(tmpdir(), 'lfs-')); + await fs.writeFile( + path.join(tempDir, 'package.json'), + JSON.stringify({name: 'pkg', version: '1.0.0'}) + ); }); afterEach(async () => { await fs.rm(tempDir, {recursive: true, force: true}); }); - describe('fileExists', () => { - it('should return false when tsconfig.json does not exist', async () => { - const fileSystem = new LocalFileSystem(tempDir); - const hasConfig = await fileSystem.fileExists('/tsconfig.json'); - expect(hasConfig).toBe(false); - }); - - it('should return true when file exists', async () => { - await fs.writeFile(path.join(tempDir, 'tsconfig.json'), '{}'); - const fileSystem = new LocalFileSystem(tempDir); - const hasConfig = await fileSystem.fileExists('/tsconfig.json'); - expect(hasConfig).toBe(true); - }); + it('should report false for a missing file and true for an existing file', async () => { + const lfs = new LocalFileSystem(tempDir); + expect(await lfs.fileExists('/tsconfig.json')).toBe(false); + + await fs.writeFile(path.join(tempDir, 'tsconfig.json'), '{}'); + expect(await lfs.fileExists('/tsconfig.json')).toBe(true); + }); + + it('should read /package.json and throw on a non-existent path', async () => { + const lfs = new LocalFileSystem(tempDir); + + const text = await lfs.readFile('/package.json'); + expect(JSON.parse(text).name).toBe('pkg'); + + await expect(lfs.readFile('/does-not-exist.json')).rejects.toBeTruthy(); + }); + + it('should return an empty list of package files when node_modules is missing', async () => { + const lfs = new LocalFileSystem(tempDir); + const files = await lfs.listPackageFiles(); + expect(files).toEqual([]); + }); + + it('should list package.json files in node_modules, including nested ones', async () => { + await fs.mkdir( + path.join(tempDir, 'node_modules', 'a', 'node_modules', 'b'), + { + recursive: true + } + ); + await fs.writeFile( + path.join(tempDir, 'node_modules', 'a', 'package.json'), + JSON.stringify({name: 'a', version: '1.0.0'}) + ); + await fs.writeFile( + path.join( + tempDir, + 'node_modules', + 'a', + 'node_modules', + 'b', + 'package.json' + ), + JSON.stringify({name: 'b', version: '1.0.0'}) + ); + + const lfs = new LocalFileSystem(tempDir); + const files = await lfs.listPackageFiles(); + + expect(files.some((p) => p.endsWith('/node_modules/a/package.json'))).toBe( + true + ); + expect( + files.some((p) => + p.endsWith('/node_modules/a/node_modules/b/package.json') + ) + ).toBe(true); + }); + + it('should report 0 install size without node_modules and a positive size when files exist', async () => { + const lfs = new LocalFileSystem(tempDir); + expect(await lfs.getInstallSize()).toBe(0); + + await fs.mkdir(path.join(tempDir, 'node_modules', 'x'), {recursive: true}); + await fs.writeFile( + path.join(tempDir, 'node_modules', 'x', 'f1.txt'), + 'abc' + ); + await fs.writeFile(path.join(tempDir, 'node_modules', 'x', 'f2.txt'), 'X'); + + const size = await lfs.getInstallSize(); + expect(size).toBeGreaterThan(0); }); }); diff --git a/src/test/tarball-file-system.test.ts b/src/test/tarball-file-system.test.ts index d5f0610..94a8722 100644 --- a/src/test/tarball-file-system.test.ts +++ b/src/test/tarball-file-system.test.ts @@ -1,53 +1,84 @@ -import {describe, it, expect, beforeEach, afterEach} from 'vitest'; -import {TarballFileSystem} from '../tarball-file-system.js'; -import {detectAndPack} from '../detect-and-pack-node.js'; -import * as fs from 'node:fs/promises'; +import {describe, it, expect, beforeEach, vi} from 'vitest'; import * as path from 'node:path'; -import {tmpdir} from 'node:os'; + +import {TarballFileSystem} from '../tarball-file-system.js'; + +const enc = (s: string) => new TextEncoder().encode(s); +let mockFiles: Array<{name: string; data: Uint8Array}>; +const mockRootDir = 'package'; + +vi.mock('@publint/pack', () => { + return { + unpack: vi.fn(async () => ({ + rootDir: mockRootDir, + files: mockFiles + })) + }; +}); describe('TarballFileSystem', () => { - let tempDir: string; + beforeEach(() => { + mockFiles = [ + { + name: path.posix.join(mockRootDir, 'package.json'), + data: enc(JSON.stringify({name: 'pkg', version: '1.0.0'})) + }, + { + name: path.posix.join(mockRootDir, 'tsconfig.json'), + data: enc('{}') + }, + { + name: path.posix.join(mockRootDir, 'node_modules/a/package.json'), + data: enc(JSON.stringify({name: 'a', version: '1.0.0'})) + }, + { + name: path.posix.join( + mockRootDir, + 'node_modules/a/node_modules/b/package.json' + ), + data: enc(JSON.stringify({name: 'b', version: '1.0.0'})) + }, + { + name: path.posix.join(mockRootDir, 'node_modules/a/readme.txt'), + data: enc('abc') + } + ]; + }); - beforeEach(async () => { - tempDir = await fs.mkdtemp(path.join(tmpdir(), 'reporter-test-')); + it('should report true for an existing file and false for a missing file', async () => { + const tfs = new TarballFileSystem(new ArrayBuffer(0)); + + expect(await tfs.fileExists('/tsconfig.json')).toBe(true); + expect(await tfs.fileExists('/does-not-exist.json')).toBe(false); + }); + + it('should read /package.json and throw on a non-existent path', async () => { + const tfs = new TarballFileSystem(new ArrayBuffer(0)); + + const text = await tfs.readFile('/package.json'); + expect(JSON.parse(text).name).toBe('pkg'); + + await expect(tfs.readFile('/nope.json')).rejects.toBeTruthy(); }); - afterEach(async () => { - await fs.rm(tempDir, {recursive: true, force: true}); + it('should list package.json files, including nested ones', async () => { + const tfs = new TarballFileSystem(new ArrayBuffer(0)); + const root = await tfs.getRootDir(); + const files = await tfs.listPackageFiles(); + + expect(files).toContain(path.posix.join(root, 'package.json')); + expect(files).toContain( + path.posix.join(root, 'node_modules/a/package.json') + ); + expect(files).toContain( + path.posix.join(root, 'node_modules/a/node_modules/b/package.json') + ); }); - describe('fileExists', () => { - it('should return false when file does not exist in tarball', async () => { - // Create a minimal package.json for the tarball - await fs.writeFile( - path.join(tempDir, 'package.json'), - JSON.stringify({ - name: 'test-package', - version: '1.0.0' - }) - ); - - const tarball = await detectAndPack(tempDir, 'npm'); - const fileSystem = new TarballFileSystem(tarball); - const hasConfig = await fileSystem.fileExists('/tsconfig.json'); - expect(hasConfig).toBe(false); - }); - - it('should return true when file exists in tarball', async () => { - // Create a minimal package.json for the tarball - await fs.writeFile( - path.join(tempDir, 'package.json'), - JSON.stringify({ - name: 'test-package', - version: '1.0.0' - }) - ); - - await fs.writeFile(path.join(tempDir, 'tsconfig.json'), '{}'); - const tarball = await detectAndPack(tempDir, 'npm'); - const fileSystem = new TarballFileSystem(tarball); - const hasConfig = await fileSystem.fileExists('/tsconfig.json'); - expect(hasConfig).toBe(true); - }); + it('should compute install size as the sum of file bytes from the unpacked tarball', async () => { + const tfs = new TarballFileSystem(new ArrayBuffer(0)); + const expected = mockFiles.reduce((n, f) => n + f.data.byteLength, 0); + const size = await tfs.getInstallSize(); + expect(size).toBe(expected); }); });