WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage from it! :)
at main 117 lines 3.2 kB view raw
1import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; 2import { getBacklinks } from '../../src/lib/constellation.js'; 3 4const mockFetch = vi.fn(); 5 6beforeEach(() => { 7 mockFetch.mockClear(); 8 vi.stubGlobal('fetch', mockFetch); 9}); 10 11afterEach(() => { 12 vi.unstubAllGlobals(); 13}); 14 15describe('getBacklinks', () => { 16 it('should return records from constellation', async () => { 17 mockFetch.mockResolvedValue({ 18 ok: true, 19 json: async () => ({ 20 total: 2, 21 linking_records: [ 22 { did: 'did:plc:abc', collection: 'sh.tangled.repo.issue', rkey: 'rkey1' }, 23 { did: 'did:plc:def', collection: 'sh.tangled.repo.issue', rkey: 'rkey2' }, 24 ], 25 cursor: null, 26 }), 27 }); 28 29 const result = await getBacklinks( 30 'at://did:plc:owner/sh.tangled.repo/my-repo', 31 'sh.tangled.repo.issue', 32 '.repo' 33 ); 34 35 expect(result.total).toBe(2); 36 expect(result.records).toHaveLength(2); 37 expect(result.records[0]).toEqual({ 38 did: 'did:plc:abc', 39 collection: 'sh.tangled.repo.issue', 40 rkey: 'rkey1', 41 }); 42 expect(result.cursor).toBeNull(); 43 44 expect(mockFetch).toHaveBeenCalledWith( 45 'https://constellation.microcosm.blue/links?target=at%3A%2F%2Fdid%3Aplc%3Aowner%2Fsh.tangled.repo%2Fmy-repo&collection=sh.tangled.repo.issue&path=.repo&limit=100' 46 ); 47 }); 48 49 it('should pass cursor and limit params', async () => { 50 mockFetch.mockResolvedValue({ 51 ok: true, 52 json: async () => ({ total: 0, linking_records: [], cursor: null }), 53 }); 54 55 await getBacklinks( 56 'at://did:plc:owner/sh.tangled.repo/my-repo', 57 'sh.tangled.repo.issue', 58 '.repo', 59 50, 60 'abc123' 61 ); 62 63 const calledUrl = mockFetch.mock.calls[0][0] as string; 64 expect(calledUrl).toContain('limit=50'); 65 expect(calledUrl).toContain('cursor=abc123'); 66 }); 67 68 it('should return cursor for pagination', async () => { 69 mockFetch.mockResolvedValue({ 70 ok: true, 71 json: async () => ({ 72 total: 200, 73 linking_records: [ 74 { did: 'did:plc:abc', collection: 'sh.tangled.repo.issue', rkey: 'rkey1' }, 75 ], 76 cursor: 'nextpage', 77 }), 78 }); 79 80 const result = await getBacklinks( 81 'at://did:plc:owner/sh.tangled.repo/my-repo', 82 'sh.tangled.repo.issue', 83 '.repo', 84 1 85 ); 86 87 expect(result.cursor).toBe('nextpage'); 88 }); 89 90 it('should throw on non-OK response', async () => { 91 mockFetch.mockResolvedValue({ 92 ok: false, 93 status: 503, 94 statusText: 'Service Unavailable', 95 }); 96 97 await expect( 98 getBacklinks('at://did:plc:owner/sh.tangled.repo/my-repo', 'sh.tangled.repo.issue', '.repo') 99 ).rejects.toThrow('Constellation API error: 503 Service Unavailable'); 100 }); 101 102 it('should return empty records when none found', async () => { 103 mockFetch.mockResolvedValue({ 104 ok: true, 105 json: async () => ({ total: 0, linking_records: [], cursor: null }), 106 }); 107 108 const result = await getBacklinks( 109 'at://did:plc:owner/sh.tangled.repo/my-repo', 110 'sh.tangled.repo.issue', 111 '.repo' 112 ); 113 114 expect(result.total).toBe(0); 115 expect(result.records).toEqual([]); 116 }); 117});