Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/just-cameron/tangled-cli. Automation-first CLI for Tangled.org
Something went wrong. Try again.
9.1 kB · 246 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247import { describe, expect, it } from 'vitest';import { isValidAtUri, isValidHandle, isValidTangledDid, safeValidateDid, safeValidateHandle, safeValidateIdentifier, validateAppPassword, validateDid, validateHandle, validateIdentifier, validateIssueBody, validateIssueTitle,} from '../../src/utils/validation.js';
describe('Handle Validation', () => { describe('validateHandle', () => { it('should accept standard Bluesky handles', () => { expect(validateHandle('user.bsky.social')).toBe('user.bsky.social'); expect(validateHandle('test.bsky.social')).toBe('test.bsky.social'); });
it('should accept custom domain handles', () => { expect(validateHandle('markbennett.ca')).toBe('markbennett.ca'); expect(validateHandle('example.com')).toBe('example.com'); expect(validateHandle('subdomain.example.com')).toBe('subdomain.example.com'); });
it('should reject invalid handles', () => { expect(() => validateHandle('')).toThrow('Handle cannot be empty'); expect(() => validateHandle('invalid')).toThrow('Invalid handle format'); expect(() => validateHandle('invalid..com')).toThrow('Invalid handle format'); expect(() => validateHandle('.example.com')).toThrow('Invalid handle format'); expect(() => validateHandle('example.com.')).toThrow('Invalid handle format'); }); });
describe('safeValidateHandle', () => { it('should return success for valid handles', () => { const result = safeValidateHandle('user.bsky.social'); expect(result.success).toBe(true); if (result.success) { expect(result.data).toBe('user.bsky.social'); } });
it('should return error for invalid handles', () => { const result = safeValidateHandle('invalid'); expect(result.success).toBe(false); if (!result.success) { expect(result.error).toContain('Invalid handle format'); } }); });});
describe('DID Validation', () => { describe('validateDid', () => { it('should accept valid DIDs', () => { expect(validateDid('did:plc:test123')).toBe('did:plc:test123'); expect(validateDid('did:web:example.com')).toBe('did:web:example.com'); expect(validateDid('did:key:z6MkhaXg')).toBe('did:key:z6MkhaXg'); });
it('should reject invalid DIDs', () => { expect(() => validateDid('')).toThrow('DID cannot be empty'); expect(() => validateDid('not-a-did')).toThrow('Invalid DID format'); expect(() => validateDid('did:')).toThrow('Invalid DID format'); expect(() => validateDid('did:plc:')).toThrow('Invalid DID format'); }); });
describe('safeValidateDid', () => { it('should return success for valid DIDs', () => { const result = safeValidateDid('did:plc:test123'); expect(result.success).toBe(true); if (result.success) { expect(result.data).toBe('did:plc:test123'); } });
it('should return error for invalid DIDs', () => { const result = safeValidateDid('not-a-did'); expect(result.success).toBe(false); if (!result.success) { expect(result.error).toContain('Invalid DID format'); } }); });});
describe('Identifier Validation', () => { describe('validateIdentifier', () => { it('should accept valid handles', () => { expect(validateIdentifier('user.bsky.social')).toBe('user.bsky.social'); expect(validateIdentifier('example.com')).toBe('example.com'); });
it('should accept valid DIDs', () => { expect(validateIdentifier('did:plc:test123')).toBe('did:plc:test123'); expect(validateIdentifier('did:web:example.com')).toBe('did:web:example.com'); });
it('should reject invalid identifiers', () => { expect(() => validateIdentifier('')).toThrow(); expect(() => validateIdentifier('invalid')).toThrow(); }); });
describe('safeValidateIdentifier', () => { it('should return success for valid identifiers', () => { expect(safeValidateIdentifier('user.bsky.social').success).toBe(true); expect(safeValidateIdentifier('did:plc:test123').success).toBe(true); });
it('should return error for invalid identifiers', () => { const result = safeValidateIdentifier('invalid'); expect(result.success).toBe(false); if (!result.success) { expect(result.error).toBeTruthy(); } }); });});
describe('App Password Validation', () => { describe('validateAppPassword', () => { it('should accept valid passwords', () => { expect(validateAppPassword('password123')).toBe('password123'); expect(validateAppPassword('xxxx-xxxx-xxxx-xxxx')).toBe('xxxx-xxxx-xxxx-xxxx'); expect(validateAppPassword('a'.repeat(100))).toBe('a'.repeat(100)); });
it('should reject empty passwords', () => { expect(() => validateAppPassword('')).toThrow('Password cannot be empty'); });
it('should reject extremely long passwords', () => { expect(() => validateAppPassword('a'.repeat(1001))).toThrow('Password is too long'); }); });});
describe('Boolean Validation Helpers', () => { describe('isValidHandle', () => { it('should return true for valid handles', () => { expect(isValidHandle('user.bsky.social')).toBe(true); expect(isValidHandle('markbennett.ca')).toBe(true); expect(isValidHandle('sub.domain.example.com')).toBe(true); });
it('should return false for invalid handles', () => { expect(isValidHandle('invalid')).toBe(false); expect(isValidHandle('.starts-with-dot.com')).toBe(false); expect(isValidHandle('ends-with-dot.com.')).toBe(false); expect(isValidHandle('has space.com')).toBe(false); expect(isValidHandle('')).toBe(false); }); });
describe('isValidTangledDid', () => { it('should return true for valid Tangled DIDs (did:plc: format)', () => { expect(isValidTangledDid('did:plc:b2mcbcamkwyznc5fkplwlxbf')).toBe(true); expect(isValidTangledDid('did:plc:abc123xyz')).toBe(true); });
it('should return false for invalid Tangled DIDs', () => { expect(isValidTangledDid('did:plc:')).toBe(false); expect(isValidTangledDid('did:plc:ABC123')).toBe(false); // uppercase not allowed expect(isValidTangledDid('did:web:example.com')).toBe(false); // wrong method expect(isValidTangledDid('not-a-did')).toBe(false); expect(isValidTangledDid('')).toBe(false); }); });});
describe('Issue Validation', () => { describe('validateIssueTitle', () => { it('should accept valid issue titles', () => { expect(validateIssueTitle('Bug: Fix login error')).toBe('Bug: Fix login error'); expect(validateIssueTitle('Feature: Add dark mode')).toBe('Feature: Add dark mode'); expect(validateIssueTitle('A')).toBe('A'); // minimum length });
it('should accept titles up to 256 characters', () => { const longTitle = 'A'.repeat(256); expect(validateIssueTitle(longTitle)).toBe(longTitle); });
it('should reject empty titles', () => { expect(() => validateIssueTitle('')).toThrow('Issue title cannot be empty'); });
it('should reject titles over 256 characters', () => { const tooLong = 'A'.repeat(257); expect(() => validateIssueTitle(tooLong)).toThrow( 'Issue title must be 256 characters or less' ); }); });
describe('validateIssueBody', () => { it('should accept valid issue bodies', () => { expect(validateIssueBody('This is a description')).toBe('This is a description'); expect(validateIssueBody('Multi\nline\ndescription')).toBe('Multi\nline\ndescription'); });
it('should accept bodies up to 50,000 characters', () => { const longBody = 'A'.repeat(50000); expect(validateIssueBody(longBody)).toBe(longBody); });
it('should accept empty string', () => { expect(validateIssueBody('')).toBe(''); });
it('should reject bodies over 50,000 characters', () => { const tooLong = 'A'.repeat(50001); expect(() => validateIssueBody(tooLong)).toThrow( 'Issue body must be 50,000 characters or less' ); }); });});
describe('AT-URI Validation', () => { describe('isValidAtUri', () => { it('should return true for valid AT-URIs', () => { expect(isValidAtUri('at://did:plc:abc123/sh.tangled.repo/my-repo')).toBe(true); expect(isValidAtUri('at://did:plc:abc123/sh.tangled.repo.issue/xyz789')).toBe(true); expect(isValidAtUri('at://did:web:example.com/collection')).toBe(true); });
it('should return true for AT-URIs without rkey', () => { expect(isValidAtUri('at://did:plc:abc123/collection')).toBe(true); });
it('should return false for invalid AT-URIs', () => { expect(isValidAtUri('http://example.com')).toBe(false); expect(isValidAtUri('at://not-a-did/collection')).toBe(false); expect(isValidAtUri('at://did:plc:abc/invalid collection')).toBe(false); expect(isValidAtUri('')).toBe(false); }); });});