@@ -1497,6 +1497,115 @@ export default function(api) { api.registerTool({ name: "test", description: "te
|
||||
expect(runCommandSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should batch npm updates per scope and run git updates in parallel while skipping pinned and current packages", async () => {
|
||||
vi.spyOn(packageManager as any, "getGlobalNpmRoot").mockReturnValue(join(agentDir, "node_modules"));
|
||||
|
||||
const userOldPath = join(agentDir, "node_modules", "user-old");
|
||||
const userCurrentPath = join(agentDir, "node_modules", "user-current");
|
||||
const userUnknownPath = join(agentDir, "node_modules", "user-unknown");
|
||||
const projectOldPath = join(tempDir, ".pi", "npm", "node_modules", "project-old");
|
||||
const projectCurrentPath = join(tempDir, ".pi", "npm", "node_modules", "project-current");
|
||||
const installPaths = [userOldPath, userCurrentPath, userUnknownPath, projectOldPath, projectCurrentPath];
|
||||
for (const installPath of installPaths) {
|
||||
mkdirSync(installPath, { recursive: true });
|
||||
}
|
||||
writeFileSync(join(userOldPath, "package.json"), JSON.stringify({ name: "user-old", version: "1.0.0" }));
|
||||
writeFileSync(
|
||||
join(userCurrentPath, "package.json"),
|
||||
JSON.stringify({ name: "user-current", version: "1.0.0" }),
|
||||
);
|
||||
writeFileSync(
|
||||
join(userUnknownPath, "package.json"),
|
||||
JSON.stringify({ name: "user-unknown", version: "1.0.0" }),
|
||||
);
|
||||
writeFileSync(join(projectOldPath, "package.json"), JSON.stringify({ name: "project-old", version: "1.0.0" }));
|
||||
writeFileSync(
|
||||
join(projectCurrentPath, "package.json"),
|
||||
JSON.stringify({ name: "project-current", version: "1.0.0" }),
|
||||
);
|
||||
|
||||
settingsManager.setPackages([
|
||||
"npm:user-old",
|
||||
"npm:user-current",
|
||||
"npm:user-unknown",
|
||||
"npm:user-pinned@1.0.0",
|
||||
"git:github.com/example/user-repo-a",
|
||||
"git:github.com/example/user-repo-b",
|
||||
"git:github.com/example/user-repo-pinned@v1",
|
||||
]);
|
||||
settingsManager.setProjectPackages([
|
||||
"npm:project-old",
|
||||
"npm:project-current",
|
||||
"npm:project-missing",
|
||||
"git:github.com/example/project-repo-a",
|
||||
]);
|
||||
|
||||
const runCommandCaptureSpy = vi
|
||||
.spyOn(packageManager as any, "runCommandCapture")
|
||||
.mockImplementation(async (...callArgs: unknown[]) => {
|
||||
const [_command, args] = callArgs as [string, string[]];
|
||||
if (args[0] !== "view") {
|
||||
throw new Error(`Unexpected runCommandCapture args: ${args.join(" ")}`);
|
||||
}
|
||||
switch (args[1]) {
|
||||
case "user-old":
|
||||
case "project-old":
|
||||
return '"2.0.0"';
|
||||
case "user-current":
|
||||
case "project-current":
|
||||
return '"1.0.0"';
|
||||
case "user-unknown":
|
||||
throw new Error("registry unavailable");
|
||||
default:
|
||||
throw new Error(`Unexpected package lookup: ${args[1]}`);
|
||||
}
|
||||
});
|
||||
|
||||
let activeNpmUpdates = 0;
|
||||
let maxConcurrentNpmUpdates = 0;
|
||||
const runCommandSpy = vi
|
||||
.spyOn(packageManager as any, "runCommand")
|
||||
.mockImplementation(async (...callArgs: unknown[]) => {
|
||||
const [command, args] = callArgs as [string, string[]];
|
||||
if (command !== "npm") {
|
||||
throw new Error(`Unexpected runCommand call: ${command} ${args.join(" ")}`);
|
||||
}
|
||||
activeNpmUpdates += 1;
|
||||
maxConcurrentNpmUpdates = Math.max(maxConcurrentNpmUpdates, activeNpmUpdates);
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
activeNpmUpdates -= 1;
|
||||
});
|
||||
|
||||
let activeGitUpdates = 0;
|
||||
let maxConcurrentGitUpdates = 0;
|
||||
const updateGitSpy = vi.spyOn(packageManager as any, "updateGit").mockImplementation(async () => {
|
||||
activeGitUpdates += 1;
|
||||
maxConcurrentGitUpdates = Math.max(maxConcurrentGitUpdates, activeGitUpdates);
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
activeGitUpdates -= 1;
|
||||
});
|
||||
|
||||
await packageManager.update();
|
||||
|
||||
expect(runCommandCaptureSpy).toHaveBeenCalledTimes(5);
|
||||
expect(runCommandSpy).toHaveBeenCalledTimes(2);
|
||||
expect(runCommandSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
"npm",
|
||||
["install", "-g", "user-old@latest", "user-unknown@latest"],
|
||||
undefined,
|
||||
);
|
||||
expect(runCommandSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
"npm",
|
||||
["install", "project-old@latest", "project-missing@latest", "--prefix", join(tempDir, ".pi", "npm")],
|
||||
undefined,
|
||||
);
|
||||
expect(updateGitSpy).toHaveBeenCalledTimes(3);
|
||||
expect(maxConcurrentNpmUpdates).toBeGreaterThan(1);
|
||||
expect(maxConcurrentGitUpdates).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it("should suggest npm source prefixes for update lookups", async () => {
|
||||
settingsManager.setProjectPackages(["npm:example"]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user