~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
90 lines · 2.8 KB Raw History
import { describe, expect, test, vi } from 'vitest';
import { lookupTxt, parseDidFromTxt } from '../src/dns.js';

describe('dns', () => {
  describe('parseDidFromTxt', () => {
    test('extracts DID from TXT record value', () => {
      expect(parseDidFromTxt('did=did:plc:xyz123')).toBe('did:plc:xyz123');
    });

    test('handles quoted TXT record', () => {
      expect(parseDidFromTxt('"did=did:plc:xyz123"')).toBe('did:plc:xyz123');
    });

    test('returns null for invalid format', () => {
      expect(parseDidFromTxt('invalid')).toBeNull();
    });

    test('returns null for empty string', () => {
      expect(parseDidFromTxt('')).toBeNull();
    });

    test('returns null for invalid DID format', () => {
      expect(parseDidFromTxt('did=not-a-valid-did')).toBeNull();
      expect(parseDidFromTxt('did=did:')).toBeNull(); // Missing method and specific-id
      expect(parseDidFromTxt('did=did:plc:')).toBeNull(); // Missing specific-id
    });

    test('accepts valid did:web format', () => {
      expect(parseDidFromTxt('did=did:web:example.com')).toBe(
        'did:web:example.com',
      );
    });
  });

  describe('lookupTxt', () => {
    test('fetches TXT records via DoH', async () => {
      const mockFetch = vi.fn().mockResolvedValue({
        ok: true,
        json: () =>
          Promise.resolve({
            Answer: [
              { type: 16, data: '"did=did:plc:z72i7hdynmk6r22z27h6tvur"' },
            ],
          }),
      });

      const records = await lookupTxt('_atproto.bsky.app', {
        fetch: mockFetch,
      });

      expect(mockFetch).toHaveBeenCalledWith(
        'https://cloudflare-dns.com/dns-query?name=_atproto.bsky.app&type=TXT',
        expect.objectContaining({
          headers: { Accept: 'application/dns-json' },
        }),
      );
      expect(records).toEqual(['did=did:plc:z72i7hdynmk6r22z27h6tvur']);
    });

    test('returns empty array on network error', async () => {
      const mockFetch = vi.fn().mockRejectedValue(new Error('Network error'));
      const records = await lookupTxt('_atproto.example.com', {
        fetch: mockFetch,
      });
      expect(records).toEqual([]);
    });

    test('returns empty array when no Answer', async () => {
      const mockFetch = vi.fn().mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({}),
      });
      const records = await lookupTxt('_atproto.example.com', {
        fetch: mockFetch,
      });
      expect(records).toEqual([]);
    });

    test('returns empty array on HTTP error response', async () => {
      const mockFetch = vi.fn().mockResolvedValue({
        ok: false,
        status: 500,
      });
      const records = await lookupTxt('_atproto.example.com', {
        fetch: mockFetch,
      });
      expect(records).toEqual([]);
    });
  });
});