GitNexus/gitnexus-desktop/test/main-utils.test.ts
Sparsh 610a102397 feat: add validation job to GitHub Actions for desktop packaging
- Introduced a new job `validate` in the GitHub Actions workflow to validate the desktop package.
- Added steps for checking out the repository, setting up Node.js, installing dependencies, type checking, and running unit tests.
- Updated the `package` job to depend on the `validate` job.

chore: update package-lock.json and package.json for vitest integration

- Added `vitest` as a development dependency in `package.json`.
- Updated `typecheck` script to include a new TypeScript configuration for vitest.
- Updated `package-lock.json` to reflect the new dependencies.

refactor: modularize runtime path handling in main process

- Created a new module `runtime-paths.ts` to handle static path normalization and request handling.
- Refactored `main.ts` to utilize the new `normalizeStaticPath`, `getRequestedPath`, and `getPackagedRendererEntry` functions.

test: add unit tests for runtime path utilities

- Implemented tests for `normalizeStaticPath`, `getRequestedPath`, and `getPackagedRendererEntry` using Vitest.
- Ensured coverage for directory traversal attempts and null-byte paths.

chore: configure Vitest for testing

- Added `vitest.config.ts` for Vitest configuration.
- Created `tsconfig.vitest.json` to include types for Vitest in TypeScript compilation.

Co-authored-by: Copilot <copilot@github.com>
2026-04-24 13:45:23 +05:30

61 lines
1.8 KiB
TypeScript

import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
getPackagedRendererEntry,
getRequestedPath,
normalizeStaticPath,
} from '../src/main/runtime-paths.js';
describe('normalizeStaticPath', () => {
let rootDir: string;
beforeEach(() => {
rootDir = mkdtempSync(path.join(os.tmpdir(), 'gitnexus-desktop-static-'));
writeFileSync(path.join(rootDir, 'index.html'), '<html></html>');
writeFileSync(path.join(rootDir, 'asset.js'), 'console.log("ok");');
});
afterEach(() => {
rmSync(rootDir, { force: true, recursive: true });
});
it('rejects directory traversal attempts', () => {
expect(normalizeStaticPath(rootDir, '/../../etc/passwd')).toBeNull();
});
it('rejects null-byte paths', () => {
expect(normalizeStaticPath(rootDir, '/\0asset.js')).toBeNull();
});
it('returns a real asset path when the file exists', () => {
expect(normalizeStaticPath(rootDir, '/asset.js')).toBe(path.join(rootDir, 'asset.js'));
});
it('falls back to index.html for extensionless routes', () => {
expect(normalizeStaticPath(rootDir, '/desktop')).toBe(path.join(rootDir, 'index.html'));
});
});
describe('getRequestedPath', () => {
it('returns null for malformed percent-encoding', () => {
expect(getRequestedPath('/%GG')).toBeNull();
});
it('returns null for decoded null bytes', () => {
expect(getRequestedPath('/%00')).toBeNull();
});
});
describe('getPackagedRendererEntry', () => {
it('resolves the packaged renderer relative to dist/main', () => {
const rendererEntry = getPackagedRendererEntry(path.join('dist', 'main'));
expect(path.normalize(rendererEntry)).toBe(
path.normalize(path.join('dist', 'renderer', 'index.html')),
);
});
});