// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.import * as NodeFS from "node:fs";import * as NodeServices from "@effect/platform-node/NodeServices";import { expect, it } from "@effect/vitest";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Path from "effect/Path";import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";import { TestClock } from "effect/testing"; import * as ProcessRunner from "../processRunner.ts";import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts"; const normalizePathSeparators = (value: string) => value.replaceAll("\\", "/");const normalizeResolvedPath = (value: string) => normalizePathSeparators(value); const git = (cwd: string, args: ReadonlyArray<string>) => Effect.gen(function* () { const processRunner = yield* ProcessRunner.ProcessRunner; return yield* processRunner.run({ command: "git", args: ["-C", cwd, ...args], }); }).pipe(Effect.provide(ProcessRunner.layer)); const makeRepositoryIdentityResolverTestLayer = (options: { readonly positiveCacheTtl?: Duration.Input; readonly negativeCacheTtl?: Duration.Input;}) => Layer.effect( RepositoryIdentityResolver.RepositoryIdentityResolver, RepositoryIdentityResolver.make({ cacheCapacity: 16, ...options, }), ).pipe(Layer.provide(ProcessRunner.layer)); it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { it.effect("reuses the cached Git root for repeated workspace lookups", () => { const calls: Array<ReadonlyArray<string>> = []; const processRunner = Layer.succeed(ProcessRunner.ProcessRunner, { run: (input) => Effect.sync(() => { calls.push(input.args); return { stdout: input.args.includes("rev-parse") ? "/repo\n" : "origin\tgit@github.com:T3Tools/t3code.git (fetch)\n", stderr: "", code: ChildProcessSpawner.ExitCode(0), timedOut: false, stdoutTruncated: false, stderrTruncated: false, stdoutInvalidUtf8: false, stderrInvalidUtf8: false, }; }), }); const resolverLayer = Layer.effect( RepositoryIdentityResolver.RepositoryIdentityResolver, RepositoryIdentityResolver.make(), ).pipe(Layer.provide(processRunner)); return Effect.gen(function* () { const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const first = yield* resolver.resolve("/repo/packages/web"); const second = yield* resolver.resolve("/repo/packages/web"); expect(first?.canonicalKey).toBe("github.com/t3tools/t3code"); expect(second).toEqual(first); expect(calls).toEqual([ ["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"], ["-C", "/repo", "remote", "-v"], ]); }).pipe(Effect.provide(resolverLayer)); }); it.effect("retries Git root discovery after a failed lookup", () => { const calls: Array<ReadonlyArray<string>> = []; let rootAttempts = 0; const processRunner = Layer.succeed(ProcessRunner.ProcessRunner, { run: (input) => Effect.sync(() => { calls.push(input.args); const rootLookup = input.args.includes("rev-parse"); const failed = rootLookup && rootAttempts++ === 0; return { stdout: rootLookup ? failed ? "" : "/repo\n" : "origin\tgit@github.com:T3Tools/t3code.git (fetch)\n", stderr: failed ? "temporary Git failure" : "", code: ChildProcessSpawner.ExitCode(failed ? 1 : 0), timedOut: false, stdoutTruncated: false, stderrTruncated: false, stdoutInvalidUtf8: false, stderrInvalidUtf8: false, }; }), }); const resolverLayer = Layer.effect( RepositoryIdentityResolver.RepositoryIdentityResolver, RepositoryIdentityResolver.make(), ).pipe(Layer.provide(processRunner)); return Effect.gen(function* () { const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; expect(yield* resolver.resolve("/repo/packages/web")).toBeNull(); const recovered = yield* resolver.resolve("/repo/packages/web"); expect(recovered?.rootPath).toBe("/repo"); expect(calls).toEqual([ ["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"], ["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"], ["-C", "/repo", "remote", "-v"], ]); }).pipe(Effect.provide(resolverLayer)); }); it.effect("normalizes equivalent GitHub remotes into a stable repository identity", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-test-", }); yield* git(cwd, ["init"]); yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const identity = yield* resolver.resolve(cwd); // Native realpath, since git reports the long form of a directory the // temp dir may name by its 8.3 short form on Windows. const resolvedIdentityRoot = identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath); const resolvedCwd = NodeFS.realpathSync.native(cwd); expect(identity).not.toBeNull(); expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code"); expect(normalizeResolvedPath(resolvedIdentityRoot)).toBe(normalizeResolvedPath(resolvedCwd)); expect(identity?.displayName).toBe("t3tools/t3code"); expect(identity?.provider).toBe("github"); expect(identity?.owner).toBe("t3tools"); expect(identity?.name).toBe("t3code"); }).pipe(Effect.provide(RepositoryIdentityResolver.layer)), ); it.effect("returns the git top-level root path when resolving from a nested workspace", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const repoRoot = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-nested-root-test-", }); const nestedWorkspace = path.join(repoRoot, "packages", "web"); yield* fileSystem.makeDirectory(nestedWorkspace, { recursive: true }); yield* git(repoRoot, ["init"]); yield* git(repoRoot, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const identity = yield* resolver.resolve(nestedWorkspace); const resolvedIdentityRoot = identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath); const resolvedRepoRoot = NodeFS.realpathSync.native(repoRoot); expect(identity).not.toBeNull(); expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code"); expect(normalizeResolvedPath(resolvedIdentityRoot)).toBe( normalizeResolvedPath(resolvedRepoRoot), ); }).pipe(Effect.provide(RepositoryIdentityResolver.layer)), ); it.effect("returns null for non-git folders and repos without remotes", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const nonGitDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-non-git-", }); const gitDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-no-remote-", }); yield* git(gitDir, ["init"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const nonGitIdentity = yield* resolver.resolve(nonGitDir); const noRemoteIdentity = yield* resolver.resolve(gitDir); expect(nonGitIdentity).toBeNull(); expect(noRemoteIdentity).toBeNull(); }).pipe(Effect.provide(RepositoryIdentityResolver.layer)), ); it.effect("prefers upstream over origin when both remotes are configured", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-upstream-test-", }); yield* git(cwd, ["init"]); yield* git(cwd, ["remote", "add", "origin", "git@github.com:julius/t3code.git"]); yield* git(cwd, ["remote", "add", "upstream", "git@github.com:T3Tools/t3code.git"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const identity = yield* resolver.resolve(cwd); expect(identity).not.toBeNull(); expect(identity?.locator.remoteName).toBe("upstream"); expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code"); expect(identity?.displayName).toBe("t3tools/t3code"); }).pipe(Effect.provide(RepositoryIdentityResolver.layer)), ); it.effect("uses the last remote path segment as the repository name for nested groups", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-nested-group-test-", }); yield* git(cwd, ["init"]); yield* git(cwd, ["remote", "add", "origin", "git@gitlab.com:T3Tools/platform/t3code.git"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const identity = yield* resolver.resolve(cwd); expect(identity).not.toBeNull(); expect(identity?.canonicalKey).toBe("gitlab.com/t3tools/platform/t3code"); expect(identity?.displayName).toBe("t3tools/platform/t3code"); expect(identity?.owner).toBe("t3tools"); expect(identity?.name).toBe("t3code"); }).pipe(Effect.provide(RepositoryIdentityResolver.layer)), ); it.effect( "keeps null identities cached across repeated resolves until the negative TTL expires", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-late-remote-test-", }); yield* git(cwd, ["init"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const initialIdentity = yield* resolver.resolve(cwd); expect(initialIdentity).toBeNull(); yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]); for (const _attempt of [1, 2, 3]) { const cachedIdentity = yield* resolver.resolve(cwd); expect(cachedIdentity).toBeNull(); } yield* TestClock.adjust(Duration.millis(120)); const refreshedIdentity = yield* resolver.resolve(cwd); expect(refreshedIdentity).not.toBeNull(); expect(refreshedIdentity?.canonicalKey).toBe("github.com/t3tools/t3code"); expect(refreshedIdentity?.name).toBe("t3code"); }).pipe( Effect.provide( Layer.merge( TestClock.layer(), makeRepositoryIdentityResolverTestLayer({ negativeCacheTtl: Duration.millis(50), positiveCacheTtl: Duration.seconds(1), }), ), ), ), ); it.effect("refreshes cached identities after the positive TTL when a remote changes", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-repository-identity-remote-change-test-", }); yield* git(cwd, ["init"]); yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]); const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; const initialIdentity = yield* resolver.resolve(cwd); expect(initialIdentity).not.toBeNull(); expect(initialIdentity?.canonicalKey).toBe("github.com/t3tools/t3code"); yield* git(cwd, ["remote", "set-url", "origin", "git@github.com:T3Tools/t3code-next.git"]); const cachedIdentity = yield* resolver.resolve(cwd); expect(cachedIdentity).not.toBeNull(); expect(cachedIdentity?.canonicalKey).toBe("github.com/t3tools/t3code"); yield* TestClock.adjust(Duration.millis(180)); const refreshedIdentity = yield* resolver.resolve(cwd); expect(refreshedIdentity).not.toBeNull(); expect(refreshedIdentity?.canonicalKey).toBe("github.com/t3tools/t3code-next"); expect(refreshedIdentity?.displayName).toBe("t3tools/t3code-next"); expect(refreshedIdentity?.name).toBe("t3code-next"); }).pipe( Effect.provide( Layer.merge( TestClock.layer(), makeRepositoryIdentityResolverTestLayer({ negativeCacheTtl: Duration.millis(50), positiveCacheTtl: Duration.millis(100), }), ), ), ), );});
// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.
import * as NodeFS from "node:fs";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { expect, it } from "@effect/vitest";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
import { TestClock } from "effect/testing";
import * as ProcessRunner from "../processRunner.ts";
import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts";
const normalizePathSeparators = (value: string) => value.replaceAll("\\", "/");
const normalizeResolvedPath = (value: string) => normalizePathSeparators(value);
const git = (cwd: string, args: ReadonlyArray<string>) =>
Effect.gen(function* () {
const processRunner = yield* ProcessRunner.ProcessRunner;
return yield* processRunner.run({
command: "git",
args: ["-C", cwd, ...args],
});
}).pipe(Effect.provide(ProcessRunner.layer));
const makeRepositoryIdentityResolverTestLayer = (options: {
readonly positiveCacheTtl?: Duration.Input;
readonly negativeCacheTtl?: Duration.Input;
}) =>
Layer.effect(
RepositoryIdentityResolver.RepositoryIdentityResolver,
RepositoryIdentityResolver.make({
cacheCapacity: 16,
...options,
}),
).pipe(Layer.provide(ProcessRunner.layer));
it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
it.effect("reuses the cached Git root for repeated workspace lookups", () => {
const calls: Array<ReadonlyArray<string>> = [];
const processRunner = Layer.succeed(ProcessRunner.ProcessRunner, {
run: (input) =>
Effect.sync(() => {
calls.push(input.args);
return {
stdout: input.args.includes("rev-parse")
? "/repo\n"
: "origin\tgit@github.com:T3Tools/t3code.git (fetch)\n",
stderr: "",
code: ChildProcessSpawner.ExitCode(0),
timedOut: false,
stdoutTruncated: false,
stderrTruncated: false,
stdoutInvalidUtf8: false,
stderrInvalidUtf8: false,
};
}),
});
const resolverLayer = Layer.effect(
RepositoryIdentityResolver.RepositoryIdentityResolver,
RepositoryIdentityResolver.make(),
).pipe(Layer.provide(processRunner));
return Effect.gen(function* () {
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const first = yield* resolver.resolve("/repo/packages/web");
const second = yield* resolver.resolve("/repo/packages/web");
expect(first?.canonicalKey).toBe("github.com/t3tools/t3code");
expect(second).toEqual(first);
expect(calls).toEqual([
["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"],
["-C", "/repo", "remote", "-v"],
]);
}).pipe(Effect.provide(resolverLayer));
});
it.effect("retries Git root discovery after a failed lookup", () => {
const calls: Array<ReadonlyArray<string>> = [];
let rootAttempts = 0;
const processRunner = Layer.succeed(ProcessRunner.ProcessRunner, {
run: (input) =>
Effect.sync(() => {
calls.push(input.args);
const rootLookup = input.args.includes("rev-parse");
const failed = rootLookup && rootAttempts++ === 0;
return {
stdout: rootLookup
? failed
? ""
: "/repo\n"
: "origin\tgit@github.com:T3Tools/t3code.git (fetch)\n",
stderr: failed ? "temporary Git failure" : "",
code: ChildProcessSpawner.ExitCode(failed ? 1 : 0),
timedOut: false,
stdoutTruncated: false,
stderrTruncated: false,
stdoutInvalidUtf8: false,
stderrInvalidUtf8: false,
};
}),
});
const resolverLayer = Layer.effect(
RepositoryIdentityResolver.RepositoryIdentityResolver,
RepositoryIdentityResolver.make(),
).pipe(Layer.provide(processRunner));
return Effect.gen(function* () {
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
expect(yield* resolver.resolve("/repo/packages/web")).toBeNull();
const recovered = yield* resolver.resolve("/repo/packages/web");
expect(recovered?.rootPath).toBe("/repo");
expect(calls).toEqual([
["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"],
["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"],
["-C", "/repo", "remote", "-v"],
]);
}).pipe(Effect.provide(resolverLayer));
});
it.effect("normalizes equivalent GitHub remotes into a stable repository identity", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-test-",
});
yield* git(cwd, ["init"]);
yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const identity = yield* resolver.resolve(cwd);
// Native realpath, since git reports the long form of a directory the
// temp dir may name by its 8.3 short form on Windows.
const resolvedIdentityRoot =
identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath);
const resolvedCwd = NodeFS.realpathSync.native(cwd);
expect(identity).not.toBeNull();
expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
expect(normalizeResolvedPath(resolvedIdentityRoot)).toBe(normalizeResolvedPath(resolvedCwd));
expect(identity?.displayName).toBe("t3tools/t3code");
expect(identity?.provider).toBe("github");
expect(identity?.owner).toBe("t3tools");
expect(identity?.name).toBe("t3code");
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
);
it.effect("returns the git top-level root path when resolving from a nested workspace", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const repoRoot = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-nested-root-test-",
});
const nestedWorkspace = path.join(repoRoot, "packages", "web");
yield* fileSystem.makeDirectory(nestedWorkspace, { recursive: true });
yield* git(repoRoot, ["init"]);
yield* git(repoRoot, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const identity = yield* resolver.resolve(nestedWorkspace);
const resolvedIdentityRoot =
identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath);
const resolvedRepoRoot = NodeFS.realpathSync.native(repoRoot);
expect(identity).not.toBeNull();
expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
expect(normalizeResolvedPath(resolvedIdentityRoot)).toBe(
normalizeResolvedPath(resolvedRepoRoot),
);
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
);
it.effect("returns null for non-git folders and repos without remotes", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const nonGitDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-non-git-",
});
const gitDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-no-remote-",
});
yield* git(gitDir, ["init"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const nonGitIdentity = yield* resolver.resolve(nonGitDir);
const noRemoteIdentity = yield* resolver.resolve(gitDir);
expect(nonGitIdentity).toBeNull();
expect(noRemoteIdentity).toBeNull();
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
);
it.effect("prefers upstream over origin when both remotes are configured", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-upstream-test-",
});
yield* git(cwd, ["init"]);
yield* git(cwd, ["remote", "add", "origin", "git@github.com:julius/t3code.git"]);
yield* git(cwd, ["remote", "add", "upstream", "git@github.com:T3Tools/t3code.git"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const identity = yield* resolver.resolve(cwd);
expect(identity).not.toBeNull();
expect(identity?.locator.remoteName).toBe("upstream");
expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
expect(identity?.displayName).toBe("t3tools/t3code");
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
);
it.effect("uses the last remote path segment as the repository name for nested groups", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-nested-group-test-",
});
yield* git(cwd, ["init"]);
yield* git(cwd, ["remote", "add", "origin", "git@gitlab.com:T3Tools/platform/t3code.git"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const identity = yield* resolver.resolve(cwd);
expect(identity).not.toBeNull();
expect(identity?.canonicalKey).toBe("gitlab.com/t3tools/platform/t3code");
expect(identity?.displayName).toBe("t3tools/platform/t3code");
expect(identity?.owner).toBe("t3tools");
expect(identity?.name).toBe("t3code");
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
);
it.effect(
"keeps null identities cached across repeated resolves until the negative TTL expires",
() =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-late-remote-test-",
});
yield* git(cwd, ["init"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const initialIdentity = yield* resolver.resolve(cwd);
expect(initialIdentity).toBeNull();
yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]);
for (const _attempt of [1, 2, 3]) {
const cachedIdentity = yield* resolver.resolve(cwd);
expect(cachedIdentity).toBeNull();
}
yield* TestClock.adjust(Duration.millis(120));
const refreshedIdentity = yield* resolver.resolve(cwd);
expect(refreshedIdentity).not.toBeNull();
expect(refreshedIdentity?.canonicalKey).toBe("github.com/t3tools/t3code");
expect(refreshedIdentity?.name).toBe("t3code");
}).pipe(
Effect.provide(
Layer.merge(
TestClock.layer(),
makeRepositoryIdentityResolverTestLayer({
negativeCacheTtl: Duration.millis(50),
positiveCacheTtl: Duration.seconds(1),
}),
),
),
),
);
it.effect("refreshes cached identities after the positive TTL when a remote changes", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-repository-identity-remote-change-test-",
});
yield* git(cwd, ["init"]);
yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]);
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const initialIdentity = yield* resolver.resolve(cwd);
expect(initialIdentity).not.toBeNull();
expect(initialIdentity?.canonicalKey).toBe("github.com/t3tools/t3code");
yield* git(cwd, ["remote", "set-url", "origin", "git@github.com:T3Tools/t3code-next.git"]);
const cachedIdentity = yield* resolver.resolve(cwd);
expect(cachedIdentity).not.toBeNull();
expect(cachedIdentity?.canonicalKey).toBe("github.com/t3tools/t3code");
yield* TestClock.adjust(Duration.millis(180));
const refreshedIdentity = yield* resolver.resolve(cwd);
expect(refreshedIdentity).not.toBeNull();
expect(refreshedIdentity?.canonicalKey).toBe("github.com/t3tools/t3code-next");
expect(refreshedIdentity?.displayName).toBe("t3tools/t3code-next");
expect(refreshedIdentity?.name).toBe("t3code-next");
}).pipe(
Effect.provide(
Layer.merge(
TestClock.layer(),
makeRepositoryIdentityResolverTestLayer({
negativeCacheTtl: Duration.millis(50),
positiveCacheTtl: Duration.millis(100),
}),
),
),
),
);
});