import { describe, it, expect } from 'vitest';
import { normalizeRedirectPath, isExternal, resolveChain, buildEdgeMap, detectChainsAndLoops, wouldCreateLoop, isRedirectStale, } from '../../redirects/graph.js';
describe('normalizeRedirectPath', () => {
    it('ensures a leading slash and strips trailing slash', () => {
        expect(normalizeRedirectPath('about')).toBe('/about');
        expect(normalizeRedirectPath('/about/')).toBe('/about');
        expect(normalizeRedirectPath('/')).toBe('/');
    });
    it('strips query and hash and lowercases', () => {
        expect(normalizeRedirectPath('/About?x=1#h')).toBe('/about');
    });
    it('leaves external URLs intact (lowercased)', () => {
        expect(normalizeRedirectPath('https://Example.com/Path')).toBe('https://example.com/path');
    });
});
describe('isExternal', () => {
    it('detects absolute URLs', () => {
        expect(isExternal('https://x.com')).toBe(true);
        expect(isExternal('/internal')).toBe(false);
    });
});
describe('resolveChain', () => {
    const rules = [
        { id: '1', from: '/a', to: '/b' },
        { id: '2', from: '/b', to: '/c' },
        { id: '3', from: '/c', to: '/d' },
    ];
    it('follows the chain to the terminal', () => {
        const map = buildEdgeMap(rules);
        const res = resolveChain(map, '/a');
        expect(res.loop).toBe(false);
        expect(res.terminal).toBe('/d');
        expect(res.hops).toEqual(['/a', '/b', '/c', '/d']);
    });
    it('detects loops', () => {
        const map = buildEdgeMap([
            { id: '1', from: '/a', to: '/b' },
            { id: '2', from: '/b', to: '/a' },
        ]);
        const res = resolveChain(map, '/a');
        expect(res.loop).toBe(true);
    });
});
describe('detectChainsAndLoops', () => {
    it('detects a multi-hop chain', () => {
        const chains = detectChainsAndLoops([
            { id: '1', from: '/a', to: '/b' },
            { id: '2', from: '/b', to: '/c' },
        ]);
        const chain = chains.find((c) => c.kind === 'chain');
        expect(chain).toBeTruthy();
        expect(chain.suggestedFrom).toBe('/a');
        expect(chain.suggestedTo).toBe('/c');
    });
    it('detects a loop', () => {
        const chains = detectChainsAndLoops([
            { id: '1', from: '/a', to: '/b' },
            { id: '2', from: '/b', to: '/a' },
        ]);
        expect(chains.some((c) => c.kind === 'loop')).toBe(true);
    });
    it('ignores a single-hop redirect', () => {
        const chains = detectChainsAndLoops([{ id: '1', from: '/a', to: '/b' }]);
        expect(chains).toHaveLength(0);
    });
    it('ignores disabled rules', () => {
        const chains = detectChainsAndLoops([
            { id: '1', from: '/a', to: '/b' },
            { id: '2', from: '/b', to: '/c', enabled: false },
        ]);
        expect(chains).toHaveLength(0);
    });
});
describe('wouldCreateLoop', () => {
    const rules = [{ id: '1', from: '/b', to: '/a' }];
    it('flags a direct loop', () => {
        expect(wouldCreateLoop(rules, '/a', '/b')).toBe(true);
    });
    it('flags a self-redirect', () => {
        expect(wouldCreateLoop(rules, '/x', '/x')).toBe(true);
    });
    it('allows a safe redirect', () => {
        expect(wouldCreateLoop(rules, '/a', '/c')).toBe(false);
    });
    it('allows external targets', () => {
        expect(wouldCreateLoop(rules, '/a', 'https://x.com')).toBe(false);
    });
    it('ignores the rule being edited', () => {
        expect(wouldCreateLoop(rules, '/b', '/a', '1')).toBe(false);
    });
});
describe('isRedirectStale', () => {
    it('treats never-hit as stale', () => {
        expect(isRedirectStale(null)).toBe(true);
    });
    it('treats a recent hit as not stale', () => {
        expect(isRedirectStale(new Date())).toBe(false);
    });
    it('treats an old hit as stale', () => {
        const old = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
        expect(isRedirectStale(old)).toBe(true);
    });
});
//# sourceMappingURL=graph.test.js.map