Skip to content

Commit

Permalink
wip: saving progress
Browse files Browse the repository at this point in the history
  • Loading branch information
btlghrants committed Jan 10, 2025
1 parent d775061 commit 7a9577d
Show file tree
Hide file tree
Showing 12 changed files with 602 additions and 9 deletions.
1 change: 1 addition & 0 deletions integration/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
workroot/
8 changes: 0 additions & 8 deletions integration/cli/build.test.ts

This file was deleted.

31 changes: 31 additions & 0 deletions integration/cli/init.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { beforeAll, describe, expect, it } from "@jest/globals";
import * as path from "node:path";
import { Workdir } from "../helpers/workdir";
import * as time from "../helpers/time";
import * as pepr from "../helpers/pepr";

const FILE = path.basename(__filename);
const HERE = __dirname;

describe("init", () => {
const workdir = new Workdir(`${FILE}`, `${HERE}/../workroot/cli`);

beforeAll(async () => {
await workdir.recreate();
await pepr.prepWorkdir(workdir.path());
});

it(
"init --help",
async () => {
const res = await pepr.cli(workdir.path(), { cmd: "pepr init --help" });
expect(res.exitcode).toBe(0);
expect(res.stderr.join("").trim()).toBe("");
expect(res.stdout.at(0)).toMatch("Usage: pepr init");
},
time.toMs("2m"),
);
});
56 changes: 56 additions & 0 deletions integration/helpers/cmd.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { describe, expect, it } from "@jest/globals";
import { Cmd } from "./cmd";

describe("runRaw()", () => {
it("returns stdout", async () => {
const expected = "pong";
const { stdout } = await new Cmd({ cmd: `echo "${expected}"` }).runRaw();
expect(stdout.join("")).toBe(expected);
});

it("returns exit code", async () => {
const expected = 83;
const { exitcode } = await new Cmd({ cmd: `exit ${expected}` }).runRaw();
expect(exitcode).toBe(expected);
});

it("returns stderr", async () => {
const expected = "oof";
const { stderr } = await new Cmd({ cmd: `>&2 echo "${expected}" ` }).runRaw();
expect(stderr.join("")).toBe(expected);
});

it("caches last result", async () => {
const cmd = new Cmd({ cmd: `echo "whatever"` });
const result = await cmd.runRaw();
expect(result).toBe(cmd.result);
});

it("accepts working directory", async () => {
const expected = "/tmp";
const { stdout } = await new Cmd({ cwd: expected, cmd: `pwd` }).runRaw();
expect(stdout.join("")).toBe(expected);
});

it("accepts env var overrides", async () => {
const [key, val] = ["TESTVAR", "testcontent"];
const { stdout } = await new Cmd({ env: { [key]: val }, cmd: `echo $${key}` }).runRaw();
expect(stdout.join("")).toBe(val);
});
});

describe("run()", () => {
it("on success, returns result", async () => {
const expected = "pong";
const result = await new Cmd({ cmd: `echo "${expected}"` }).run();
expect(result.stdout.join("")).toBe(expected);
expect(result.stderr.join("")).toBe("");
expect(result.exitcode).toBe(0);
});

it("on failure, throws result", async () => {
const expected = { exitcode: 1, stderr: [], stdout: [] };
const promise = new Cmd({ cmd: `exit ${expected.exitcode}` }).run();
expect(promise).rejects.toEqual(expected);
});
});
74 changes: 74 additions & 0 deletions integration/helpers/cmd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { spawn } from "child_process";

export interface Spec {
cmd: string;
stdin?: string[];
cwd?: string;
env?: object; // object containing key-value pairs
}

export interface Result {
stdout: string[];
stderr: string[];
exitcode: number;
}

export class Cmd {
result?: Result;
cmd: string;
stdin: string[];
cwd: string;
env: object;

constructor(spec: Spec) {
this.cmd = spec.cmd;
this.stdin = spec.stdin || [];
this.cwd = spec.cwd || process.cwd();
this.env = spec.env ? { ...process.env, ...spec.env } : process.env;
}

runRaw(): Promise<Result> {
return new Promise((resolve, reject) => {
const proc = spawn(this.cmd, [], {
shell: true,
cwd: this.cwd,
env: this.env as NodeJS.ProcessEnv,
});

this.stdin.forEach(line => proc.stdin.write(`${line}\n`));
proc.stdin.end();

let bufout: Buffer = Buffer.from("");
proc.stdout.on("data", buf => {
bufout = Buffer.concat([bufout, buf]);
});

let buferr: Buffer = Buffer.from("");
proc.stderr.on("data", buf => {
buferr = Buffer.concat([buferr, buf]);
});

proc.on("close", exitcode => {
const stdout = bufout.toString("utf8") === "" ? [] : bufout.toString("utf8").split(/[\r\n]+/);

const stderr = buferr.toString("utf8") === "" ? [] : buferr.toString("utf8").split(/[\r\n]+/);

this.result = { stdout, stderr, exitcode: exitcode || 0 };
resolve(this.result);
});

proc.on("error", err => {
reject(err);
});
});
}

run(): Promise<Result> {
return this.runRaw().then(result => {
if (result.exitcode > 0) {
throw result;
}
return result;
});
}
}
56 changes: 56 additions & 0 deletions integration/helpers/pepr.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { beforeAll, describe, expect, it } from "@jest/globals";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { Workdir } from "./workdir";
import * as time from "./time";
import * as sut from "./pepr";

const FILE = path.basename(__filename);
const HERE = __dirname;

describe("pepr", () => {
describe("projectRoot", () => {
it("returns pepr project root directory", async () => {
const expected = path.resolve(HERE, "../..");
const actual = await sut.projectRoot();
expect(actual).toBe(expected);
});
});

describe("prepWorkdir", () => {
const workdir = new Workdir(`${FILE}-prepWorkdir`, `${HERE}/../workroot/helpers`);

beforeAll(async () => await workdir.recreate());

it("builds pepr package and drops .tgz into given directory", async () => {
await sut.prepWorkdir(workdir.path());
const files = await fs.readdir(workdir.path());

expect(files).toHaveLength(1);
expect(files).toContain("pepr-0.0.0-development.tgz");
});
});

describe("cli", () => {
const workdir = new Workdir(`${FILE}-cli`, `${HERE}/../workroot/helpers`);

beforeAll(async () => {
await workdir.recreate();
await sut.prepWorkdir(workdir.path());
});

it(
"invokes pepr command via .tgz",
async () => {
const res = await sut.cli(workdir.path(), { cmd: "pepr --version" });
expect(res.exitcode).toBe(0);
expect(res.stderr.join("").trim()).toBe("");
expect(res.stdout.join("").trim()).toBe("0.0.0-development");
},
time.toMs("2m"),
);
});
});
34 changes: 34 additions & 0 deletions integration/helpers/pepr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { resolve, join } from "node:path";
import { copyFile } from "node:fs/promises";
import { Spec, Cmd, Result } from "./cmd";
import { clone } from "ramda";

const HERE = __dirname;

export async function projectRoot(): Promise<string> {
const cmd = new Cmd({ cmd: `npm root`, cwd: HERE });
const res = await cmd.run();
const npmroot = res.stdout.join("").trim();
return resolve(npmroot, "..");
}

export async function prepWorkdir(workdir: string): Promise<void> {
const rootdir = await projectRoot();
const tgz = "pepr-0.0.0-development.tgz";
const src = join(rootdir, tgz);
const dst = join(workdir, tgz);
await copyFile(src, dst);
}

export async function cli(workdir: string, spec: Spec): Promise<Result> {
const tgz = "pepr-0.0.0-development.tgz";

const _spec = clone(spec);
_spec.cwd = workdir;

const _cmd = _spec.cmd.trim().replace(/^pepr /, `npx --yes file://./${tgz} `);
_spec.cmd = _cmd;

const cmd = new Cmd(_spec);
return await cmd.runRaw();
}
64 changes: 64 additions & 0 deletions integration/helpers/time.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { describe, it, expect } from "@jest/globals";
import * as sut from "./time";

describe("toHuman()", () => {
it.each([
// simple
[1, "1ms"],
[1000, "1s"],
[60000, "1m"],
[3600000, "1h"],
[86400000, "1d"],
[604800000, "1w"],
[2592000000, "1mo"],
[31536000000, "1y"],

// combined
[34822861001, "1y1mo1w1d1h1m1s1ms"],
])("given ms '%s', returns '%s' duration", (ms, human) => {
const result = sut.toHuman(ms);
expect(result).toBe(human);
});
});

describe("toMs()", () => {
it.each([
// simple
["1ms", 1],
["1s", 1000],
["60s", 60000],
["1m", 60000],
["60m", 3600000],
["1h", 3600000],
["24h", 86400000],
["1d", 86400000],
["7d", 604800000],
["1w", 604800000],
["30d", 2592000000],
["1mo", 2592000000],
["365d", 31536000000],
["1y", 31536000000],

// weird
["0001s", 1000],
["1 s ", 1000],

// combined
["1y1mo1w1d1h1m1s1ms", 34822861001],
["1ms1s1m1h1d1w1mo1y", 34822861001],
])("given duration '%s', returns '%s' ms", (human, ms) => {
const result = sut.toMs(human);
expect(result).toBe(ms);
});

it.each([
// bad
["h1m1s", /Unrecognized number .* while parsing/],
["1z", /Unrecognized unit .* while parsing/],
])("given duration '%s', throws error matching '%s'", (human, err) => {
expect(() => sut.toMs(human)).toThrow(err);
});
});
Loading

0 comments on commit 7a9577d

Please sign in to comment.