# test.deploy/core/steps/release.step.test.ts

> 59 lines of code and 0 definitions.

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

Listed in [test.deploy/core/steps](https://banes-lab.com/api/source/testing/test.deploy/core/steps.md), after [test.deploy/core/steps/recovery.step.test.ts](https://banes-lab.com/source/testing/test.deploy/core/steps/recovery.step.test.ts.md) and before [test.deploy/core/steps/remote.step.test.ts](https://banes-lab.com/source/testing/test.deploy/core/steps/remote.step.test.ts.md).

## Contained in

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

## Source

```typescript
import type { CommandResult, UploadStats } from "@banes-lab/deploy/types/deployment.types.ts";
import { REMOTE_SITE, SITE_ARCHIVE } from "@banes-lab/deploy/configuration/constants/deployment.constants.ts";
import { UPLOAD_INCOMPLETE, unpackFailed } from "@banes-lab/deploy/configuration/strings/deployment.strings.ts";
import { describe, expect, it, vi } from "vitest";
import { fakeJournal, fakeShell, ok } from "./shell.fixture.ts";
import { uploadSite } from "@banes-lab/deploy/core/steps/release.step.ts";

interface Entry {
    readonly isDirectory: () => boolean;
    readonly name: string;
}

const fileSystem = vi.hoisted(() => ({
    readdirSync: vi.fn<(path: string) => Entry[]>(),
    statSync: vi.fn<(path: string) => { size: number }>(),
}));

const packed = vi.hoisted(() => ({ calls: [] as (readonly [string, string])[] }));

vi.mock("node:fs", async (importOriginal) => ({ ...(await importOriginal<Record<string, unknown>>()), ...fileSystem }));

vi.mock("@banes-lab/deploy/core/adapters/release.adapter.ts", () => ({
    packFolder: async (source: string, archive: string) => {
        packed.calls.push([source, archive]);
        await Promise.resolve();
    },
}));

const entry = function entry(name: string, directory: boolean): Entry {
    return { isDirectory: () => directory, name };
};

const tree = function tree(): void {
    fileSystem.readdirSync.mockImplementation((path) =>
        path.endsWith(".hidden") ? [entry("b.js", false)] : [entry("a.html", false), entry(".hidden", true)],
    );
    fileSystem.statSync.mockReturnValue({ size: 5 });
};

const answering = function answering(count: string, unpack: CommandResult = ok()): (command: string) => CommandResult {
    return (command) => {
        if (command.includes("wc -l")) {
            return ok(count);
        }
        return command.includes("tar -xzf") ? unpack : ok();
    };
};

describe("uploadSite", () => {
    it("packs the tree, uploads one archive, replaces the remote site with its contents, and counts the files", async () => {
        tree();
        const shell = fakeShell(answering("2\n"));
        const stats: UploadStats = await uploadSite(shell, fakeJournal());
        expect(packed.calls.at(-1)?.[1].endsWith(SITE_ARCHIVE)).toBe(true);
        expect(shell.uploads).toHaveLength(1);
        expect(shell.commands.some((command) => command.includes("tar -xzf") && command.includes(REMOTE_SITE))).toBe(
            true,
        );
        expect(stats).toStrictEqual({ failedCount: 0, totalBytes: 10, totalFileCount: 2, uploadedCount: 2 });
    });

    it("fails when the archive does not unpack, or the droplet holds fewer files than the build", async () => {
        tree();
        const broken = fakeShell(answering("2\n", { code: 2, stderr: "gzip: unexpected end", stdout: "" }));
        await expect(uploadSite(broken, fakeJournal())).rejects.toThrow(unpackFailed("gzip: unexpected end"));
        const short = fakeShell(answering("1\n"));
        await expect(uploadSite(short, fakeJournal())).rejects.toThrow(UPLOAD_INCOMPLETE);
    });
});
```
