fix: retarget updater to @mosaic/mosaic (#384)
This commit was merged in pull request #384.
This commit is contained in:
@@ -1,7 +1,40 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { semverLt, formatUpdateNotice } from '../src/runtime/update-checker.js';
|
||||
import type { UpdateCheckResult } from '../src/runtime/update-checker.js';
|
||||
|
||||
const { execSyncMock, cacheFiles } = vi.hoisted(() => ({
|
||||
execSyncMock: vi.fn(),
|
||||
cacheFiles: new Map<string, string>(),
|
||||
}));
|
||||
|
||||
vi.mock('node:child_process', () => ({
|
||||
execSync: execSyncMock,
|
||||
}));
|
||||
|
||||
vi.mock('node:fs', () => ({
|
||||
existsSync: vi.fn((path: string) => cacheFiles.has(path)),
|
||||
mkdirSync: vi.fn(),
|
||||
readFileSync: vi.fn((path: string) => {
|
||||
const value = cacheFiles.get(path);
|
||||
if (value === undefined) {
|
||||
throw new Error(`ENOENT: ${path}`);
|
||||
}
|
||||
return value;
|
||||
}),
|
||||
writeFileSync: vi.fn((path: string, content: string) => {
|
||||
cacheFiles.set(path, content);
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('node:os', () => ({
|
||||
homedir: vi.fn(() => '/mock-home'),
|
||||
}));
|
||||
|
||||
async function importUpdateChecker() {
|
||||
vi.resetModules();
|
||||
return import('../src/runtime/update-checker.js');
|
||||
}
|
||||
|
||||
describe('semverLt', () => {
|
||||
it('returns true when a < b', () => {
|
||||
expect(semverLt('0.0.1', '0.0.2')).toBe(true);
|
||||
@@ -25,6 +58,11 @@ describe('semverLt', () => {
|
||||
});
|
||||
|
||||
describe('formatUpdateNotice', () => {
|
||||
beforeEach(() => {
|
||||
execSyncMock.mockReset();
|
||||
cacheFiles.clear();
|
||||
});
|
||||
|
||||
it('returns empty string when up to date', () => {
|
||||
const result: UpdateCheckResult = {
|
||||
current: '1.0.0',
|
||||
@@ -49,4 +87,109 @@ describe('formatUpdateNotice', () => {
|
||||
expect(notice).toContain('0.1.0');
|
||||
expect(notice).toContain('Update available');
|
||||
});
|
||||
|
||||
it('uses @mosaic/mosaic hints for modern installs', async () => {
|
||||
execSyncMock.mockImplementation((command: string) => {
|
||||
if (command.includes('ls -g --depth=0 --json')) {
|
||||
return JSON.stringify({
|
||||
dependencies: {
|
||||
'@mosaic/mosaic': { version: '0.0.17' },
|
||||
'@mosaic/cli': { version: '0.0.16' },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/mosaic version')) {
|
||||
return '0.0.18';
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected command: ${command}`);
|
||||
});
|
||||
|
||||
const { checkForUpdate } = await importUpdateChecker();
|
||||
const result = checkForUpdate({ skipCache: true });
|
||||
const notice = formatUpdateNotice(result);
|
||||
|
||||
expect(result.current).toBe('0.0.17');
|
||||
expect(result.latest).toBe('0.0.18');
|
||||
expect(notice).toContain('@mosaic/mosaic@latest');
|
||||
expect(notice).not.toContain('@mosaic/cli@latest');
|
||||
});
|
||||
|
||||
it('falls back to @mosaic/mosaic for legacy @mosaic/cli installs when cli is unavailable', async () => {
|
||||
execSyncMock.mockImplementation((command: string) => {
|
||||
if (command.includes('ls -g --depth=0 --json')) {
|
||||
return JSON.stringify({
|
||||
dependencies: {
|
||||
'@mosaic/cli': { version: '0.0.16' },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/cli version')) {
|
||||
throw new Error('not found');
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/mosaic version')) {
|
||||
return '0.0.17';
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected command: ${command}`);
|
||||
});
|
||||
|
||||
const { checkForUpdate } = await importUpdateChecker();
|
||||
const result = checkForUpdate({ skipCache: true });
|
||||
const notice = formatUpdateNotice(result);
|
||||
|
||||
expect(result.current).toBe('0.0.16');
|
||||
expect(result.latest).toBe('0.0.17');
|
||||
expect(notice).toContain('@mosaic/mosaic@latest');
|
||||
});
|
||||
|
||||
it('does not reuse a cached modern-package result for a legacy install', async () => {
|
||||
let installedPackage = '@mosaic/mosaic';
|
||||
|
||||
execSyncMock.mockImplementation((command: string) => {
|
||||
if (command.includes('ls -g --depth=0 --json')) {
|
||||
return JSON.stringify({
|
||||
dependencies:
|
||||
installedPackage === '@mosaic/mosaic'
|
||||
? { '@mosaic/mosaic': { version: '0.0.17' } }
|
||||
: { '@mosaic/cli': { version: '0.0.16' } },
|
||||
});
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/mosaic version')) {
|
||||
return installedPackage === '@mosaic/mosaic' ? '0.0.18' : '0.0.17';
|
||||
}
|
||||
|
||||
if (command.includes('view @mosaic/cli version')) {
|
||||
throw new Error('not found');
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected command: ${command}`);
|
||||
});
|
||||
|
||||
const { checkForUpdate } = await importUpdateChecker();
|
||||
|
||||
const modernResult = checkForUpdate();
|
||||
installedPackage = '@mosaic/cli';
|
||||
const legacyResult = checkForUpdate();
|
||||
|
||||
expect(modernResult.currentPackage).toBe('@mosaic/mosaic');
|
||||
expect(modernResult.targetPackage).toBe('@mosaic/mosaic');
|
||||
expect(modernResult.latest).toBe('0.0.18');
|
||||
|
||||
expect(legacyResult.currentPackage).toBe('@mosaic/cli');
|
||||
expect(legacyResult.targetPackage).toBe('@mosaic/mosaic');
|
||||
expect(legacyResult.latest).toBe('0.0.17');
|
||||
expect(execSyncMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining('view @mosaic/cli version'),
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(execSyncMock).toHaveBeenCalledWith(
|
||||
expect.stringContaining('view @mosaic/mosaic version'),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user