215 lines
7.9 KiB
TypeScript
215 lines
7.9 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import type * as smartnpm from '@push.rocks/smartnpm';
|
|
import * as smartupdate from '../ts/index.js';
|
|
|
|
const createMockedSmartUpdate = (versionsArg: Record<string, string>) => {
|
|
const checkedPackages: string[] = [];
|
|
const testSmartUpdate = new smartupdate.SmartUpdate({
|
|
logLevel: 'SILENT',
|
|
noColor: true,
|
|
cacheDuration: { minutes: 30 },
|
|
cacheStore: { storeType: 'ephemeral' },
|
|
});
|
|
|
|
testSmartUpdate.npmRegistry.getPackageInfo = async (packageNameArg: string) => {
|
|
checkedPackages.push(packageNameArg);
|
|
const version = versionsArg[packageNameArg];
|
|
if (!version) {
|
|
throw new Error(`Package not found: ${packageNameArg}`);
|
|
}
|
|
return {
|
|
name: packageNameArg,
|
|
version,
|
|
} as unknown as smartnpm.NpmPackage;
|
|
};
|
|
|
|
return { testSmartUpdate, checkedPackages };
|
|
};
|
|
|
|
tap.test('backward compatibility: should create an instance of SmartUpdate', async () => {
|
|
const { testSmartUpdate } = createMockedSmartUpdate({ lodash: '4.17.21' });
|
|
expect(testSmartUpdate).toBeInstanceOf(smartupdate.SmartUpdate);
|
|
});
|
|
|
|
tap.test('backward compatibility: should check for a npm module using old API', async () => {
|
|
const { testSmartUpdate } = createMockedSmartUpdate({ lodash: '4.17.21' });
|
|
const result = await testSmartUpdate.check('lodash', '1.0.5');
|
|
expect(result).toBeTrue();
|
|
});
|
|
|
|
tap.test('modern API: checkForUpdate should return rich result object', async () => {
|
|
const { testSmartUpdate } = createMockedSmartUpdate({ lodash: '4.17.21' });
|
|
const result = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'lodash',
|
|
currentVersion: '1.0.0',
|
|
cacheStrategy: 'never',
|
|
});
|
|
|
|
expect(result).toBeTypeOf('object');
|
|
expect(result.status).toEqual('update-available');
|
|
expect(result.packageName).toEqual('lodash');
|
|
expect(result.currentVersion).toEqual('1.0.0');
|
|
expect(result.latestVersion).toEqual('4.17.21');
|
|
expect(result.checkTime).toBeInstanceOf(Date);
|
|
expect(result.cacheHit).toBeFalse();
|
|
});
|
|
|
|
tap.test('modern API: checkForUpdate with up-to-date version', async () => {
|
|
const { testSmartUpdate } = createMockedSmartUpdate({ '@push.rocks/smartversion': '3.1.0' });
|
|
const result = await testSmartUpdate.checkForUpdate({
|
|
packageName: '@push.rocks/smartversion',
|
|
currentVersion: '999.999.999',
|
|
cacheStrategy: 'never',
|
|
});
|
|
|
|
expect(result.status).toEqual('up-to-date');
|
|
expect(result.latestVersion).toEqual('3.1.0');
|
|
});
|
|
|
|
tap.test('modern API: getLatestVersion utility method', async () => {
|
|
const { testSmartUpdate } = createMockedSmartUpdate({ lodash: '4.17.21' });
|
|
const latestVersion = await testSmartUpdate.getLatestVersion('lodash');
|
|
expect(latestVersion).toEqual('4.17.21');
|
|
});
|
|
|
|
tap.test('modern API: error handling for non-existent package', async () => {
|
|
const { testSmartUpdate } = createMockedSmartUpdate({});
|
|
const result = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'this-package-definitely-does-not-exist-12345',
|
|
currentVersion: '1.0.0',
|
|
cacheStrategy: 'never',
|
|
});
|
|
|
|
expect(result.status).toEqual('error');
|
|
expect(result.error).toBeInstanceOf(smartupdate.PackageNotFoundError);
|
|
});
|
|
|
|
tap.test('modern API: invalid versions are reported before registry access', async () => {
|
|
const { testSmartUpdate, checkedPackages } = createMockedSmartUpdate({ lodash: '4.17.21' });
|
|
const result = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'lodash',
|
|
currentVersion: 'not-a-version',
|
|
cacheStrategy: 'never',
|
|
});
|
|
|
|
expect(result.status).toEqual('error');
|
|
expect(result.error).toBeInstanceOf(smartupdate.InvalidVersionError);
|
|
expect(checkedPackages).toHaveLength(0);
|
|
});
|
|
|
|
tap.test('modern API: invalid package names are reported before registry access', async () => {
|
|
const { testSmartUpdate, checkedPackages } = createMockedSmartUpdate({ lodash: '4.17.21' });
|
|
const result = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'invalid package name',
|
|
currentVersion: '1.0.0',
|
|
cacheStrategy: 'never',
|
|
});
|
|
|
|
expect(result.status).toEqual('error');
|
|
expect(result.error).toBeInstanceOf(smartupdate.InvalidPackageNameError);
|
|
expect(checkedPackages).toHaveLength(0);
|
|
});
|
|
|
|
tap.test('modern API: caches update-available results for time-based checks', async () => {
|
|
const { testSmartUpdate, checkedPackages } = createMockedSmartUpdate({ express: '5.0.0' });
|
|
|
|
const result1 = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'express',
|
|
currentVersion: '1.0.0',
|
|
cacheStrategy: 'time-based',
|
|
});
|
|
const result2 = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'express',
|
|
currentVersion: '1.0.0',
|
|
cacheStrategy: 'time-based',
|
|
});
|
|
|
|
expect(result1.status).toEqual('update-available');
|
|
expect(result2.status).toEqual('check-skipped');
|
|
expect(result2.cacheHit).toBeTrue();
|
|
expect(result2.latestVersion).toEqual('5.0.0');
|
|
expect(checkedPackages).toHaveLength(1);
|
|
});
|
|
|
|
tap.test('modern API: caches up-to-date results for time-based checks', async () => {
|
|
const { testSmartUpdate, checkedPackages } = createMockedSmartUpdate({ express: '5.0.0' });
|
|
|
|
const result1 = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'express',
|
|
currentVersion: '5.0.0',
|
|
cacheStrategy: 'time-based',
|
|
});
|
|
const result2 = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'express',
|
|
currentVersion: '5.0.0',
|
|
cacheStrategy: 'time-based',
|
|
});
|
|
|
|
expect(result1.status).toEqual('up-to-date');
|
|
expect(result2.status).toEqual('check-skipped');
|
|
expect(result2.cacheHit).toBeTrue();
|
|
expect(result2.latestVersion).toEqual('5.0.0');
|
|
expect(checkedPackages).toHaveLength(1);
|
|
});
|
|
|
|
tap.test('modern API: cache entries are scoped to the checked current version', async () => {
|
|
const { testSmartUpdate, checkedPackages } = createMockedSmartUpdate({ express: '5.0.0' });
|
|
|
|
const result1 = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'express',
|
|
currentVersion: '1.0.0',
|
|
cacheStrategy: 'time-based',
|
|
});
|
|
const result2 = await testSmartUpdate.checkForUpdate({
|
|
packageName: 'express',
|
|
currentVersion: '5.0.0',
|
|
cacheStrategy: 'time-based',
|
|
});
|
|
|
|
expect(result1.status).toEqual('update-available');
|
|
expect(result2.status).toEqual('up-to-date');
|
|
expect(result2.cacheHit).toBeFalse();
|
|
expect(checkedPackages).toHaveLength(2);
|
|
});
|
|
|
|
tap.test('modern API: checkForCli returns true for cached known updates', async () => {
|
|
const { testSmartUpdate, checkedPackages } = createMockedSmartUpdate({ 'my-cli': '2.0.0' });
|
|
|
|
const result1 = await testSmartUpdate.checkForCli('my-cli', '1.0.0');
|
|
const result2 = await testSmartUpdate.checkForCli('my-cli', '1.0.0');
|
|
|
|
expect(result1).toBeTrue();
|
|
expect(result2).toBeTrue();
|
|
expect(checkedPackages).toHaveLength(1);
|
|
});
|
|
|
|
tap.test('cache manager: clears package-specific and complete cache data', async () => {
|
|
const cacheManager = new smartupdate.UpdateCacheManager({
|
|
durationMs: 60_000,
|
|
storeType: 'ephemeral',
|
|
});
|
|
|
|
await cacheManager.setCached('one', cacheManager.createCacheStatus('1.0.0'));
|
|
await cacheManager.setCached('two', cacheManager.createCacheStatus('2.0.0'));
|
|
|
|
await cacheManager.clearCache('one');
|
|
expect(await cacheManager.getCached('one')).toBeNull();
|
|
expect(await cacheManager.getCached('two')).toBeTypeOf('object');
|
|
|
|
await cacheManager.clearCache();
|
|
expect(await cacheManager.getCached('two')).toBeNull();
|
|
});
|
|
|
|
tap.test('modern API: exports all runtime classes and constants', async () => {
|
|
expect(smartupdate.SmartUpdate).toBeTypeOf('function');
|
|
expect(smartupdate.UpdateCacheManager).toBeTypeOf('function');
|
|
expect(smartupdate.UpdateNotifier).toBeTypeOf('function');
|
|
expect(smartupdate.RegistryUnavailableError).toBeTypeOf('function');
|
|
expect(smartupdate.PackageNotFoundError).toBeTypeOf('function');
|
|
expect(smartupdate.InvalidVersionError).toBeTypeOf('function');
|
|
expect(smartupdate.InvalidPackageNameError).toBeTypeOf('function');
|
|
expect(smartupdate.LOG_LEVELS).toBeTypeOf('object');
|
|
});
|
|
|
|
export default tap.start();
|