Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
149 lines
5.9 KiB
JavaScript
149 lines
5.9 KiB
JavaScript
'use strict';
|
|
|
|
// Focused unit tests for the path-separator helpers on shell-command-projection
|
|
// (toPosixPath / toNativePath / posixNormalize). These are drop-in replacements
|
|
// for open-coded `.replace(/\\/g, '/')` and
|
|
// `process.platform === 'win32' ? x.replace(/\//g, '\\') : x`
|
|
// call sites — see shell-command-projection.cts for the platform-relative contract.
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const path = require('node:path');
|
|
const fc = require('fast-check');
|
|
|
|
const { toPosixPath, toNativePath, posixNormalize } = require('../msd-core/bin/lib/shell-command-projection.cjs');
|
|
|
|
describe('toPosixPath', () => {
|
|
test('plain relative path: already-POSIX segments pass through unchanged', () => {
|
|
assert.equal(toPosixPath('a/b/c'), 'a/b/c');
|
|
});
|
|
|
|
test('plain relative path built with the native separator normalizes to POSIX segments', () => {
|
|
const native = path.join('alpha', 'beta', 'gamma');
|
|
assert.equal(toPosixPath(native), ['alpha', 'beta', 'gamma'].join('/'));
|
|
});
|
|
|
|
test('absolute path: segment list matches the native path split on path.sep', () => {
|
|
const abs = path.resolve('alpha', 'beta');
|
|
assert.equal(toPosixPath(abs), abs.split(path.sep).join('/'));
|
|
});
|
|
|
|
test('idempotency: applying twice equals applying once', () => {
|
|
const native = path.join('alpha', 'beta', 'gamma');
|
|
const once = toPosixPath(native);
|
|
const twice = toPosixPath(once);
|
|
assert.equal(once, twice);
|
|
});
|
|
|
|
test('empty string in, empty string out', () => {
|
|
assert.equal(toPosixPath(''), '');
|
|
});
|
|
|
|
test('does not corrupt a POSIX-style input containing a literal backslash-free path', () => {
|
|
// A string that already uses only '/' as its separator and contains no
|
|
// occurrence of path.sep-as-backslash must survive unchanged regardless of
|
|
// the host platform's path.sep (no-op on POSIX; nothing to split on Windows
|
|
// either, since there is no backslash present).
|
|
const alreadyPosix = 'already/posix/style/path';
|
|
assert.equal(toPosixPath(alreadyPosix), alreadyPosix);
|
|
});
|
|
|
|
test('property: toPosixPath output never contains path.sep when path.sep differs from "/"', () => {
|
|
fc.assert(
|
|
fc.property(fc.array(fc.stringMatching(/^[a-zA-Z0-9_-]+$/), { minLength: 1, maxLength: 5 }), (segments) => {
|
|
const native = segments.join(path.sep);
|
|
const posix = toPosixPath(native);
|
|
if (path.sep !== '/') {
|
|
assert.ok(!posix.includes(path.sep));
|
|
}
|
|
assert.equal(posix, segments.join('/'));
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('toNativePath', () => {
|
|
test('plain relative path: already-native segments pass through unchanged', () => {
|
|
const native = path.join('alpha', 'beta', 'gamma');
|
|
assert.equal(toNativePath(native), native);
|
|
});
|
|
|
|
test('POSIX-style relative path converts to native segments', () => {
|
|
const posix = 'alpha/beta/gamma';
|
|
assert.equal(toNativePath(posix), ['alpha', 'beta', 'gamma'].join(path.sep));
|
|
});
|
|
|
|
test('absolute path: round-trips back to the original POSIX form via split/join', () => {
|
|
const posixAbs = path.posix.resolve('/', 'alpha', 'beta');
|
|
const native = toNativePath(posixAbs);
|
|
assert.equal(native.split(path.sep).join('/'), posixAbs);
|
|
});
|
|
|
|
test('idempotency: applying twice equals applying once', () => {
|
|
const posix = 'alpha/beta/gamma';
|
|
const once = toNativePath(posix);
|
|
const twice = toNativePath(once);
|
|
assert.equal(once, twice);
|
|
});
|
|
|
|
test('empty string in, empty string out', () => {
|
|
assert.equal(toNativePath(''), '');
|
|
});
|
|
|
|
test('does not corrupt an already-native-style input containing no POSIX separator', () => {
|
|
// A string that already uses only path.sep as its separator (and contains
|
|
// no '/' occurrence) must survive unchanged: no-op on POSIX (path.sep is
|
|
// '/', so it IS the separator being normalized to); on Windows there is no
|
|
// '/' present to split on, so the single-element join reproduces the input.
|
|
const alreadyNative = ['already', 'native', 'style', 'path'].join(path.sep);
|
|
assert.equal(toNativePath(alreadyNative), alreadyNative);
|
|
});
|
|
|
|
test('property: toPosixPath and toNativePath round-trip a POSIX-style path through both conversions', () => {
|
|
fc.assert(
|
|
fc.property(fc.array(fc.stringMatching(/^[a-zA-Z0-9_-]+$/), { minLength: 1, maxLength: 5 }), (segments) => {
|
|
const posix = segments.join('/');
|
|
assert.equal(toPosixPath(toNativePath(posix)), posix);
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('posixNormalize', () => {
|
|
test('unconditional conversion: literal backslashes always become forward slashes, regardless of host platform', () => {
|
|
// Unlike toPosixPath (which splits on path.sep — a no-op on POSIX hosts),
|
|
// posixNormalize must convert backslashes even when running on a POSIX
|
|
// host, because the input represents a TARGET platform's path, not this
|
|
// machine's filesystem path.
|
|
assert.equal(posixNormalize('alpha\\beta\\gamma'), 'alpha/beta/gamma');
|
|
});
|
|
|
|
test('mixed separators: only backslashes are converted, forward slashes are left alone', () => {
|
|
assert.equal(posixNormalize('alpha\\beta/gamma\\delta'), 'alpha/beta/gamma/delta');
|
|
});
|
|
|
|
test('already-POSIX input with no backslash passes through unchanged', () => {
|
|
const alreadyPosix = 'already/posix/style/path';
|
|
assert.equal(posixNormalize(alreadyPosix), alreadyPosix);
|
|
});
|
|
|
|
test('idempotency: applying twice equals applying once', () => {
|
|
const once = posixNormalize('alpha\\beta\\gamma');
|
|
const twice = posixNormalize(once);
|
|
assert.equal(once, twice);
|
|
});
|
|
|
|
test('empty string in, empty string out', () => {
|
|
assert.equal(posixNormalize(''), '');
|
|
});
|
|
|
|
test('property: posixNormalize output never contains a backslash', () => {
|
|
fc.assert(
|
|
fc.property(fc.string(), (input) => {
|
|
const normalized = posixNormalize(input);
|
|
assert.ok(!normalized.includes('\\'));
|
|
}),
|
|
);
|
|
});
|
|
});
|