-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgit-push-tool.test.ts
More file actions
157 lines (126 loc) · 5.63 KB
/
git-push-tool.test.ts
File metadata and controls
157 lines (126 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* Integration tests for git_push.
*/
import { afterEach, describe, expect, test } from "bun:test";
import { writeFileSync } from "node:fs";
import { join } from "node:path";
import { registerGitPushTool } from "./git-push-tool.js";
import {
captureTool,
cleanupTmpPaths,
gitCmd,
makeRepoWithUpstream,
mkTmpDir,
writeTestGitConfig,
} from "./test-harness.js";
afterEach(cleanupTmpPaths);
function makeRepoWithRemote(): { dir: string; remote: string } {
// Use shared builder then add extra seed file (diverges from standard)
const { work: dir, remote } = makeRepoWithUpstream("mcp-git-push-test-", "mcp-git-push-remote-");
writeFileSync(join(dir, "base.ts"), "const b = 0;\n");
gitCmd(dir, "add", "base.ts");
gitCmd(dir, "commit", "-m", "chore: base");
gitCmd(dir, "push", "origin", "main");
return { dir, remote };
}
describe("git_push", () => {
test("pushes to configured upstream (json)", async () => {
const { dir } = makeRepoWithRemote();
writeFileSync(join(dir, "new.ts"), "export const n = 1;\n");
gitCmd(dir, "add", "new.ts");
gitCmd(dir, "commit", "-m", "feat: new");
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, format: "json" });
const parsed = JSON.parse(text) as { ok: boolean; branch: string; upstream: string };
expect(parsed.ok).toBe(true);
expect(parsed.branch).toBe("main");
expect(parsed.upstream).toContain("origin");
});
test("pushes to configured upstream (markdown)", async () => {
const { dir } = makeRepoWithRemote();
writeFileSync(join(dir, "new2.ts"), "export const n2 = 2;\n");
gitCmd(dir, "add", "new2.ts");
gitCmd(dir, "commit", "-m", "feat: new2");
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir });
expect(text).toContain("# Push");
expect(text).toContain("main");
expect(text).toContain("→");
});
test("setUpstream=true sets tracking on a branch with no upstream (json)", async () => {
const dir = mkTmpDir("mcp-git-push-set-upstream-");
const remote = mkTmpDir("mcp-git-push-set-upstream-remote-");
gitCmd(dir, "init", "-b", "feature/new");
writeTestGitConfig(dir);
writeFileSync(join(dir, "base.ts"), "const b = 0;\n");
gitCmd(dir, "add", "base.ts");
gitCmd(dir, "commit", "-m", "chore: base");
gitCmd(remote, "init", "--bare", "-b", "feature/new");
gitCmd(dir, "remote", "add", "origin", remote);
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, setUpstream: true, format: "json" });
const parsed = JSON.parse(text) as { ok: boolean; setUpstream?: boolean };
expect(parsed.ok).toBe(true);
expect(parsed.setUpstream).toBe(true);
});
test("explicit remote overrides inferred remote", async () => {
const { dir, remote } = makeRepoWithRemote();
// Add a second remote pointing to the same bare repo
gitCmd(dir, "remote", "add", "mirror", remote);
writeFileSync(join(dir, "c.ts"), "export const c = 3;\n");
gitCmd(dir, "add", "c.ts");
gitCmd(dir, "commit", "-m", "feat: c");
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, remote: "mirror", format: "json" });
const parsed = JSON.parse(text) as { ok: boolean; remote: string };
expect(parsed.ok).toBe(true);
expect(parsed.remote).toBe("mirror");
});
test("push_no_upstream when no tracking configured", async () => {
const dir = mkTmpDir("mcp-git-push-no-upstream-");
gitCmd(dir, "init", "-b", "main");
writeTestGitConfig(dir);
writeFileSync(join(dir, "base.ts"), "const b = 0;\n");
gitCmd(dir, "add", "base.ts");
gitCmd(dir, "commit", "-m", "chore: base");
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, format: "json" });
const parsed = JSON.parse(text) as { error: string };
expect(parsed.error).toBe("push_no_upstream");
});
test("push_detached_head when HEAD is detached", async () => {
const dir = mkTmpDir("mcp-git-push-detached-");
gitCmd(dir, "init", "-b", "main");
writeTestGitConfig(dir);
writeFileSync(join(dir, "base.ts"), "const b = 0;\n");
gitCmd(dir, "add", "base.ts");
gitCmd(dir, "commit", "-m", "chore: base");
const sha = gitCmd(dir, "rev-parse", "HEAD").trim();
gitCmd(dir, "checkout", sha);
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, format: "json" });
const parsed = JSON.parse(text) as { error: string };
expect(parsed.error).toBe("push_detached_head");
});
test("unsafe_ref_token for a bad branch argument", async () => {
const { dir } = makeRepoWithRemote();
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, branch: "bad;branch", format: "json" });
const parsed = JSON.parse(text) as { error: string };
expect(parsed.error).toBe("unsafe_ref_token");
});
test("unsafe_remote_token for a bad remote argument", async () => {
const { dir } = makeRepoWithRemote();
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, remote: "bad;remote", format: "json" });
const parsed = JSON.parse(text) as { error: string };
expect(parsed.error).toBe("unsafe_remote_token");
});
test("not_a_git_repository for a plain directory", async () => {
const dir = mkTmpDir("mcp-nongit-");
const run = captureTool(registerGitPushTool);
const text = await run({ workspaceRoot: dir, format: "json" });
const parsed = JSON.parse(text) as { error: string };
expect(parsed.error).toBe("not_a_git_repository");
});
});