1import { describe, it, expect } from 'vitest';
2import { parseRecordURI, hashContent, getExplorerURL } from '../../src/lib/utils';
3
4describe('parseRecordURI', () => {
5 it('should parse valid AT Protocol URI', () => {
6 const uri = 'at://did:plc:abc123/app.bsky.feed.post/xyz789';
7 const result = parseRecordURI(uri);
8
9 expect(result.did).toBe('did:plc:abc123');
10 expect(result.collection).toBe('app.bsky.feed.post');
11 expect(result.rkey).toBe('xyz789');
12 });
13
14 it('should handle complex DIDs', () => {
15 const uri = 'at://did:plc:z72i7hdynmk6r22z27h6tvur/com.whtwnd.blog.entry/abc123';
16 const result = parseRecordURI(uri);
17
18 expect(result.did).toBe('did:plc:z72i7hdynmk6r22z27h6tvur');
19 expect(result.collection).toBe('com.whtwnd.blog.entry');
20 expect(result.rkey).toBe('abc123');
21 });
22
23 it('should throw on invalid URI format', () => {
24 expect(() => parseRecordURI('invalid-uri')).toThrow('Invalid AT Protocol URI format');
25 expect(() => parseRecordURI('https://example.com')).toThrow('Invalid AT Protocol URI format');
26 expect(() => parseRecordURI('at://invalid')).toThrow('Invalid AT Protocol URI format');
27 });
28});
29
30describe('hashContent', () => {
31 it('should generate consistent SHA-256 hash', () => {
32 const content = { text: 'Hello World', createdAt: '2024-01-01' };
33 const hash1 = hashContent(content);
34 const hash2 = hashContent(content);
35
36 expect(hash1).toBe(hash2);
37 expect(hash1).toMatch(/^0x[a-f0-9]{64}$/);
38 });
39
40 it('should generate different hashes for different content', () => {
41 const content1 = { text: 'Hello' };
42 const content2 = { text: 'World' };
43
44 expect(hashContent(content1)).not.toBe(hashContent(content2));
45 });
46
47 it('should be order-sensitive', () => {
48 const content1 = { a: 1, b: 2 };
49 const content2 = { b: 2, a: 1 };
50
51 // JSON.stringify is order-sensitive
52 const hash1 = hashContent(content1);
53 const hash2 = hashContent(content2);
54
55 expect(hash1).toBeTruthy();
56 expect(hash2).toBeTruthy();
57 });
58});
59
60describe('getExplorerURL', () => {
61 it('should generate correct explorer URL for sepolia', () => {
62 const uid = '0xabc123';
63 const url = getExplorerURL(uid, 'sepolia');
64
65 expect(url).toBe('https://sepolia.easscan.org/attestation/view/0xabc123');
66 });
67
68 it('should generate correct explorer URL for sepolia', () => {
69 const uid = '0xabc123';
70 const url = getExplorerURL(uid, 'sepolia');
71
72 expect(url).toBe('https://sepolia.easscan.org/attestation/view/0xabc123');
73 });
74
75 it('should default to sepolia for unknown network', () => {
76 const uid = '0xabc123';
77 const url = getExplorerURL(uid, 'unknown-network');
78
79 expect(url).toBe('https://sepolia.easscan.org/attestation/view/0xabc123');
80 });
81});
82
83describe('hashContent', () => {
84 it('should generate consistent SHA-256 hash', () => {
85 const content = { text: 'Hello World', createdAt: '2024-01-01' };
86 const hash1 = hashContent(content);
87 const hash2 = hashContent(content);
88
89 expect(hash1).toBe(hash2);
90 expect(hash1).toMatch(/^0x[a-f0-9]{64}$/);
91 });
92
93 it('should be order-independent', () => {
94 const content1 = { a: 1, b: 2, c: 3 };
95 const content2 = { c: 3, a: 1, b: 2 };
96 const content3 = { b: 2, c: 3, a: 1 };
97
98 const hash1 = hashContent(content1);
99 const hash2 = hashContent(content2);
100 const hash3 = hashContent(content3);
101
102 expect(hash1).toBe(hash2);
103 expect(hash2).toBe(hash3);
104 });
105
106 it('should handle nested objects', () => {
107 const content1 = { outer: { b: 2, a: 1 }, x: 5 };
108 const content2 = { x: 5, outer: { a: 1, b: 2 } };
109
110 expect(hashContent(content1)).toBe(hashContent(content2));
111 });
112});