import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option"; import * as AzureDevOpsCli from "./AzureDevOpsCli.ts";import * as AzureDevOpsSourceControlProvider from "./AzureDevOpsSourceControlProvider.ts"; function makeProvider(azure: Partial<AzureDevOpsCli.AzureDevOpsCli["Service"]>) { return AzureDevOpsSourceControlProvider.make.pipe( Effect.provide(Layer.mock(AzureDevOpsCli.AzureDevOpsCli)(azure)), );} it.effect("maps Azure DevOps PR summaries into provider-neutral change requests", () => Effect.gen(function* () { const provider = yield* makeProvider({ getPullRequest: () => Effect.succeed({ number: 42, title: "Add Azure provider", url: "https://dev.azure.com/acme/project/_git/repo/pullrequest/42", baseRefName: "main", headRefName: "feature/source-control", state: "closed", closedAt: "2026-08-23T10:00:00Z", updatedAt: Option.none(), }), }); const changeRequest = yield* provider.getChangeRequest({ cwd: "/repo", reference: "42", }); assert.deepStrictEqual(changeRequest, { provider: "azure-devops", number: 42, title: "Add Azure provider", url: "https://dev.azure.com/acme/project/_git/repo/pullrequest/42", baseRefName: "main", headRefName: "feature/source-control", state: "closed", closedAt: "2026-08-23T10:00:00Z", mergedAt: null, updatedAt: Option.none(), isCrossRepository: false, }); }),); it.effect("adds change-request context while retaining Azure CLI causes", () => Effect.gen(function* () { const cause = new AzureDevOpsCli.AzureDevOpsCommandFailedError({ operation: "execute", command: "az", cwd: "/repo", argumentCount: 2, cause: new Error("raw upstream detail that should remain in the cause"), }); const provider = yield* makeProvider({ checkoutPullRequest: () => Effect.fail(cause), }); const error = yield* provider .checkoutChangeRequest({ cwd: "/repo", reference: "#42" }) .pipe(Effect.flip); assert.deepStrictEqual( { provider: error.provider, operation: error.operation, command: error.command, cwd: error.cwd, reference: error.reference, detail: error.detail, }, { provider: "azure-devops", operation: "checkoutChangeRequest", command: "az", cwd: "/repo", reference: "#42", detail: "Azure DevOps CLI command failed.", }, ); assert.strictEqual(error.cause, cause); assert.equal(error.message.includes("raw upstream detail"), false); }),); it.effect("creates Azure DevOps PRs through provider-neutral input names", () => Effect.gen(function* () { let createInput: | Parameters<AzureDevOpsCli.AzureDevOpsCli["Service"]["createPullRequest"]>[0] | null = null; const provider = yield* makeProvider({ createPullRequest: (input) => { createInput = input; return Effect.void; }, }); yield* provider.createChangeRequest({ cwd: "/repo", baseRefName: "main", headSelector: "feature/provider", title: "Provider PR", bodyFile: "/tmp/body.md", }); assert.deepStrictEqual(createInput, { cwd: "/repo", baseBranch: "main", headSelector: "feature/provider", title: "Provider PR", bodyFile: "/tmp/body.md", }); }),);
import { assert, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as AzureDevOpsCli from "./AzureDevOpsCli.ts";
import * as AzureDevOpsSourceControlProvider from "./AzureDevOpsSourceControlProvider.ts";
function makeProvider(azure: Partial<AzureDevOpsCli.AzureDevOpsCli["Service"]>) {
return AzureDevOpsSourceControlProvider.make.pipe(
Effect.provide(Layer.mock(AzureDevOpsCli.AzureDevOpsCli)(azure)),
);
}
it.effect("maps Azure DevOps PR summaries into provider-neutral change requests", () =>
Effect.gen(function* () {
const provider = yield* makeProvider({
getPullRequest: () =>
Effect.succeed({
number: 42,
title: "Add Azure provider",
url: "https://dev.azure.com/acme/project/_git/repo/pullrequest/42",
baseRefName: "main",
headRefName: "feature/source-control",
state: "closed",
closedAt: "2026-08-23T10:00:00Z",
updatedAt: Option.none(),
}),
});
const changeRequest = yield* provider.getChangeRequest({
cwd: "/repo",
reference: "42",
});
assert.deepStrictEqual(changeRequest, {
provider: "azure-devops",
number: 42,
title: "Add Azure provider",
url: "https://dev.azure.com/acme/project/_git/repo/pullrequest/42",
baseRefName: "main",
headRefName: "feature/source-control",
state: "closed",
closedAt: "2026-08-23T10:00:00Z",
mergedAt: null,
updatedAt: Option.none(),
isCrossRepository: false,
});
}),
);
it.effect("adds change-request context while retaining Azure CLI causes", () =>
Effect.gen(function* () {
const cause = new AzureDevOpsCli.AzureDevOpsCommandFailedError({
operation: "execute",
command: "az",
cwd: "/repo",
argumentCount: 2,
cause: new Error("raw upstream detail that should remain in the cause"),
});
const provider = yield* makeProvider({
checkoutPullRequest: () => Effect.fail(cause),
});
const error = yield* provider
.checkoutChangeRequest({ cwd: "/repo", reference: "#42" })
.pipe(Effect.flip);
assert.deepStrictEqual(
{
provider: error.provider,
operation: error.operation,
command: error.command,
cwd: error.cwd,
reference: error.reference,
detail: error.detail,
},
{
provider: "azure-devops",
operation: "checkoutChangeRequest",
command: "az",
cwd: "/repo",
reference: "#42",
detail: "Azure DevOps CLI command failed.",
},
);
assert.strictEqual(error.cause, cause);
assert.equal(error.message.includes("raw upstream detail"), false);
}),
);
it.effect("creates Azure DevOps PRs through provider-neutral input names", () =>
Effect.gen(function* () {
let createInput:
| Parameters<AzureDevOpsCli.AzureDevOpsCli["Service"]["createPullRequest"]>[0]
| null = null;
const provider = yield* makeProvider({
createPullRequest: (input) => {
createInput = input;
return Effect.void;
},
});
yield* provider.createChangeRequest({
cwd: "/repo",
baseRefName: "main",
headSelector: "feature/provider",
title: "Provider PR",
bodyFile: "/tmp/body.md",
});
assert.deepStrictEqual(createInput, {
cwd: "/repo",
baseBranch: "main",
headSelector: "feature/provider",
title: "Provider PR",
bodyFile: "/tmp/body.md",
});
}),
);