Files
smartupdate/test/test.node+bun+deno.ts
T

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();