# test.deploy/core/adapters/site.adapter.test.ts

> 95 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-deploy-core-adapters-site-adapter-test-ts
Source text: https://banes-lab.com/source/testing/test.deploy/core/adapters/site.adapter.test.ts.txt

Listed in [test.deploy/core/adapters](https://banes-lab.com/api/source/testing/test.deploy/core/adapters.md), after [test.deploy/core/adapters/shell.adapter.test.ts](https://banes-lab.com/source/testing/test.deploy/core/adapters/shell.adapter.test.ts.md) and before [test.deploy/core/adapters/site.fixture.ts](https://banes-lab.com/source/testing/test.deploy/core/adapters/site.fixture.ts.md).

## Contained in

- [test.deploy/core/adapters](https://banes-lab.com/anatomy/testing/folder-testing-test-deploy-core-adapters.md)

## Source

```typescript
import { SERVED_CERTIFICATE, SERVED_KEY_DIGEST } from "./site.fixture.ts";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { fetchProbe, readDane } from "@banes-lab/deploy/core/adapters/site.adapter.ts";
import { tlsMissing, tlsTimeout } from "@banes-lab/deploy/configuration/strings/deployment.strings.ts";
import { Buffer } from "node:buffer";
import { TLSSocket } from "node:tls";
import { X509Certificate } from "node:crypto";

const SITE = "https://x.test";
const PORT = 443;

type Handler = (error?: Error) => void;

interface Exchange {
    readonly queried: string[];
    plaintext: boolean;
    stalls: boolean;
}

const exchange: Exchange = { plaintext: false, queried: [], stalls: false };

const tlsSocket = function tlsSocket(): TLSSocket {
    const socket: TLSSocket = Object.create(TLSSocket.prototype);
    Object.defineProperty(socket, "remotePort", { value: PORT });
    Object.defineProperty(socket, "getPeerCertificate", {
        value: () => ({ raw: new X509Certificate(SERVED_CERTIFICATE).raw }),
    });
    return socket;
};

vi.mock("node:dns/promises", () => ({
    resolveTlsa: async (name: string) => {
        exchange.queried.push(name);
        return Promise.resolve([
            { certUsage: 3, data: Uint8Array.from(Buffer.from(SERVED_KEY_DIGEST, "hex")).buffer, match: 1, selector: 1 },
            { certUsage: 2, data: Uint8Array.of(1, 2).buffer, match: 1, selector: 1 },
        ]);
    },
}));

vi.mock("node:https", () => ({
    request: (_site: string, _options: unknown, answer: (response: unknown) => void) => {
        const handlers = new Map<string, Handler>();
        return {
            destroy: (error: Error) => handlers.get("error")?.(error),
            end: () => {
                if (exchange.stalls) {
                    handlers.get("timeout")?.();
                    return;
                }
                const socket = exchange.plaintext ? {} : tlsSocket();
                queueMicrotask(() => {
                    answer({ resume: () => undefined, socket });
                });
            },
            on: (event: string, handler: Handler) => {
                handlers.set(event, handler);
            },
        };
    },
}));

beforeEach(() => {
    exchange.queried.length = 0;
    exchange.plaintext = false;
    exchange.stalls = false;
});

afterEach(() => {
    vi.unstubAllGlobals();
});

describe("readDane", () => {
    it("digests the served key and keeps only the published 3 1 1 records at the served port", async () => {
        expect(await readDane(SITE)).toStrictEqual({
            name: "_443._tcp.x.test",
            published: [SERVED_KEY_DIGEST],
            served: SERVED_KEY_DIGEST,
        });
        expect(exchange.queried).toStrictEqual(["_443._tcp.x.test"]);
    });

    it("refuses an answer without TLS and a handshake that times out", async () => {
        exchange.plaintext = true;
        await expect(readDane(SITE)).rejects.toThrow(tlsMissing(SITE));
        exchange.plaintext = false;
        exchange.stalls = true;
        await expect(readDane(SITE)).rejects.toThrow(tlsTimeout(SITE));
    });
});

describe("fetchProbe", () => {
    it("reads the status and type without following a redirect, and the body only for a GET", async () => {
        const fetch = vi.fn(async () => {
            await Promise.resolve();
            return new Response("body", { headers: { "content-type": "text/markdown" }, status: 200 });
        });
        vi.stubGlobal("fetch", fetch);
        expect(await fetchProbe("https://x.test/a.md", "GET")).toStrictEqual({
            body: "body",
            status: 200,
            type: "text/markdown",
        });
        expect(await fetchProbe("https://x.test/a.md", "HEAD")).toMatchObject({ body: "", status: 200 });
        expect(fetch).toHaveBeenCalledWith("https://x.test/a.md", expect.objectContaining({ redirect: "manual" }));
    });
});
```
