mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 13:52:51 +00:00
Changes: - **Search**: Replaced Inkeep with Fumadocs' native built-in search (createFromSource). Removed @inkeep/cxkit-react and custom search dialogs. - **Hydration Fix**: Removed markdown links from ### headings in egress.mdx (across all versions) to fix the <a inside a> DOM mismatch error. - **Console Fix**: Added the missing key prop to <VersionSelector/> in the layout to resolve the React list warning. --------- Co-authored-by: Florian Forster <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]>
203 lines
7.5 KiB
JavaScript
203 lines
7.5 KiB
JavaScript
import { test, describe, beforeEach, afterEach, mock } from 'node:test';
|
|
import assert from 'node:assert';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { getCurrentRef, resetCache, downloadFileContent, isValidRef, safeLog, unwrapHeadingLinks, parseLsRemoteTags } from './fetch-remote-content.mjs';
|
|
|
|
const TEST_TMP_DIR = path.join(process.cwd(), '.test-tmp');
|
|
const MOCK_REPO_ROOT = path.join(TEST_TMP_DIR, 'mock-repo');
|
|
|
|
describe('fetch-remote-content', () => {
|
|
|
|
// --- Setup & Teardown ---
|
|
const originalEnv = process.env;
|
|
|
|
beforeEach(() => {
|
|
process.env = { ...originalEnv };
|
|
resetCache();
|
|
|
|
// Set up a mock repo structure
|
|
if (fs.existsSync(TEST_TMP_DIR)) {
|
|
fs.rmSync(TEST_TMP_DIR, { recursive: true, force: true });
|
|
}
|
|
fs.mkdirSync(MOCK_REPO_ROOT, { recursive: true });
|
|
|
|
// Create some dummy content
|
|
fs.writeFileSync(path.join(MOCK_REPO_ROOT, 'README.md'), '# Mock Repo');
|
|
fs.mkdirSync(path.join(MOCK_REPO_ROOT, 'secrets'), { recursive: true });
|
|
fs.writeFileSync(path.join(MOCK_REPO_ROOT, 'secrets/passwd'), 'super_secret');
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env = originalEnv;
|
|
mock.restoreAll();
|
|
resetCache();
|
|
if (fs.existsSync(TEST_TMP_DIR)) {
|
|
fs.rmSync(TEST_TMP_DIR, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
// --- Helper Tests ---
|
|
describe('isValidRef', () => {
|
|
test('accepts valid alphanumeric refs', () => {
|
|
assert.strictEqual(isValidRef('main'), true);
|
|
assert.strictEqual(isValidRef('v1.0.0'), true);
|
|
assert.strictEqual(isValidRef('feature/new-docs'), true);
|
|
assert.strictEqual(isValidRef('fix_bug-123'), true);
|
|
assert.strictEqual(isValidRef('dependabot/npm_and_yarn/@types/react'), true);
|
|
assert.strictEqual(isValidRef('build+test'), true);
|
|
});
|
|
|
|
test('rejects malicious refs', () => {
|
|
assert.strictEqual(isValidRef('; rm -rf'), false);
|
|
assert.strictEqual(isValidRef('main && echo pwned'), false);
|
|
assert.strictEqual(isValidRef('../parent'), false); // contains .. which isn't allowed in regex
|
|
});
|
|
});
|
|
|
|
describe('safeLog', () => {
|
|
test('strips newlines', () => {
|
|
assert.strictEqual(safeLog('valid'), 'valid');
|
|
assert.strictEqual(safeLog('malicious\nlog'), 'maliciouslog');
|
|
assert.strictEqual(safeLog('malicious\r\nlog'), 'maliciouslog');
|
|
});
|
|
});
|
|
|
|
// --- getCurrentRef Tests ---
|
|
describe('getCurrentRef', () => {
|
|
test('returns VERCEL_GIT_COMMIT_REF if set and valid', () => {
|
|
process.env.VERCEL_GIT_COMMIT_REF = 'vercel-branch';
|
|
delete process.env.GITHUB_REF_NAME;
|
|
assert.strictEqual(getCurrentRef(), 'vercel-branch');
|
|
});
|
|
|
|
test('ignores invalid VERCEL_GIT_COMMIT_REF', () => {
|
|
// Should fall through to GITHUB or git command
|
|
process.env.VERCEL_GIT_COMMIT_REF = 'invalid;cmd';
|
|
process.env.GITHUB_REF_NAME = 'valid-github';
|
|
assert.strictEqual(getCurrentRef(), 'valid-github');
|
|
});
|
|
|
|
test('returns GITHUB_REF_NAME if set and valid', () => {
|
|
delete process.env.VERCEL_GIT_COMMIT_REF;
|
|
process.env.GITHUB_REF_NAME = 'github-branch';
|
|
assert.strictEqual(getCurrentRef(), 'github-branch');
|
|
});
|
|
});
|
|
|
|
// --- downloadFileContent Tests ---
|
|
describe('downloadFileContent', () => {
|
|
|
|
test('refuses traversal attempts (..)', async () => {
|
|
// We expect this to return null and log a warning
|
|
const content = await downloadFileContent('main', '../secrets/passwd');
|
|
assert.strictEqual(content, null);
|
|
});
|
|
|
|
test('refuses absolute paths', async () => {
|
|
const content = await downloadFileContent('main', '/etc/passwd');
|
|
assert.strictEqual(content, null);
|
|
});
|
|
|
|
test('refuses encoded traversal attempts', async () => {
|
|
// %2e%2e is ..
|
|
const content = await downloadFileContent('main', '%2e%2e/secrets/passwd');
|
|
assert.strictEqual(content, null);
|
|
});
|
|
|
|
test('reads valid local file (README.md)', async () => {
|
|
// Mock getCurrentRef to return 'main' and pass 'main' to simulate local
|
|
process.env.VERCEL_GIT_COMMIT_REF = 'main';
|
|
|
|
const content = await downloadFileContent('main', 'README.md');
|
|
// It should find the real README.md of the project since the script resolves from __dirname
|
|
assert.ok(content);
|
|
assert.ok(content.length > 0);
|
|
});
|
|
|
|
test('refuses invalid ref strings', async () => {
|
|
// Validation check for command injection protection
|
|
const content = await downloadFileContent('invalid; rm -rf', 'README.md');
|
|
assert.strictEqual(content, null);
|
|
});
|
|
});
|
|
|
|
// --- filterVersions Tests ---
|
|
describe('filterVersions', () => {
|
|
test('filters versions older than CUTOFF (4.10.0)', async () => {
|
|
const { filterVersions } = await import('./fetch-remote-content.mjs');
|
|
const tags = [
|
|
{ name: 'v4.9.0' },
|
|
{ name: 'v4.10.0' }, // Equal to cutoff — excluded (gt is strict)
|
|
{ name: 'v4.11.0' },
|
|
{ name: 'invalid' }
|
|
];
|
|
|
|
const result = filterVersions(tags);
|
|
assert.ok(result.includes('v4.11.0'));
|
|
assert.ok(!result.includes('v4.10.0'));
|
|
assert.ok(!result.includes('v4.9.0'));
|
|
});
|
|
|
|
test('excludes prerelease tags', async () => {
|
|
const { filterVersions } = await import('./fetch-remote-content.mjs');
|
|
const tags = [
|
|
{ name: 'v5.0.0' },
|
|
{ name: 'v5.0.0-alpha' },
|
|
{ name: 'v5.0.0-beta.1' },
|
|
{ name: 'v4.11.0-rc.1' },
|
|
{ name: 'v4.11.0' },
|
|
];
|
|
|
|
const result = filterVersions(tags);
|
|
assert.ok(result.includes('v5.0.0'), 'stable release should be included');
|
|
assert.ok(result.includes('v4.11.0'), 'stable release should be included');
|
|
assert.ok(!result.includes('v5.0.0-alpha'), 'alpha prerelease should be excluded');
|
|
assert.ok(!result.includes('v5.0.0-beta.1'), 'beta prerelease should be excluded');
|
|
assert.ok(!result.includes('v4.11.0-rc.1'), 'rc prerelease should be excluded');
|
|
});
|
|
});
|
|
|
|
// --- parseLsRemoteTags Tests ---
|
|
describe('parseLsRemoteTags', () => {
|
|
test('extracts tag names and de-duplicates peeled annotated tags', () => {
|
|
const output = [
|
|
'aaa\trefs/tags/v4.17.3',
|
|
'bbb\trefs/tags/v4.17.3^{}',
|
|
'ccc\trefs/tags/v4.16.0',
|
|
'ddd\trefs/heads/main',
|
|
'',
|
|
].join('\n');
|
|
assert.deepStrictEqual(parseLsRemoteTags(output), [{ name: 'v4.17.3' }, { name: 'v4.16.0' }]);
|
|
});
|
|
});
|
|
|
|
// --- unwrapHeadingLinks Tests ---
|
|
describe('unwrapHeadingLinks', () => {
|
|
test('unwraps markdown links inside headings only', () => {
|
|
const input = [
|
|
'### [Identity Providers & Federation](/guides/integrate/identity-providers/introduction)',
|
|
'#### **1. [List my project roles](/reference/api/auth/AuthService.ListMyProjectPermissions)**',
|
|
'A paragraph with a [link](/kept) is left untouched.',
|
|
].join('\n');
|
|
const expected = [
|
|
'### Identity Providers & Federation',
|
|
'#### **1. List my project roles**',
|
|
'A paragraph with a [link](/kept) is left untouched.',
|
|
].join('\n');
|
|
assert.strictEqual(unwrapHeadingLinks(input), expected);
|
|
});
|
|
|
|
test('leaves images in headings and comments inside code fences alone', () => {
|
|
const input = [
|
|
'##  Title',
|
|
'```bash',
|
|
'# [not a heading](https://example.com)',
|
|
'```',
|
|
].join('\n');
|
|
assert.strictEqual(unwrapHeadingLinks(input), input);
|
|
});
|
|
});
|
|
|
|
});
|