Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 210 additions & 7 deletions apps/server/src/git/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
GitCommandError,
ProviderDriverKind,
ProviderInstanceId,
type SourceControlProviderKind,
TextGenerationError,
} from "@t3tools/contracts";
import * as GitHubCli from "../sourceControl/GitHubCli.ts";
Expand Down Expand Up @@ -637,6 +638,7 @@ function makeManager(input?: {
serverSettings?: Parameters<typeof ServerSettings.layerTest>[0];
setupScriptRunner?: ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"];
gitConfigReads?: string[];
sourceControlProviderKind?: SourceControlProviderKind | (() => SourceControlProviderKind);
}) {
const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario);
const textGeneration = createTextGeneration(input?.textGeneration);
Expand Down Expand Up @@ -676,15 +678,23 @@ function makeManager(input?: {
? GitHubSourceControlProvider.make
: Effect.succeed(input.sourceControlProvider)
).pipe(
Effect.map((provider) =>
SourceControlProviderRegistry.SourceControlProviderRegistry.of({
Effect.map((provider) => {
const sourceControlProvider = () => ({
...provider,
kind:
typeof input?.sourceControlProviderKind === "function"
? input.sourceControlProviderKind()
: (input?.sourceControlProviderKind ?? provider.kind),
});
return SourceControlProviderRegistry.SourceControlProviderRegistry.of({
resolveLink: (input) => provider.resolveLink?.(input),
get: () => Effect.succeed(provider),
resolveHandle: () => Effect.succeed({ provider, context: null }),
resolve: () => Effect.succeed(provider),
get: () => Effect.sync(sourceControlProvider),
resolveHandle: () =>
Effect.sync(() => ({ provider: sourceControlProvider(), context: null })),
resolve: () => Effect.sync(sourceControlProvider),
discover: Effect.succeed([]),
}),
),
});
}),
Effect.provide(Layer.succeed(GitHubCli.GitHubCli, gitHubCli)),
),
);
Expand Down Expand Up @@ -965,6 +975,199 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
}),
);

it.effect("status skips PR lookup when the source-control provider is unknown", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-unknown-provider-");
yield* initRepo(repoDir);
yield* runGit(repoDir, ["checkout", "-b", "feature/unknown-provider"]);
const remoteDir = yield* createBareRemote();
yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
yield* runGit(repoDir, ["push", "-u", "origin", "feature/unknown-provider"]);

const { manager, ghCalls } = yield* makeManager({
sourceControlProviderKind: "unknown",
});

const status = yield* manager.status({ cwd: repoDir });

expect(status.pr).toBeNull();
expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(0);
}),
);

it.effect("status retries an unknown provider on the short PR lookup cadence", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-provider-refinement-");
yield* initRepo(repoDir);
yield* runGit(repoDir, ["checkout", "-b", "feature/provider-refinement"]);
const remoteDir = yield* createBareRemote();
yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
yield* runGit(repoDir, ["push", "-u", "origin", "feature/provider-refinement"]);

let providerKind: SourceControlProviderKind = "unknown";
const existingPr = {
number: 411,
title: "Provider refinement PR",
url: "https://github.com/pingdotgg/t3code/pull/411",
baseRefName: "main",
headRefName: "feature/provider-refinement",
};
const { manager, ghCalls } = yield* makeManager({
sourceControlProviderKind: () => providerKind,
ghScenario: {
// Fake gh returns raw JSON stdout, matching the CLI boundary under test.
// @effect-diagnostics-next-line preferSchemaOverJson:off
prListSequence: [JSON.stringify([existingPr])],
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
},
});

const first = yield* manager.status({ cwd: repoDir });
expect(first.pr).toBeNull();
expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(0);

providerKind = "github";
yield* manager.invalidateRemoteStatus(repoDir);

const cached = yield* manager.status({ cwd: repoDir });
expect(cached.pr).toBeNull();
expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(0);

yield* TestClock.adjust(Duration.seconds(20));

const second = yield* manager.status({ cwd: repoDir });
expect(second.pr?.number).toBe(411);
expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(1);
}),
);

it.effect("status keeps the last known PR while the provider is temporarily unknown", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-provider-hiccup-");
yield* initRepo(repoDir);
yield* runGit(repoDir, ["checkout", "-b", "feature/provider-hiccup"]);
const remoteDir = yield* createBareRemote();
yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
yield* runGit(repoDir, ["push", "-u", "origin", "feature/provider-hiccup"]);

let providerKind: SourceControlProviderKind = "github";
const existingPr = {
number: 412,
title: "Provider hiccup PR",
url: "https://github.com/pingdotgg/t3code/pull/412",
baseRefName: "main",
headRefName: "feature/provider-hiccup",
};
const { manager } = yield* makeManager({
sourceControlProviderKind: () => providerKind,
ghScenario: {
// Fake gh returns raw JSON stdout, matching the CLI boundary under test.
// @effect-diagnostics-next-line preferSchemaOverJson:off
prListSequence: [JSON.stringify([existingPr])],
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
},
});

const first = yield* manager.status({ cwd: repoDir });
expect(first.pr?.number).toBe(412);

providerKind = "unknown";
yield* manager.invalidateStatus(repoDir);

const second = yield* manager.status({ cwd: repoDir });
expect(second.pr?.number).toBe(412);
}),
);

it.effect("branch PR lookup returns null when the source-control provider is unknown", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-unknown-branch-provider-");
yield* initRepo(repoDir);
const remoteDir = yield* createBareRemote();
yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
yield* runGit(repoDir, ["checkout", "-b", "feature/unknown-branch-provider"]);
yield* runGit(repoDir, ["push", "-u", "origin", "feature/unknown-branch-provider"]);

const { manager, ghCalls } = yield* makeManager({
sourceControlProviderKind: "unknown",
});

const pullRequest = yield* manager.branchPullRequest({
cwd: repoDir,
branch: "feature/unknown-branch-provider",
});

expect(pullRequest).toBeNull();

yield* runGit(repoDir, ["config", "--unset", "remote.origin.url"]);

expect(
yield* manager.branchPullRequest({
cwd: repoDir,
branch: "feature/unknown-branch-provider",
}),
).toBeNull();

expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(0);
}),
);

it.effect("branch PR refresh preserves unknown-provider backoff and retries after expiry", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-unknown-provider-refresh-");
yield* initRepo(repoDir);
const branch = "feature/unknown-provider-refresh";
yield* runGit(repoDir, ["checkout", "-b", branch]);
const remoteDir = yield* createBareRemote();
yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
yield* runGit(repoDir, ["push", "-u", "origin", branch]);

let providerKind: SourceControlProviderKind = "unknown";
let providerLookups = 0;
const { manager, ghCalls } = yield* makeManager({
sourceControlProviderKind: () => {
providerLookups += 1;
return providerKind;
},
ghScenario: {
prListSequence: [
encodeCliJson([
{
number: 413,
title: "Recovered provider PR",
url: "https://github.com/pingdotgg/t3code/pull/413",
baseRefName: "main",
headRefName: branch,
},
]),
],
},
});
const input = { cwd: repoDir, branch };

expect(yield* manager.branchPullRequest(input)).toBeNull();
expect(yield* manager.branchPullRequest(input, { refresh: true })).toBeNull();
expect(yield* manager.branchPullRequest(input, { refresh: true })).toBeNull();
expect(providerLookups).toBe(1);

yield* TestClock.adjust(Duration.seconds(20));
expect(yield* manager.branchPullRequest(input, { refresh: true })).toBeNull();
expect(providerLookups).toBe(2);

providerKind = "github";
yield* TestClock.adjust(Duration.seconds(39));
expect(yield* manager.branchPullRequest(input, { refresh: true })).toBeNull();
expect(providerLookups).toBe(2);
expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(0);

yield* TestClock.adjust(Duration.seconds(1));
expect(yield* manager.branchPullRequest(input, { refresh: true })).toMatchObject({
number: 413,
state: "open",
});
expect(providerLookups).toBe(3);
expect(ghCalls.filter((call) => call.startsWith("pr list "))).toHaveLength(1);
}),
);

it.effect("status briefly caches repeated lookups for the same cwd", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-");
Expand Down
Loading
Loading