Spaces:
Running
Running
File size: 6,163 Bytes
704e84f a2c232b 704e84f a2c232b 704e84f a2c232b 704e84f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | import { inspectDependencyInstallation } from './dependency-installation.mjs';
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, it } from 'node:test';
const ROOT_LOCKFILE = {
name: 'fixture',
lockfileVersion: 3,
packages: {
'': {
name: 'fixture',
dependencies: { demo: '1.0.0' },
devDependencies: { '@scope/tool': '2.0.0' }
},
'node_modules/demo': { version: '1.0.0' },
'node_modules/@scope/tool': { version: '2.0.0' }
}
};
const LOCAL_LINK_ROOT_LOCKFILE = {
name: 'fixture',
lockfileVersion: 3,
packages: {
'': {
name: 'fixture',
dependencies: { demo: 'file:vendor/demo' }
},
'node_modules/demo': {
resolved: 'vendor/demo',
link: true
},
'vendor/demo': {
name: 'demo',
version: '1.0.0'
}
}
};
async function createFixture({ rootLockfile = ROOT_LOCKFILE, hiddenLockfile, manifests = {} } = {}) {
const root = await mkdtemp(join(tmpdir(), 'gipc-dependency-installation-'));
await mkdir(join(root, 'node_modules'), { recursive: true });
await writeFile(join(root, 'package-lock.json'), JSON.stringify(rootLockfile));
if (hiddenLockfile !== undefined) {
await writeFile(join(root, 'node_modules', '.package-lock.json'), JSON.stringify(hiddenLockfile));
}
for (const [name, manifest] of Object.entries(manifests)) {
const packageDirectory = join(root, 'node_modules', name);
await mkdir(packageDirectory, { recursive: true });
await writeFile(join(packageDirectory, 'package.json'), JSON.stringify(manifest));
}
return root;
}
function buildHiddenLockfile(packages = ROOT_LOCKFILE.packages) {
return {
lockfileVersion: 3,
packages: Object.fromEntries(Object.entries(packages).filter(([path]) => path !== ''))
};
}
describe('dependency installation inspection', () => {
it('detects an interrupted install when only the hidden lockfile remains', async () => {
const root = await createFixture({ hiddenLockfile: buildHiddenLockfile() });
try {
const state = inspectDependencyInstallation(root);
assert.equal(state.ok, false);
assert.equal(state.reason, 'direct_package_missing');
assert.deepEqual(state.missingPackages, ['@scope/tool', 'demo']);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it('detects a missing hidden lockfile', async () => {
const root = await createFixture();
try {
const state = inspectDependencyInstallation(root);
assert.equal(state.ok, false);
assert.equal(state.reason, 'hidden_lockfile_missing');
assert.deepEqual(state.missingPackages, []);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it('detects a direct dependency version mismatch', async () => {
const root = await createFixture({
hiddenLockfile: buildHiddenLockfile(),
manifests: {
demo: { name: 'demo', version: '1.1.0' },
'@scope/tool': { name: '@scope/tool', version: '2.0.0' }
}
});
try {
const state = inspectDependencyInstallation(root);
assert.equal(state.ok, false);
assert.equal(state.reason, 'direct_package_version_mismatch');
assert.deepEqual(state.versionMismatches, [{ name: 'demo', expected: '1.0.0', actual: '1.1.0' }]);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it('accepts direct dependencies matching both lockfiles', async () => {
const root = await createFixture({
hiddenLockfile: buildHiddenLockfile(),
manifests: {
demo: { name: 'demo', version: '1.0.0' },
'@scope/tool': { name: '@scope/tool', version: '2.0.0' }
}
});
try {
const state = inspectDependencyInstallation(root);
assert.equal(state.ok, true);
assert.deepEqual(state.directDependencies, ['@scope/tool', 'demo']);
assert.equal(state.reason, undefined);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it('accepts a local file dependency whose link target matches both lockfiles', async () => {
const root = await createFixture({
rootLockfile: LOCAL_LINK_ROOT_LOCKFILE,
hiddenLockfile: buildHiddenLockfile(LOCAL_LINK_ROOT_LOCKFILE.packages),
manifests: {
demo: { name: 'demo', version: '1.0.0' }
}
});
try {
const state = inspectDependencyInstallation(root);
assert.equal(state.ok, true);
assert.deepEqual(state.directDependencies, ['demo']);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it('detects a local file dependency version mismatch in the hidden lockfile', async () => {
const hiddenLockfile = buildHiddenLockfile({
...LOCAL_LINK_ROOT_LOCKFILE.packages,
'vendor/demo': { name: 'demo', version: '1.1.0' }
});
const root = await createFixture({
rootLockfile: LOCAL_LINK_ROOT_LOCKFILE,
hiddenLockfile,
manifests: {
demo: { name: 'demo', version: '1.0.0' }
}
});
try {
const state = inspectDependencyInstallation(root);
assert.equal(state.ok, false);
assert.equal(state.reason, 'hidden_lockfile_package_mismatch');
assert.deepEqual(state.hiddenLockMismatches, [{ name: 'demo', expected: '1.0.0', actual: '1.1.0' }]);
} finally {
await rm(root, { force: true, recursive: true });
}
});
});
|