Files
immich-folio/lib/__tests__/updateCheck.test.ts

91 lines
3.5 KiB
TypeScript
Raw Normal View History

import { describe, it, expect } from 'vitest';
import { compareVersions, isNewerVersion, parseLatestTag } from '@/lib/updateCheck';
describe('compareVersions', () => {
it.each([
['0.12.0', '0.12.0', 0],
['0.12.1', '0.12.0', 1],
['0.12.0', '0.12.1', -1],
['1.0.0', '0.99.99', 1],
['0.13.0', '0.9.0', 1],
])('%s vs %s', (a, b, expected) => {
expect(Math.sign(compareVersions(a, b))).toBe(expected);
});
/** 10 must not sort before 9, which is what a string compare would do. */
it('compares numerically, not lexically', () => {
expect(compareVersions('0.10.0', '0.9.0')).toBeGreaterThan(0);
expect(compareVersions('0.2.0', '0.10.0')).toBeLessThan(0);
});
it('tolerates the leading v the releases API uses', () => {
expect(compareVersions('v0.12.0', '0.12.0')).toBe(0);
expect(compareVersions('V0.13.0', '0.12.0')).toBeGreaterThan(0);
});
it('treats missing segments as zero', () => {
expect(compareVersions('1', '1.0.0')).toBe(0);
expect(compareVersions('1.1', '1.0.9')).toBeGreaterThan(0);
});
it('compares a pre-release on its numbers alone', () => {
expect(compareVersions('0.13.0-rc.1', '0.12.0')).toBeGreaterThan(0);
expect(compareVersions('0.12.0-rc.1', '0.12.0')).toBe(0);
});
it('does not crash on rubbish', () => {
expect(() => compareVersions('', 'x.y.z')).not.toThrow();
expect(compareVersions('x.y.z', '')).toBe(0);
});
});
describe('isNewerVersion', () => {
it('is true only for a strictly newer release', () => {
expect(isNewerVersion('0.13.0', '0.12.0')).toBe(true);
expect(isNewerVersion('0.12.0', '0.12.0')).toBe(false);
expect(isNewerVersion('0.11.0', '0.12.0')).toBe(false);
});
/** A downgraded tag must never be advertised as an update. */
it('does not nag when running ahead of the latest release', () => {
expect(isNewerVersion('v0.12.0', '0.13.0-dev')).toBe(false);
});
});
describe('parseLatestTag', () => {
it('reads tag_name', () => {
expect(parseLatestTag({ tag_name: 'v0.13.0' })).toBe('v0.13.0');
});
it('trims surrounding whitespace', () => {
expect(parseLatestTag({ tag_name: ' v0.13.0 ' })).toBe('v0.13.0');
});
it.each([
['null', null],
['a string', 'v0.13.0'],
['an array', []],
['an error object served with 200', { message: 'API rate limit exceeded' }],
['a non-string tag', { tag_name: 42 }],
['a tag that is not a version', { tag_name: 'nightly' }],
// The check only guarded the *start* of the tag, so anything beginning
// with a digit slipped through and was then compared as a version. A
// date-style tag reads as version 2024 and leaves every instance showing
// an update that does not exist, permanently.
['a date-style tag', { tag_name: '2024-01-05' }],
['a tag with a digit and then rubbish', { tag_name: '1abc' }],
])('returns null for %s', (_label, payload) => {
expect(parseLatestTag(payload)).toBeNull();
});
it('still accepts the shapes the project actually tags', () => {
expect(parseLatestTag({ tag_name: 'v0.13.0' })).toBe('v0.13.0');
expect(parseLatestTag({ tag_name: '0.13.0' })).toBe('0.13.0');
expect(parseLatestTag({ tag_name: 'v1.0' })).toBe('v1.0');
expect(parseLatestTag({ tag_name: 'v1.2.3-rc1' })).toBe('v1.2.3-rc1');
// Build metadata is ignorable for ordering, so rejecting it would mean
// never mentioning a release that is genuinely newer.
expect(parseLatestTag({ tag_name: 'v1.2.3+build.7' })).toBe('v1.2.3+build.7');
});
});