// @ts-expect-error -- bun:test is a runtime built-in with no types installed
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { mkdtempSync, writeFileSync, readFileSync, existsSync, utimesSync, statSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import {
isValidSelfhstRef,
selfhstCachePath,
sanitizeSvg,
getSelfhstIcon
} from '../src/lib/server/selfhst-icons';
import { looksLikeImage } from '../src/lib/server/stack-icons';
describe('isValidSelfhstRef', () => {
test('accepts normal references', () => {
for (const r of ['plex', 'jellyfin', 'home-assistant', 'pihole', 'nextcloud', '2fauth']) {
expect(isValidSelfhstRef(r)).toBe(true);
}
});
test('rejects path traversal and junk', () => {
for (const r of ['../etc/passwd', 'a/b', 'foo.svg', 'Foo', 'a_b', 'a b', '', '..', './x', 'x/../y']) {
expect(isValidSelfhstRef(r)).toBe(false);
}
});
test('rejects an over-long ref', () => {
expect(isValidSelfhstRef('a'.repeat(65))).toBe(false);
});
test('selfhstCachePath throws on an invalid ref (never builds a path from junk)', () => {
expect(() => selfhstCachePath('../../etc/passwd')).toThrow();
expect(() => selfhstCachePath('ok-ref')).not.toThrow();
});
test('selfhstCachePath keeps the ref inside the cache dir', () => {
const p = selfhstCachePath('plex');
expect(p.endsWith('/icon-cache/selfhst/plex.svg')).toBe(true);
});
});
describe('sanitizeSvg (SVG-XSS defense)', () => {
test('strips ');
expect(out).not.toContain('