Files

114 lines
4.4 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { resolveChangelogUrl, interpolateChangelogUrl } from '../src/lib/utils/changelog-url';
describe('resolveChangelogUrl', () => {
test('dockhand.changelog.url override wins over everything', () => {
expect(
resolveChangelogUrl('ghcr.io/owner/repo:latest', {
'dockhand.changelog.url': 'https://example.com/notes',
'org.opencontainers.image.source': 'https://github.com/somebody/else'
})
).toBe('https://example.com/notes');
});
test('OCI source label → /releases', () => {
expect(
resolveChangelogUrl('docker.io/foo/bar:1.0', {
'org.opencontainers.image.source': 'https://github.com/foo/bar'
})
).toBe('https://github.com/foo/bar/releases');
});
test('OCI source label with trailing slash is normalized', () => {
expect(
resolveChangelogUrl('foo:1.0', {
'org.opencontainers.image.source': 'https://github.com/foo/bar/'
})
).toBe('https://github.com/foo/bar/releases');
});
test('OCI source label that does not point at GitHub is ignored', () => {
expect(
resolveChangelogUrl('gitlab.com/foo/bar', {
'org.opencontainers.image.source': 'https://gitlab.com/foo/bar'
})
).toBeNull();
});
test('GHCR heuristic with tag', () => {
expect(resolveChangelogUrl('ghcr.io/linuxserver/sabnzbd:latest', null)).toBe(
'https://github.com/linuxserver/sabnzbd/releases'
);
});
test('GHCR heuristic with digest', () => {
expect(
resolveChangelogUrl('ghcr.io/linuxserver/sabnzbd@sha256:abc123', null)
).toBe('https://github.com/linuxserver/sabnzbd/releases');
});
test('GHCR heuristic with nested path', () => {
expect(resolveChangelogUrl('ghcr.io/org/team/svc:1.2.3', null)).toBe(
'https://github.com/org/team/svc/releases'
);
});
test('malformed GHCR (single segment) returns null', () => {
expect(resolveChangelogUrl('ghcr.io/alone:latest', null)).toBeNull();
});
test('Docker Hub image with no labels returns null', () => {
expect(resolveChangelogUrl('nginx:latest', {})).toBeNull();
});
test('null inputs are handled', () => {
expect(resolveChangelogUrl(null)).toBeNull();
expect(resolveChangelogUrl(undefined)).toBeNull();
expect(resolveChangelogUrl('', {})).toBeNull();
});
test('empty override label is ignored, falls through to next tier', () => {
expect(
resolveChangelogUrl('ghcr.io/owner/repo', {
'dockhand.changelog.url': ' '
})
).toBe('https://github.com/owner/repo/releases');
});
// Backward compat: a plain (no-placeholder) override is returned verbatim,
// whether or not a version is passed - unchanged from before templating.
test('plain override URL is unchanged with or without a version', () => {
const labels = { 'dockhand.changelog.url': 'https://nginx.org/en/CHANGES' };
expect(resolveChangelogUrl('nginx:1.27', labels)).toBe('https://nginx.org/en/CHANGES');
expect(resolveChangelogUrl('nginx:1.27', labels, '1.27')).toBe('https://nginx.org/en/CHANGES');
});
test('{{version}} / {{tag}} override is interpolated when a version is given', () => {
const labels = { 'dockhand.changelog.url': 'https://github.com/maximhq/bifrost/releases/tag/transports/{{version}}' };
expect(resolveChangelogUrl('maximhq/bifrost:1.6.11', labels, '1.6.11')).toBe(
'https://github.com/maximhq/bifrost/releases/tag/transports/1.6.11'
);
const tagLabel = { 'dockhand.changelog.url': 'https://ex.com/r/{{ tag }}/notes' };
expect(resolveChangelogUrl('x:2.0', tagLabel, '2.0')).toBe('https://ex.com/r/2.0/notes');
});
test('templated override with NO version passed is left as-is (generic list link)', () => {
const labels = { 'dockhand.changelog.url': 'https://ex.com/tag/{{version}}' };
// The container/stack list calls resolveChangelogUrl without a target version.
expect(resolveChangelogUrl('x:1.0', labels)).toBe('https://ex.com/tag/{{version}}');
});
});
describe('interpolateChangelogUrl', () => {
test('no placeholder -> unchanged', () => {
expect(interpolateChangelogUrl('https://a/b', '1.2.3')).toBe('https://a/b');
});
test('no version -> unchanged (even with a placeholder)', () => {
expect(interpolateChangelogUrl('https://a/{{version}}', null)).toBe('https://a/{{version}}');
expect(interpolateChangelogUrl('https://a/{{version}}')).toBe('https://a/{{version}}');
});
test('replaces every {{version}} and {{tag}}, case/space tolerant', () => {
expect(interpolateChangelogUrl('https://a/{{version}}/{{ TAG }}/{{version}}', 'v9')).toBe('https://a/v9/v9/v9');
});
});