Skip to content

Commit

Permalink
test(forge): add most of missing tests (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
tugrulates authored Feb 28, 2025
1 parent 638ec8b commit 89d1eb8
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 35 deletions.
31 changes: 17 additions & 14 deletions tool/forge/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { PackageError, packageInfo } from "@roka/forge/package";
import { expandGlob } from "@std/fs";
import { dirname, join } from "@std/path";
import { basename, dirname, fromFileUrl } from "@std/path";

/** Options for {@linkcode version}. */
export interface VersionOptions {
Expand Down Expand Up @@ -79,19 +79,22 @@ async function packageVersion(): Promise<string> {
} catch (e: unknown) {
if (!(e instanceof PackageError)) throw e;
}
if (import.meta.dirname) {
for await (
const path of expandGlob("**/deno.json", {
root: join(import.meta.dirname, "..", "..", "dist"),
includeDirs: false,
})
) {
try {
const pkg = await packageInfo({ directory: dirname(path.path) });
if (pkg.version) return pkg.version;
} catch (e: unknown) {
if (!(e instanceof PackageError)) throw e;
}
let directory = fromFileUrl(Deno.mainModule);
while (!basename(directory).match(/^deno-compile-.+$/)) {
directory = dirname(directory);
if (directory === dirname(directory)) break;
}
for await (
const path of expandGlob("**/deno.json", {
root: directory,
includeDirs: false,
})
) {
try {
const pkg = await packageInfo({ directory: dirname(path.path) });
if (pkg.version) return pkg.version;
} catch (e: unknown) {
if (!(e instanceof PackageError)) throw e;
}
}
return "(unknown)";
Expand Down
157 changes: 157 additions & 0 deletions tool/forge/bump.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { bump } from "@roka/forge/bump";
import { PackageError, packageInfo } from "@roka/forge/package";
import { tempRepository } from "@roka/git/testing";
import { testRepository } from "@roka/github/testing";
import { assertExists, assertRejects } from "@std/assert";
import { assert } from "@std/assert/assert";
import { assertEquals } from "@std/assert/equals";
import { assertMatch } from "@std/assert/match";
import { pooledMap } from "@std/async/pool";

Deno.test("bump() rejects package without version", async () => {
await using git = await tempRepository();
const config = { name: "@scope/module" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("feat(module): introduce module");
await git.tags.create("[email protected]");
const pkg = await packageInfo({ directory: git.path() });
await assertRejects(() => bump([pkg]), PackageError);
});

Deno.test("bump() rejects package without update", async () => {
await using git = await tempRepository();
const config = { name: "@scope/module", version: "0.1.0" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("feat(module): introduce module");
await git.tags.create("[email protected]");
const pkg = await packageInfo({ directory: git.path() });
await assertRejects(() => bump([pkg]), PackageError);
});

Deno.test("bump() updates package", async () => {
await using git = await tempRepository();
const config = { name: "@scope/module", version: "0.1.0" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("feat(module): introduce module");
await git.tags.create("[email protected]");
await git.commits.create("fix(module): fix module", { allowEmpty: true });
const pkg = await packageInfo({ directory: git.path() });
const pr = await bump([pkg]);
assertEquals(pr, undefined);
const updated = await packageInfo({ directory: git.path() });
assertEquals(updated.config, { ...pkg.config, version: "0.1.1" });
const commit = await git.commits.head();
assertEquals(commit.summary, "fix(module): fix module");
});

Deno.test("bump() minor updates unreleased package", async () => {
await using git = await tempRepository();
const config = { name: "@scope/module", version: "0.0.0" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("feat(module): introduce module");
const pkg = await packageInfo({ directory: git.path() });
const pr = await bump([pkg]);
assertEquals(pr, undefined);
const updated = await packageInfo({ directory: git.path() });
assertEquals(updated.config, { ...pkg.config, version: "0.1.0" });
});

Deno.test("bump() patch updates unreleased package", async () => {
await using git = await tempRepository();
const config = { name: "@scope/module", version: "0.0.0" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("fix(module): introduce module with a fix");
const pkg = await packageInfo({ directory: git.path() });
const pr = await bump([pkg]);
assertEquals(pr, undefined);
const updated = await packageInfo({ directory: git.path() });
assertEquals(updated.config, { ...pkg.config, version: "0.0.1" });
});

Deno.test("bump() creates pull request", async () => {
await using remote = await tempRepository();
await using git = await tempRepository({ clone: remote });
const repo = testRepository({ git });
const config = { name: "@scope/module", version: "0.0.0" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("feat(module): introduce module");
const pkg = await packageInfo({ directory: git.path() });
const pr = await bump([pkg], {
repo,
pr: true,
user: { name: "bump-name", email: "bump-email" },
});
assert(pr);
assertEquals(pr.title, "chore: bump module version");
assertMatch(pr.body, /## module@0.1.0 \[minor\]/);
assertMatch(pr.body, /feat\(module\): introduce module/);
const updated = await packageInfo({ directory: git.path() });
assertEquals(updated.config, { ...pkg.config, version: "0.1.0" });
const commit = await git.commits.head();
assertEquals(commit.summary, "chore: bump module version");
assertEquals(commit.author.name, "bump-name");
assertEquals(commit.author.email, "bump-email");
});

Deno.test("bump() updates pull request", async () => {
await using remote = await tempRepository({ bare: true });
await using git = await tempRepository({ clone: remote });
const repo = testRepository({ git });
const config = { name: "@scope/module", version: "0.0.0" };
await Deno.writeTextFile(git.path("deno.json"), JSON.stringify(config));
await git.index.add("deno.json");
await git.commits.create("feat(module): introduce module");
const defaultBranch = await git.branches.current();
assertExists(defaultBranch);
const pkg = await packageInfo({ directory: git.path() });
const [pr1, pr2] = await Array.fromAsync(pooledMap(1, [1, 2], async () => {
const pr = await bump([pkg], { repo, pr: true });
const branch = await git.branches.current();
assertExists(branch);
await git.branches.checkout({ target: defaultBranch });
await git.branches.delete(branch, { force: true });
return pr;
}));
assert(pr1?.number === pr2?.number);
});

Deno.test("bump() updates multiple packages", async () => {
await using remote = await tempRepository();
await using git = await tempRepository({ clone: remote });
const repo = testRepository({ git });
const config1 = { name: "@scope/module1", version: "0.0.0" };
const config2 = { name: "@scope/module2", version: "0.0.0" };
await Deno.mkdir(git.path("module1"));
await Deno.mkdir(git.path("module2"));
await Deno.writeTextFile(
git.path("module1/deno.json"),
JSON.stringify(config1),
);
await Deno.writeTextFile(
git.path("module2/deno.json"),
JSON.stringify(config2),
);
await git.index.add("module1/deno.json");
await git.index.add("module2/deno.json");
await git.commits.create("feat(module1,module2): introduce modules");
const pkg1 = await packageInfo({ directory: git.path("module1") });
const pkg2 = await packageInfo({ directory: git.path("module2") });
const pr = await bump([pkg1, pkg2], { repo, pr: true });
assert(pr);
assertEquals(pr.title, "chore: bump versions");
assertMatch(pr.body, /## module1@0.1.0 \[minor\]/);
assertMatch(pr.body, /## module2@0.1.0 \[minor\]/);
assertMatch(pr.body, /feat\(module1,module2\): introduce modules/);
const updated1 = await packageInfo({ directory: git.path("module1") });
const updated2 = await packageInfo({ directory: git.path("module2") });
assertEquals(updated1.config, { ...pkg1.config, version: "0.1.0" });
assertEquals(updated2.config, { ...pkg2.config, version: "0.1.0" });
const commit = await git.commits.head();
assertEquals(commit.summary, "chore: bump versions");
});
32 changes: 21 additions & 11 deletions tool/forge/bump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
*/

import { changelog } from "@roka/forge/changelog";
import { type Package, packageInfo } from "@roka/forge/package";
import { github, type PullRequest } from "@roka/github";
import { assert, assertEquals } from "@std/assert";
import { join } from "@std/path";
import { type Package, PackageError, packageInfo } from "@roka/forge/package";
import { github, type PullRequest, type Repository } from "@roka/github";
import { assertEquals } from "@std/assert";
import { common, join } from "@std/path";
import { format, parse } from "@std/semver";

const BUMP_BRANCH = "automated/bump";
Expand All @@ -20,6 +20,12 @@ const BUMP_BRANCH = "automated/bump";
export interface BumpOptions {
/** GitHub access token. */
token?: string;
/**
* GitHub repository to use.
*
* If not defined, the repository is determined from the package directories.
*/
repo?: Repository;
/** Git user for the bump commit. */
user?: {
/** Name of the user. */
Expand All @@ -34,22 +40,24 @@ export interface BumpOptions {
/**
* Update version number of packages on `deno.json`.
*
* The calculated version is based on the calculated version {@code pkg.update},
* dropping pre-release and build information.
* The calculated version is based on {@linkcode Package.update}, dropping
* pre-release and build information.
*/
export async function bump(
packages: Package[],
options?: BumpOptions,
): Promise<PullRequest | undefined> {
packages = await Promise.all(packages.map((pkg) => updateVersion(pkg)));
if (!options?.pr || packages.length === 0) return undefined;
const repo = await github(options).repos.get();
const directory = common(packages.map((pkg) => pkg.directory));
const { repo = await github(options).repos.get({ directory }), user } =
options ?? {};
const title = packages.length === 1
? "chore: bump package version"
: "chore: bump package versions";
? `chore: bump ${packages[0]?.module} version`
: "chore: bump versions";
const body = prBody(packages);
await repo.git.branches.checkout({ new: BUMP_BRANCH });
await repo.git.config.set({ ...options?.user && { user: options?.user } });
await repo.git.config.set({ ...user && { user } });
await repo.git.commits.create(title, { body, all: true });
let [pr] = await repo.pulls.list({ title, closed: false });
if (pr) {
Expand All @@ -63,7 +71,9 @@ export async function bump(
}

async function updateVersion(pkg: Package): Promise<Package> {
assert(pkg.update, "Cannot bump a package without update");
if (pkg.update === undefined) {
throw new PackageError("Cannot bump a package without update");
}
const version = format({
...parse(pkg.update.version),
prerelease: [],
Expand Down
27 changes: 27 additions & 0 deletions tool/forge/changelog.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { changelog } from "@roka/forge/changelog";
import { packageInfo } from "@roka/forge/package";
import { tempRepository } from "@roka/git/testing";
import { assertEquals } from "@std/assert/equals";

Deno.test("changelog() provides package changelog", async () => {
await using repo = await tempRepository();
const config = { name: "@scope/module", version: "0.0.0" };
await Deno.writeTextFile(repo.path("deno.json"), JSON.stringify(config));
await repo.index.add("deno.json");
await repo.commits.create("feat(module): introduce module");
await Deno.writeTextFile(repo.path("fix.ts"), "//fix");
await repo.index.add("fix.ts");
await repo.commits.create("fix(module): fix module");
await Deno.writeTextFile(repo.path("README.md"), "docs");
await repo.index.add("README.md");
await repo.commits.create("docs(module): add docs");
const pkg = await packageInfo({ directory: repo.path() });
assertEquals(
changelog(pkg),
[
" * docs(module): add docs",
" * fix(module): fix module",
" * feat(module): introduce module",
].join("\n"),
);
});
Loading

0 comments on commit 89d1eb8

Please sign in to comment.