Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/tests #8

Draft
wants to merge 9 commits into
base: native-rebase-for-creating-initial-git-rebase-todo
Choose a base branch
from
54 changes: 54 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: "tests"

on: [push, workflow_dispatch]

jobs:
test:
runs-on: ${{ matrix.os }}

# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-preventing-a-specific-failing-matrix-job-from-failing-a-workflow-run
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
node: [12, 14]
experimental: [false]
exclude:
- os: macos-latest
node: 12
include:
# test macos - for some reason, internet conn fails @ github actions
- os: macos-latest
node: 12
experimental: true

# - os: ubuntu-latest
# node: 10
# experimental: true
# - os: ubuntu-latest
# # v16 should work fine; there're some issues in CI with `krb5-config`
# node: 16
# experimental: true
# - os: ubuntu-latest
# node: 18
# experimental: true

# # test windows, w/ wanted versions
# - os: windows-latest
# node: 12
# experimental: true
# - os: windows-latest
# node: 14
# experimental: true

steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'
- run: yarn --frozen-lockfile
- run: yarn test

27 changes: 27 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ts-node tests",
"type": "node",
"request": "launch",
"args": [
// "${relativeFile}" //
"test/run.ts"
],
"runtimeArgs": [
"-r", //
"ts-node/register"
],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart",
"env": {
"DEBUG": "gsr:*"
}
}
]
}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
"git-stacked-rebase": "./dist/git-stacked-rebase.js"
},
"scripts": {
"prebuild": "node ./script/prebuild.js",
"build": "yarn tsc -b",
"postbuild": "node ./script/postbuild.js",
"prebuild:lean": "node ./script/prebuild.js",
"test": "ts-node-dev ./test/run.ts",
"build": "yarn test && yarn build:lean",
"build:lean": "yarn tsc -b",
"postbuild:lean": "node ./script/postbuild.js",
"prepack": "yarn build"
},
"devDependencies": {
Expand Down
10 changes: 5 additions & 5 deletions parse-todo-of-stacked-rebase/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export const regularRebaseCommands = {
// m: standardCommand,
} as const;

type RegularRebaseCommand = keyof typeof regularRebaseCommands;
export type RegularRebaseCommand = keyof typeof regularRebaseCommands;

/**
* TODO: assert each value is `RegularRebaseCommand`,
Expand Down Expand Up @@ -219,7 +219,7 @@ export const stackedRebaseCommands = {
}),
} as const;

type StackedRebaseCommand = keyof typeof stackedRebaseCommands;
export type StackedRebaseCommand = keyof typeof stackedRebaseCommands;

// const allowedCommandAliasesFromGitStackedRebase: { [key: string]: AllowedGitStackedRebaseCommand } = {
const stackedRebaseCommandAliases = {
Expand All @@ -229,10 +229,10 @@ const stackedRebaseCommandAliases = {

type StackedRebaseCommandAlias = keyof typeof stackedRebaseCommandAliases;

type EitherRebaseCommand = RegularRebaseCommand | StackedRebaseCommand;
type EitherRebaseCommandAlias = RegularRebaseCommandAlias | StackedRebaseCommandAlias;
export type EitherRebaseCommand = RegularRebaseCommand | StackedRebaseCommand;
export type EitherRebaseCommandAlias = RegularRebaseCommandAlias | StackedRebaseCommandAlias;

type EitherRebaseEitherCommandOrAlias = EitherRebaseCommand | EitherRebaseCommandAlias;
export type EitherRebaseEitherCommandOrAlias = EitherRebaseCommand | EitherRebaseCommandAlias;

type MapOfAllowedRebaseCommands = {
[key in EitherRebaseCommand]: Command;
Expand Down
2 changes: 2 additions & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
folders-to-delete
.tmp
248 changes: 248 additions & 0 deletions test/experiment.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
#!/usr/bin/env ts-node-dev

import fs from "fs";
import path from "path";
import assert from "assert";

import Git from "nodegit";

import { gitStackedRebase, defaultGitCmd } from "../git-stacked-rebase";

import { RegularRebaseCommand } from "../parse-todo-of-stacked-rebase/validator";
import { createExecSyncInRepo } from "../util/execSyncInRepo";
import { configKeys } from "../configKeys";

export async function testCase() {
const {
repo, //
config,
sig,
dir,
} = await setupRepo();

const commitOidsInInitial: Git.Oid[] = [];
const initialBranch: Git.Reference = await appendCommitsTo(commitOidsInInitial, 3, repo, sig);

const latestStackedBranch: Git.Reference = await Git.Branch.create(
repo,
"stack-latest",
await repo.getHeadCommit(),
0
);
await repo.checkoutBranch(latestStackedBranch);

const execSyncInRepo = createExecSyncInRepo(repo);

// const read = () => execSyncInRepo("read");
const read = () => void 0;

read();

const commitOidsInLatestStacked: Git.Oid[] = [];
await appendCommitsTo(commitOidsInLatestStacked, 12, repo, sig);

const newPartialBranches = [
["partial-1", 4],
["partial-2", 6],
["partial-3", 8],
] as const;

console.log("launching 1st rebase to create partial branches");
await gitStackedRebase(initialBranch.shorthand(), {
gitDir: dir,
getGitConfig: () => config,
editor: async ({ filePath }) => {
console.log("filePath %s", filePath);

for (const [newPartial, nthCommit] of newPartialBranches) {
await humanOpAppendLineAfterNthCommit(
filePath,
commitOidsInLatestStacked[nthCommit].tostrS(),
`branch-end-new ${newPartial}`
);
}

console.log("finished editor");

read();
},
});

console.log("looking up branches to make sure they were created successfully");
read();
for (const [newPartial] of newPartialBranches) {
/**
* will throw if branch does not exist
* TODO "properly" expect to not throw
*/
await Git.Branch.lookup(repo, newPartial, Git.Branch.BRANCH.LOCAL);
}

/**
*
*/
console.log("launching 2nd rebase to change command of nth commit");
read();

const nthCommit2ndRebase = 5;

await gitStackedRebase(initialBranch.shorthand(), {
gitDir: dir,
getGitConfig: () => config,
editor: async ({ filePath }) => {
const SHA = commitOidsInLatestStacked[nthCommit2ndRebase].tostrS();

humanOpChangeCommandOfNthCommitInto("edit", SHA, filePath);
},
});
/**
* rebase will now exit because of the "edit" command,
* and so will our stacked rebase,
* allowing us to edit.
*/

fs.writeFileSync(nthCommit2ndRebase.toString(), "new data from 2nd rebase\n");

execSyncInRepo(`${defaultGitCmd} add .`);
execSyncInRepo(`${defaultGitCmd} -c commit.gpgSign=false commit --amend --no-edit`);

execSyncInRepo(`${defaultGitCmd} rebase --continue`);

execSyncInRepo(`${defaultGitCmd} status`);
read();

/**
* now some partial branches will be "gone" from the POV of the latestBranch<->initialBranch.
*
* TODO verify they are gone (history got modified successfully)
*/

// TODO

/**
* TODO continue with --apply
* TODO and then verify that partial branches are "back" in our POV properly.
*/

console.log("attempting early 3rd rebase to --apply");
read();

await gitStackedRebase(initialBranch.shorthand(), {
gitDir: dir,
getGitConfig: () => config,
apply: true,
});
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export async function setupRepo() {
const dir: string = path.join(__dirname, ".tmp");
if (fs.existsSync(dir)) {
fs.rmdirSync(dir, { recursive: true });
}
fs.mkdirSync(dir);
console.log("tmpdir path %s", dir);

const foldersToDeletePath: string = path.join(__dirname, "folders-to-delete");
fs.appendFileSync(foldersToDeletePath, dir + "\n", { encoding: "utf-8" });

process.chdir(dir);
console.log("chdir to tmpdir");

const isBare = 0;
const repo: Git.Repository = await Git.Repository.init(dir, isBare);

const config: Git.Config = await repo.config();

await config.setBool(configKeys.autoApplyIfNeeded, Git.Config.MAP.FALSE);
await config.setString("user.email", "tester@test.com");
await config.setString("user.name", "tester");

/**
* fixups / not implemented in libgit2.
* though, would be better if received empty/minimal config by default..
*/
await config.setString("merge.conflictStyle", "diff3"); // zdiff3

const sig: Git.Signature = await Git.Signature.default(repo);
console.log("sig %s", sig);

const inicialCommitId = "Initial commit (from setupRepo)";
const initialCommit: Git.Oid = await fs.promises
.writeFile(inicialCommitId, inicialCommitId) //
.then(() => repo.createCommitOnHead([inicialCommitId], sig, sig, inicialCommitId));

return {
dir,
repo,
config,
sig,
initialCommit,
} as const;
}

async function appendCommitsTo(
alreadyExistingCommits: Git.Oid[],
n: number,
repo: Git.Repository, //
sig: Git.Signature
): Promise<Git.Reference> {
assert(n > 0, "cannot append <= 0 commits");

const commits: string[] = new Array(n)
.fill(0) //
.map((_, i) => "a".charCodeAt(0) + i + alreadyExistingCommits.length)
.map((ascii) => String.fromCharCode(ascii));

for (const c of commits) {
const branchName: string = repo.isEmpty() ? "<initial>" : (await repo.getCurrentBranch()).shorthand();
const cInBranch: string = c + " in " + branchName;

const oid: Git.Oid = await fs.promises
.writeFile(c, cInBranch) //
.then(() => repo.createCommitOnHead([c], sig, sig, cInBranch));

alreadyExistingCommits.push(oid);

console.log(`oid of commit "%s" in branch "%s": %s`, c, branchName, oid);
}

return repo.getCurrentBranch();
}

/**
* TODO general "HumanOp" for `appendLineAfterNthCommit` & similar utils
*/
async function humanOpAppendLineAfterNthCommit(
filePath: string, //
commitSHA: string,
newLine: string
): Promise<void> {
const file = await fs.promises.readFile(filePath, { encoding: "utf-8" });
const lines = file.split("\n");
const lineIdx: number = lines.findIndex((line) => line.startsWith(`pick ${commitSHA}`));

console.log("commitSHA: %s, lineIdx: %s, newLine: %s", commitSHA, lineIdx, newLine);

lines.splice(lineIdx, 0, newLine);

await fs.promises.writeFile(filePath, lines.join("\n"));
}

function humanOpChangeCommandOfNthCommitInto(
newCommand: RegularRebaseCommand, //
commitSHA: string,
filePath: string
): void {
const file = fs.readFileSync(filePath, { encoding: "utf-8" });
const lines = file.split("\n");
const lineIdx: number = lines.findIndex((line) => line.startsWith(`pick ${commitSHA}`));

console.log("commitSHA: %s, lineIdx: %s, newCommand: %s", commitSHA, lineIdx, newCommand);

const parts = lines[lineIdx].split(" ");
parts[0] = newCommand;
lines[lineIdx] = parts.join(" ");

fs.writeFileSync(filePath, lines.join("\n"));
}
13 changes: 13 additions & 0 deletions test/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env ts-node-dev

import { testCase } from "./experiment.spec";

main();
function main() {
testCase()
.then(() => process.stdout.write("\nsuccess\n\n"))
.catch((e) => {
process.stderr.write("\nfailure: " + e + "\n\n");
process.exit(1);
});
}