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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
106 lines · 3.5 KB Raw History
// The shared request/response helpers: the single-range parser across its
// forms, and the CORS wrapper's pass-through for upgrade responses.
import { describe, expect, it } from 'vitest';
import {
  addCorsHeaders,
  corsHeaders,
  parseByteRange,
  preflightHeaders,
} from '../src/http.js';

describe('parseByteRange', () => {
  it('is null without a header', () => {
    expect(parseByteRange(null, 100)).toBe(null);
  });

  it('is null for a unit it does not speak', () => {
    expect(parseByteRange('rows=1-2', 100)).toBe(null);
  });

  it('is null for a range with neither bound', () => {
    expect(parseByteRange('bytes=-', 100)).toBe(null);
  });

  it('is unsatisfiable against an empty file', () => {
    expect(parseByteRange('bytes=0-1', 0)).toBe('unsatisfiable');
  });

  it('reads a bounded range and clamps the end', () => {
    expect(parseByteRange('bytes=2-5', 100)).toEqual({ start: 2, end: 5 });
    expect(parseByteRange('bytes=90-200', 100)).toEqual({
      start: 90,
      end: 99,
    });
  });

  it('reads an open-ended range', () => {
    expect(parseByteRange('bytes=95-', 100)).toEqual({ start: 95, end: 99 });
  });

  it('reads a suffix range and clamps it to the start', () => {
    expect(parseByteRange('bytes=-5', 100)).toEqual({ start: 95, end: 99 });
    expect(parseByteRange('bytes=-500', 100)).toEqual({ start: 0, end: 99 });
  });

  it('a zero-length suffix is unsatisfiable', () => {
    expect(parseByteRange('bytes=-0', 100)).toBe('unsatisfiable');
  });

  it('a start past the end is unsatisfiable', () => {
    expect(parseByteRange('bytes=100-', 100)).toBe('unsatisfiable');
  });

  it('a backwards range is unsatisfiable', () => {
    expect(parseByteRange('bytes=5-2', 100)).toBe('unsatisfiable');
  });
});

describe('addCorsHeaders', () => {
  it('adds the headers to a plain response', () => {
    const wrapped = addCorsHeaders(new Response('ok'));
    expect(wrapped.headers.get('Access-Control-Allow-Origin')).toBe('*');
  });

  it('passes an upgrade response through untouched', () => {
    const upgrade = Object.assign(new Response('ok'), { webSocket: {} });
    expect(addCorsHeaders(upgrade)).toBe(upgrade);
  });
});

describe('preflightHeaders', () => {
  const preflight = (/** @type {Record<string, string>} */ headers) =>
    preflightHeaders(
      new Request('https://pds.example.com/xrpc/x', { headers }),
    );

  it('allows exactly the headers the browser asks for', () => {
    const allowed = preflight({
      'Access-Control-Request-Headers': 'authorization,x-bsky-is-beta-user',
    })['Access-Control-Allow-Headers'];
    expect(allowed).toBe('authorization,x-bsky-is-beta-user');
  });

  it('allows a header this server has never heard of', () => {
    const allowed = preflight({
      'Access-Control-Request-Headers': 'x-bsky-invented-tomorrow',
    })['Access-Control-Allow-Headers'];
    expect(allowed).toContain('x-bsky-invented-tomorrow');
  });

  it('falls back to the static list when the browser names none', () => {
    expect(preflight({})).toEqual(corsHeaders);
  });

  it('keeps the other CORS headers', () => {
    const headers = preflight({
      'Access-Control-Request-Headers': 'authorization',
    });
    expect(headers['Access-Control-Allow-Origin']).toBe('*');
    expect(headers['Access-Control-Expose-Headers']).toContain('DPoP-Nonce');
  });

  it('leaves the exported constant alone', () => {
    preflight({ 'Access-Control-Request-Headers': 'x-one' });
    expect(corsHeaders['Access-Control-Allow-Headers']).toContain('Range');
  });
});