mirror of
https://github.com/zhu1090093659/dsh-web.git
synced 2026-09-28 14:24:03 +08:00
The guard rejected any satellite version above the range's base (for example 0.4.3 under ^0.4.2) because the caret branch demanded component equality with the base before its upper-bound check. Rewrite the comparison as ordered lower/upper bounds: caret bumps the leftmost non-zero component, tilde the minor, x-ranges their first hole, and prerelease suffixes compare by release core. Covers the shapes the aggregate declares (caret, tilde, OR alternations, x-ranges, exact pins); regression tests pin 0.4.3-in-^0.4.2 accepted and 0.5.0 rejected.
160 lines
6.7 KiB
JavaScript
160 lines
6.7 KiB
JavaScript
/**
|
|
* Unit tests for the link-profile helpers: the pure link-state decision logic,
|
|
* the bundle patch reader, and the satellite package walk.
|
|
*
|
|
* Importing link-profile.mjs must not execute main() (it is guarded by the
|
|
* entry-script check), so these tests never touch the real ~/.dsh profile.
|
|
*/
|
|
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { decideLinkAction, bundlePatchChildNames, satellitePackages, decideAggregateRelink } from './link-profile.mjs'
|
|
|
|
const TARGET = '../../dsh-web-ui/packages/dsh-web-ui'
|
|
|
|
test('missing -> create', () => {
|
|
assert.equal(decideLinkAction('missing', TARGET, null), 'create')
|
|
})
|
|
|
|
test('symlink to target -> keep', () => {
|
|
assert.equal(decideLinkAction('symlink', TARGET, TARGET), 'keep')
|
|
})
|
|
|
|
test('symlink to other -> replace', () => {
|
|
assert.equal(decideLinkAction('symlink', TARGET, '../something-else'), 'replace')
|
|
})
|
|
|
|
test('broken symlink -> replace', () => {
|
|
assert.equal(decideLinkAction('symlink', TARGET, null), 'replace')
|
|
})
|
|
|
|
test('real file -> skip', () => {
|
|
assert.equal(decideLinkAction('file', TARGET, null), 'skip-report')
|
|
})
|
|
|
|
test('real dir -> skip', () => {
|
|
assert.equal(decideLinkAction('dir', TARGET, null), 'skip-report')
|
|
})
|
|
|
|
test('bundle patch child names include scoped and plain plugin rows', () => {
|
|
const patch = `- id: session-persistence-jsonl
|
|
disabled: true
|
|
|
|
- insert:
|
|
- id: session-rdb
|
|
name: '@morlay/session-rdb'
|
|
- id: ui-conversation-message-actions
|
|
name: '@morlay/ui-conversation-message-actions'
|
|
- id: better-sidebar
|
|
name: 'dsh-better-sidebar'
|
|
`
|
|
assert.deepEqual(bundlePatchChildNames(patch), [
|
|
'@morlay/session-rdb',
|
|
'@morlay/ui-conversation-message-actions',
|
|
'dsh-better-sidebar',
|
|
])
|
|
})
|
|
|
|
test('satellite packages: only family-scoped satellites/* with a readable manifest are collected', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'link-profile-satellites-'))
|
|
try {
|
|
const write = (name, manifest) => {
|
|
const dir = join(root, 'satellites', name)
|
|
mkdirSync(dir, { recursive: true })
|
|
if (manifest !== null) writeFileSync(join(dir, 'package.json'), manifest)
|
|
}
|
|
write('dsh-pet', JSON.stringify({ name: '@linxin666/dsh-pet' }))
|
|
write('dsh-community-plugins', JSON.stringify({ name: '@linxin666/dsh-client-ui-community-plugins' }))
|
|
write('third-party', JSON.stringify({ name: 'some-other-package' }))
|
|
write('no-manifest', null)
|
|
write('broken-manifest', '{ not json')
|
|
assert.deepEqual(satellitePackages(root).map((p) => p.name), [
|
|
'dsh-client-ui-community-plugins',
|
|
'dsh-pet',
|
|
])
|
|
assert.equal(satellitePackages(join(root, 'absent')).length, 0)
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
|
|
const STORE_LINK = '../../../../node_modules/.pnpm/@linxin666+dsh-client-ui-skin-center@0.4.2_react@18.3.1/node_modules/@linxin666/dsh-client-ui-skin-center'
|
|
|
|
test('aggregate relink: replaces a pnpm-store symlink when the satellite is built and in range', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'link-profile-relink-'))
|
|
try {
|
|
const satellite = join(root, 'satellites', 'dsh-skins')
|
|
mkdirSync(join(satellite, 'lib'), { recursive: true })
|
|
writeFileSync(join(satellite, 'package.json'), JSON.stringify({ name: '@linxin666/dsh-client-ui-skin-center', version: '0.4.2' }))
|
|
writeFileSync(join(satellite, 'lib', 'index.js'), 'export {}')
|
|
assert.equal(decideAggregateRelink('symlink', STORE_LINK, satellite, '^0.4.2'), 'replace')
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('aggregate relink: keeps a link that already points outside the store', () => {
|
|
assert.equal(decideAggregateRelink('symlink', '../../../satellites/dsh-skins', '/nonexistent', '^0.4.2'), 'keep')
|
|
})
|
|
|
|
test('aggregate relink: never touches a real file or directory', () => {
|
|
assert.equal(decideAggregateRelink('file', STORE_LINK, '/nonexistent', '^0.4.2'), 'skip-report')
|
|
assert.equal(decideAggregateRelink('dir', STORE_LINK, '/nonexistent', '^0.4.2'), 'skip-report')
|
|
assert.equal(decideAggregateRelink('missing', null, '/nonexistent', '^0.4.2'), 'skip-report')
|
|
})
|
|
|
|
test('aggregate relink: skips a satellite without a built lib', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'link-profile-relink-'))
|
|
try {
|
|
const satellite = join(root, 'satellites', 'dsh-skins')
|
|
mkdirSync(satellite, { recursive: true })
|
|
writeFileSync(join(satellite, 'package.json'), JSON.stringify({ name: '@linxin666/dsh-client-ui-skin-center', version: '0.4.2' }))
|
|
assert.equal(decideAggregateRelink('symlink', STORE_LINK, satellite, '^0.4.2'), 'skip-report')
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('aggregate relink: skips a satellite outside the declared range', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'link-profile-relink-'))
|
|
try {
|
|
const satellite = join(root, 'satellites', 'dsh-skins')
|
|
mkdirSync(join(satellite, 'lib'), { recursive: true })
|
|
writeFileSync(join(satellite, 'package.json'), JSON.stringify({ name: '@linxin666/dsh-client-ui-skin-center', version: '0.5.0' }))
|
|
writeFileSync(join(satellite, 'lib', 'index.js'), 'export {}')
|
|
assert.equal(decideAggregateRelink('symlink', STORE_LINK, satellite, '^0.4.2'), 'skip-report')
|
|
assert.equal(decideAggregateRelink('symlink', STORE_LINK, satellite, '^0.4.2 || ^0.5.0'), 'replace')
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('aggregate relink: accepts a newer patch satellite under a caret range', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'link-profile-relink-'))
|
|
try {
|
|
const satellite = join(root, 'satellites', 'dsh-skins')
|
|
mkdirSync(join(satellite, 'lib'), { recursive: true })
|
|
writeFileSync(join(satellite, 'package.json'), JSON.stringify({ name: '@linxin666/dsh-client-ui-skin-center', version: '0.4.3' }))
|
|
writeFileSync(join(satellite, 'lib', 'index.js'), 'export {}')
|
|
assert.equal(decideAggregateRelink('symlink', STORE_LINK, satellite, '^0.4.2'), 'replace')
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('aggregate relink: caret range still rejects the next minor', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'link-profile-relink-'))
|
|
try {
|
|
const satellite = join(root, 'satellites', 'dsh-skins')
|
|
mkdirSync(join(satellite, 'lib'), { recursive: true })
|
|
writeFileSync(join(satellite, 'package.json'), JSON.stringify({ name: '@linxin666/dsh-client-ui-skin-center', version: '0.5.0' }))
|
|
writeFileSync(join(satellite, 'lib', 'index.js'), 'export {}')
|
|
assert.equal(decideAggregateRelink('symlink', STORE_LINK, satellite, '^0.4.2'), 'skip-report')
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|