@esaio/esa-mcp-server 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/bin/index.js +1 -1
- package/package.json +20 -2
- package/.dockerignore +0 -36
- package/.github/dependabot.yml +0 -23
- package/.github/workflows/docker-publish.yml +0 -120
- package/.github/workflows/main.yml +0 -41
- package/CLAUDE.md +0 -94
- package/Dockerfile +0 -34
- package/biome.json +0 -57
- package/src/__tests__/fixtures/mock-comment.ts +0 -90
- package/src/__tests__/fixtures/mock-post.ts +0 -79
- package/src/__tests__/index.test.ts +0 -216
- package/src/api_client/__tests__/index.test.ts +0 -149
- package/src/api_client/__tests__/middleware.test.ts +0 -120
- package/src/api_client/__tests__/with-context.test.ts +0 -98
- package/src/api_client/index.ts +0 -29
- package/src/api_client/middleware.ts +0 -21
- package/src/api_client/with-context.ts +0 -26
- package/src/config/__tests__/index.test.ts +0 -65
- package/src/config/index.ts +0 -20
- package/src/context/mcp-context.ts +0 -1
- package/src/context/stdio-context.ts +0 -6
- package/src/errors/missing-team-name-error.ts +0 -8
- package/src/formatters/__tests__/mcp-response.test.ts +0 -106
- package/src/formatters/mcp-response.ts +0 -95
- package/src/generated/api-types.ts +0 -2968
- package/src/i18n/__tests__/index.test.ts +0 -53
- package/src/i18n/index.ts +0 -39
- package/src/index.ts +0 -47
- package/src/locales/en.json +0 -13
- package/src/locales/ja.json +0 -13
- package/src/prompts/__tests__/index.test.ts +0 -48
- package/src/prompts/__tests__/summarize-post.test.ts +0 -291
- package/src/prompts/index.ts +0 -21
- package/src/prompts/summarize-post.ts +0 -94
- package/src/resources/__tests__/index.test.ts +0 -50
- package/src/resources/__tests__/recent-posts-list.test.ts +0 -92
- package/src/resources/__tests__/recent-posts.test.ts +0 -270
- package/src/resources/index.ts +0 -33
- package/src/resources/recent-posts-list.ts +0 -22
- package/src/resources/recent-posts.ts +0 -45
- package/src/schemas/team-name-schema.ts +0 -19
- package/src/tools/__tests__/attachments.test.ts +0 -460
- package/src/tools/__tests__/categories.test.ts +0 -402
- package/src/tools/__tests__/comments.test.ts +0 -970
- package/src/tools/__tests__/helps.test.ts +0 -222
- package/src/tools/__tests__/index.test.ts +0 -48
- package/src/tools/__tests__/post-actions.test.ts +0 -445
- package/src/tools/__tests__/posts.test.ts +0 -917
- package/src/tools/__tests__/search.test.ts +0 -339
- package/src/tools/__tests__/teams.test.ts +0 -615
- package/src/tools/attachments.ts +0 -167
- package/src/tools/categories.ts +0 -153
- package/src/tools/comments.ts +0 -258
- package/src/tools/helps.ts +0 -50
- package/src/tools/index.ts +0 -351
- package/src/tools/post-actions.ts +0 -132
- package/src/tools/posts.ts +0 -179
- package/src/tools/search.ts +0 -98
- package/src/tools/teams.ts +0 -157
- package/src/transformers/__tests__/category-transformer.test.ts +0 -161
- package/src/transformers/__tests__/comment-transformer.test.ts +0 -129
- package/src/transformers/__tests__/post-name-normalizer.test.ts +0 -53
- package/src/transformers/__tests__/post-transformer.test.ts +0 -70
- package/src/transformers/__tests__/query-normalizer.test.ts +0 -98
- package/src/transformers/__tests__/team-name-normalizer.test.ts +0 -21
- package/src/transformers/category-transformer.ts +0 -36
- package/src/transformers/comment-transformer.ts +0 -34
- package/src/transformers/post-name-normalizer.ts +0 -30
- package/src/transformers/post-transformer.ts +0 -38
- package/src/transformers/query-normalizer.ts +0 -36
- package/src/transformers/team-name-normalizer.ts +0 -7
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -30
- package/tsdown.config.ts +0 -13
- package/vitest.config.ts +0 -24
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { createEsaClient } from "../../api_client/index.js";
|
|
3
|
-
import {
|
|
4
|
-
getMarkdownSyntaxHelp,
|
|
5
|
-
getSearchOptionsHelp,
|
|
6
|
-
HELP_DOCS,
|
|
7
|
-
searchHelp,
|
|
8
|
-
} from "../helps.js";
|
|
9
|
-
import * as posts from "../posts.js";
|
|
10
|
-
import * as search from "../search.js";
|
|
11
|
-
|
|
12
|
-
// Mock the posts and search modules
|
|
13
|
-
vi.mock("../posts.js", () => ({
|
|
14
|
-
getPost: vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
vi.mock("../search.js", async (importOriginal) => {
|
|
18
|
-
const actual = await importOriginal<typeof import("../search.js")>();
|
|
19
|
-
return {
|
|
20
|
-
...actual,
|
|
21
|
-
searchPosts: vi.fn(),
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe("getSearchOptionsHelp", () => {
|
|
26
|
-
const mockClient = {} as ReturnType<typeof createEsaClient>;
|
|
27
|
-
|
|
28
|
-
it("should call getPost with correct parameters for search options documentation", async () => {
|
|
29
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
30
|
-
mockGetPost.mockResolvedValue({
|
|
31
|
-
content: [{ type: "text", text: "mocked response" }],
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const result = await getSearchOptionsHelp(mockClient, {});
|
|
35
|
-
|
|
36
|
-
expect(mockGetPost).toHaveBeenCalledWith(mockClient, {
|
|
37
|
-
teamName: HELP_DOCS.TEAM,
|
|
38
|
-
postNumber: HELP_DOCS.SEARCH_OPTIONS_POST_ID,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(result.content[0].text).toBe("mocked response");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should handle API errors", async () => {
|
|
45
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
46
|
-
mockGetPost.mockResolvedValue({
|
|
47
|
-
content: [{ type: "text", text: "Error: Post not found" }],
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const result = await getSearchOptionsHelp(mockClient, {});
|
|
51
|
-
|
|
52
|
-
expect(result.content[0].text).toContain("Error:");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should handle network errors", async () => {
|
|
56
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
57
|
-
mockGetPost.mockResolvedValue({
|
|
58
|
-
content: [{ type: "text", text: "Error: Network connection failed" }],
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const result = await getSearchOptionsHelp(mockClient, {});
|
|
62
|
-
|
|
63
|
-
expect(result.content[0].text).toContain("Network connection failed");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("should handle non-Error exceptions", async () => {
|
|
67
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
68
|
-
mockGetPost.mockResolvedValue({
|
|
69
|
-
content: [{ type: "text", text: "Error: Unexpected error" }],
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const result = await getSearchOptionsHelp(mockClient, {});
|
|
73
|
-
|
|
74
|
-
expect(result.content[0].text).toContain("Unexpected error");
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("getMarkdownSyntaxHelp", () => {
|
|
79
|
-
const mockClient = {} as ReturnType<typeof createEsaClient>;
|
|
80
|
-
|
|
81
|
-
it("should call getPost with correct parameters for markdown syntax documentation", async () => {
|
|
82
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
83
|
-
mockGetPost.mockResolvedValue({
|
|
84
|
-
content: [{ type: "text", text: "markdown syntax help" }],
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const result = await getMarkdownSyntaxHelp(mockClient, {});
|
|
88
|
-
|
|
89
|
-
expect(mockGetPost).toHaveBeenCalledWith(mockClient, {
|
|
90
|
-
teamName: HELP_DOCS.TEAM,
|
|
91
|
-
postNumber: HELP_DOCS.MARKDOWN_SYNTAX_POST_ID,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
expect(result.content[0].text).toBe("markdown syntax help");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("should handle API errors", async () => {
|
|
98
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
99
|
-
mockGetPost.mockResolvedValue({
|
|
100
|
-
content: [{ type: "text", text: "Error: Access denied" }],
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const result = await getMarkdownSyntaxHelp(mockClient, {});
|
|
104
|
-
|
|
105
|
-
expect(result.content[0].text).toContain("Error:");
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("should handle network errors", async () => {
|
|
109
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
110
|
-
mockGetPost.mockResolvedValue({
|
|
111
|
-
content: [{ type: "text", text: "Error: Network connection failed" }],
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const result = await getMarkdownSyntaxHelp(mockClient, {});
|
|
115
|
-
|
|
116
|
-
expect(result.content[0].text).toContain("Network connection failed");
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("should handle non-Error exceptions", async () => {
|
|
120
|
-
const mockGetPost = vi.mocked(posts.getPost);
|
|
121
|
-
mockGetPost.mockResolvedValue({
|
|
122
|
-
content: [{ type: "text", text: "Error: Unexpected error" }],
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const result = await getMarkdownSyntaxHelp(mockClient, {});
|
|
126
|
-
|
|
127
|
-
expect(result.content[0].text).toContain("Unexpected error");
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("searchHelp", () => {
|
|
132
|
-
const mockClient = {} as ReturnType<typeof createEsaClient>;
|
|
133
|
-
|
|
134
|
-
it("should call searchPosts with docs team and correct parameters", async () => {
|
|
135
|
-
const mockSearchPosts = vi.mocked(search.searchPosts);
|
|
136
|
-
mockSearchPosts.mockResolvedValue({
|
|
137
|
-
content: [{ type: "text", text: "mocked search results" }],
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
await searchHelp(mockClient, {
|
|
141
|
-
query: "webhook API",
|
|
142
|
-
page: 2,
|
|
143
|
-
perPage: 20,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
expect(mockSearchPosts).toHaveBeenCalledWith(mockClient, {
|
|
147
|
-
teamName: HELP_DOCS.TEAM,
|
|
148
|
-
sort: "best_match",
|
|
149
|
-
query: "webhook API",
|
|
150
|
-
page: 2,
|
|
151
|
-
perPage: 20,
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("should call searchPosts with minimal parameters", async () => {
|
|
156
|
-
const mockSearchPosts = vi.mocked(search.searchPosts);
|
|
157
|
-
mockSearchPosts.mockResolvedValue({
|
|
158
|
-
content: [{ type: "text", text: "emoji help results" }],
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const result = await searchHelp(mockClient, {
|
|
162
|
-
query: "emoji reaction",
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
expect(mockSearchPosts).toHaveBeenCalledWith(mockClient, {
|
|
166
|
-
teamName: HELP_DOCS.TEAM,
|
|
167
|
-
sort: "best_match",
|
|
168
|
-
query: "emoji reaction",
|
|
169
|
-
page: undefined,
|
|
170
|
-
perPage: undefined,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
expect(result.content[0].text).toBe("emoji help results");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("should handle API errors", async () => {
|
|
177
|
-
const mockSearchPosts = vi.mocked(search.searchPosts);
|
|
178
|
-
mockSearchPosts.mockResolvedValue({
|
|
179
|
-
content: [{ type: "text", text: "Error: Search failed" }],
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const result = await searchHelp(mockClient, {
|
|
183
|
-
query: "test query",
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
expect(result.content[0].text).toContain("Error:");
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("should handle network errors", async () => {
|
|
190
|
-
const mockSearchPosts = vi.mocked(search.searchPosts);
|
|
191
|
-
mockSearchPosts.mockResolvedValue({
|
|
192
|
-
content: [{ type: "text", text: "Error: Network connection failed" }],
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const result = await searchHelp(mockClient, {
|
|
196
|
-
query: "test query",
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
expect(result.content[0].text).toContain("Network connection failed");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("should handle non-Error exceptions", async () => {
|
|
203
|
-
const mockSearchPosts = vi.mocked(search.searchPosts);
|
|
204
|
-
mockSearchPosts.mockResolvedValue({
|
|
205
|
-
content: [{ type: "text", text: "Error: Unexpected error" }],
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const result = await searchHelp(mockClient, {
|
|
209
|
-
query: "test query",
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
expect(result.content[0].text).toContain("Unexpected error");
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe("HELP_DOCS constants", () => {
|
|
217
|
-
it("should have correct constant values", () => {
|
|
218
|
-
expect(HELP_DOCS.TEAM).toBe("docs");
|
|
219
|
-
expect(HELP_DOCS.SEARCH_OPTIONS_POST_ID).toBe(104);
|
|
220
|
-
expect(HELP_DOCS.MARKDOWN_SYNTAX_POST_ID).toBe(49);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { MockInstance } from "vitest";
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
import type { MCPContext } from "../../context/mcp-context.js";
|
|
5
|
-
import { setupTools } from "../index.js";
|
|
6
|
-
|
|
7
|
-
describe("setupTools", () => {
|
|
8
|
-
let server: McpServer;
|
|
9
|
-
let context: MCPContext;
|
|
10
|
-
let consoleErrorSpy: MockInstance<typeof console.error>;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
server = new McpServer({
|
|
14
|
-
name: "test-server",
|
|
15
|
-
version: "1.0.0",
|
|
16
|
-
});
|
|
17
|
-
context = {} as unknown as MCPContext;
|
|
18
|
-
|
|
19
|
-
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
20
|
-
vi.clearAllMocks();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
afterEach(() => {
|
|
24
|
-
consoleErrorSpy.mockRestore();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should register all 24 tools with correct handlers", () => {
|
|
28
|
-
const registerToolSpy = vi.spyOn(server, "registerTool");
|
|
29
|
-
|
|
30
|
-
setupTools(server, context);
|
|
31
|
-
|
|
32
|
-
expect(registerToolSpy).toHaveBeenCalledTimes(24);
|
|
33
|
-
|
|
34
|
-
for (let i = 0; i < 24; i++) {
|
|
35
|
-
const [toolName, schema, handler] = registerToolSpy.mock.calls[i];
|
|
36
|
-
expect(typeof toolName).toBe("string");
|
|
37
|
-
expect(toolName).toMatch(/^esa_/); // All tools should start with 'esa_'
|
|
38
|
-
expect(schema).toBeTypeOf("object"); // Schema verification handled by individual tool tests
|
|
39
|
-
expect(handler).toBeTypeOf("function"); // Handler functionality tested in specific tool tests
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should log setup completion message", () => {
|
|
44
|
-
setupTools(server, context);
|
|
45
|
-
|
|
46
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith("Setting up MCP tools...");
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { createMockPost } from "../../__tests__/fixtures/mock-post.js";
|
|
3
|
-
import type { createEsaClient } from "../../api_client/index.js";
|
|
4
|
-
import { archivePost, duplicatePost, shipPost } from "../post-actions.js";
|
|
5
|
-
import * as postsModule from "../posts.js";
|
|
6
|
-
|
|
7
|
-
vi.mock("../posts.js", () => ({
|
|
8
|
-
updatePost: vi.fn(),
|
|
9
|
-
createPost: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
describe("archivePost", () => {
|
|
13
|
-
const mockClient = {
|
|
14
|
-
GET: vi.fn(),
|
|
15
|
-
} as unknown as ReturnType<typeof createEsaClient> & {
|
|
16
|
-
GET: ReturnType<typeof vi.fn>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const mockUpdatePost = vi.mocked(postsModule.updatePost);
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
vi.clearAllMocks();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("should call updatePost with Archived category for post without category", async () => {
|
|
26
|
-
const currentPost = createMockPost({
|
|
27
|
-
number: 123,
|
|
28
|
-
category: "",
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
mockClient.GET.mockResolvedValue({
|
|
32
|
-
data: currentPost,
|
|
33
|
-
error: undefined,
|
|
34
|
-
response: {
|
|
35
|
-
ok: true,
|
|
36
|
-
status: 200,
|
|
37
|
-
} as Response,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
mockUpdatePost.mockResolvedValue({
|
|
41
|
-
content: [{ type: "text", text: "Updated post" }],
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
await archivePost(mockClient, {
|
|
45
|
-
teamName: "test-team",
|
|
46
|
-
postNumber: 123,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
expect(mockUpdatePost).toHaveBeenCalledWith(mockClient, {
|
|
50
|
-
teamName: "test-team",
|
|
51
|
-
postNumber: 123,
|
|
52
|
-
category: "Archived",
|
|
53
|
-
message: "Archive post",
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("should call updatePost with Archived/ prefix for post with category", async () => {
|
|
58
|
-
const currentPost = createMockPost({
|
|
59
|
-
number: 123,
|
|
60
|
-
category: "dev/docs",
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
mockClient.GET.mockResolvedValue({
|
|
64
|
-
data: currentPost,
|
|
65
|
-
error: undefined,
|
|
66
|
-
response: {
|
|
67
|
-
ok: true,
|
|
68
|
-
status: 200,
|
|
69
|
-
} as Response,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
mockUpdatePost.mockResolvedValue({
|
|
73
|
-
content: [{ type: "text", text: "Updated post" }],
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
await archivePost(mockClient, {
|
|
77
|
-
teamName: "test-team",
|
|
78
|
-
postNumber: 123,
|
|
79
|
-
message: "Custom archive message",
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
expect(mockUpdatePost).toHaveBeenCalledWith(mockClient, {
|
|
83
|
-
teamName: "test-team",
|
|
84
|
-
postNumber: 123,
|
|
85
|
-
category: "Archived/dev/docs",
|
|
86
|
-
message: "Custom archive message",
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("should return already archived message without calling updatePost", async () => {
|
|
91
|
-
const currentPost = createMockPost({
|
|
92
|
-
number: 123,
|
|
93
|
-
category: "Archived/dev/docs",
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
mockClient.GET.mockResolvedValue({
|
|
97
|
-
data: currentPost,
|
|
98
|
-
error: undefined,
|
|
99
|
-
response: {
|
|
100
|
-
ok: true,
|
|
101
|
-
status: 200,
|
|
102
|
-
} as Response,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const result = await archivePost(mockClient, {
|
|
106
|
-
teamName: "test-team",
|
|
107
|
-
postNumber: 123,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
expect(mockUpdatePost).not.toHaveBeenCalled();
|
|
111
|
-
expect(result.content[0].text).toContain("Post is already archived");
|
|
112
|
-
expect(result.content[0].text).toContain("Archived/dev/docs");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("should handle GET error", async () => {
|
|
116
|
-
const mockError = { error: "not_found", message: "Post not found" };
|
|
117
|
-
|
|
118
|
-
mockClient.GET.mockResolvedValue({
|
|
119
|
-
data: undefined,
|
|
120
|
-
error: mockError,
|
|
121
|
-
response: {
|
|
122
|
-
ok: false,
|
|
123
|
-
status: 404,
|
|
124
|
-
} as Response,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const result = await archivePost(mockClient, {
|
|
128
|
-
teamName: "test-team",
|
|
129
|
-
postNumber: 999,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
expect(result.content[0].text).toContain("not_found");
|
|
133
|
-
expect(mockUpdatePost).not.toHaveBeenCalled();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("should handle network errors", async () => {
|
|
137
|
-
const networkError = new Error("Network connection failed");
|
|
138
|
-
|
|
139
|
-
mockClient.GET.mockRejectedValue(networkError);
|
|
140
|
-
|
|
141
|
-
const result = await archivePost(mockClient, {
|
|
142
|
-
teamName: "test-team",
|
|
143
|
-
postNumber: 123,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
expect(result.content[0].text).toContain("Network connection failed");
|
|
147
|
-
expect(mockUpdatePost).not.toHaveBeenCalled();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("should handle non-Error exceptions", async () => {
|
|
151
|
-
mockClient.GET.mockRejectedValue("Unexpected error");
|
|
152
|
-
|
|
153
|
-
const result = await archivePost(mockClient, {
|
|
154
|
-
teamName: "test-team",
|
|
155
|
-
postNumber: 123,
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
expect(result.content[0].text).toContain("Unexpected error");
|
|
159
|
-
expect(mockUpdatePost).not.toHaveBeenCalled();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("should throw MissingTeamNameError when teamName is empty", async () => {
|
|
163
|
-
const result = await archivePost(mockClient, {
|
|
164
|
-
teamName: "",
|
|
165
|
-
postNumber: 123,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
expect(result).toEqual({
|
|
169
|
-
content: [
|
|
170
|
-
{
|
|
171
|
-
type: "text",
|
|
172
|
-
text: "Error: Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.",
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
expect(mockClient.GET).not.toHaveBeenCalled();
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe("shipPost", () => {
|
|
182
|
-
const mockClient = {} as ReturnType<typeof createEsaClient>;
|
|
183
|
-
const mockUpdatePost = vi.mocked(postsModule.updatePost);
|
|
184
|
-
|
|
185
|
-
beforeEach(() => {
|
|
186
|
-
vi.clearAllMocks();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("should call updatePost with wip: false", async () => {
|
|
190
|
-
mockUpdatePost.mockResolvedValue({
|
|
191
|
-
content: [{ type: "text", text: "Updated post" }],
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
await shipPost(mockClient, {
|
|
195
|
-
teamName: "test-team",
|
|
196
|
-
postNumber: 123,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
expect(mockUpdatePost).toHaveBeenCalledWith(mockClient, {
|
|
200
|
-
teamName: "test-team",
|
|
201
|
-
postNumber: 123,
|
|
202
|
-
wip: false,
|
|
203
|
-
message: "Ship It!",
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("should handle updatePost errors", async () => {
|
|
208
|
-
mockUpdatePost.mockResolvedValue({
|
|
209
|
-
content: [{ type: "text", text: "Error: Post not found" }],
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const result = await shipPost(mockClient, {
|
|
213
|
-
teamName: "test-team",
|
|
214
|
-
postNumber: 999,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
expect(result.content[0].text).toContain("Error:");
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("should handle network errors", async () => {
|
|
221
|
-
const networkError = new Error("Network connection failed");
|
|
222
|
-
mockUpdatePost.mockRejectedValue(networkError);
|
|
223
|
-
|
|
224
|
-
const result = await shipPost(mockClient, {
|
|
225
|
-
teamName: "test-team",
|
|
226
|
-
postNumber: 123,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
expect(result.content[0].text).toContain("Network connection failed");
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("should handle non-Error exceptions", async () => {
|
|
233
|
-
mockUpdatePost.mockRejectedValue("Unexpected error");
|
|
234
|
-
|
|
235
|
-
const result = await shipPost(mockClient, {
|
|
236
|
-
teamName: "test-team",
|
|
237
|
-
postNumber: 123,
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
expect(result.content[0].text).toContain("Unexpected error");
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it("should throw MissingTeamNameError when teamName is empty", async () => {
|
|
244
|
-
const result = await shipPost(mockClient, {
|
|
245
|
-
teamName: "",
|
|
246
|
-
postNumber: 123,
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
expect(result).toEqual({
|
|
250
|
-
content: [
|
|
251
|
-
{
|
|
252
|
-
type: "text",
|
|
253
|
-
text: "Error: Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.",
|
|
254
|
-
},
|
|
255
|
-
],
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
expect(mockUpdatePost).not.toHaveBeenCalled();
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
describe("duplicatePost", () => {
|
|
263
|
-
const mockClient = {
|
|
264
|
-
GET: vi.fn(),
|
|
265
|
-
POST: vi.fn(),
|
|
266
|
-
} as unknown as ReturnType<typeof createEsaClient> & {
|
|
267
|
-
GET: ReturnType<typeof vi.fn>;
|
|
268
|
-
POST: ReturnType<typeof vi.fn>;
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
const mockCreatePost = vi.mocked(postsModule.createPost);
|
|
272
|
-
|
|
273
|
-
beforeEach(() => {
|
|
274
|
-
vi.clearAllMocks();
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("should call createPost with correct parameters", async () => {
|
|
278
|
-
const mockPostNew = {
|
|
279
|
-
name: "docs/api/authentication",
|
|
280
|
-
body_md: "# Authentication\n\nThis is the authentication guide.",
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
mockClient.GET.mockResolvedValue({
|
|
284
|
-
data: { post: mockPostNew },
|
|
285
|
-
error: undefined,
|
|
286
|
-
response: {
|
|
287
|
-
ok: true,
|
|
288
|
-
status: 200,
|
|
289
|
-
} as Response,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
mockCreatePost.mockResolvedValue({
|
|
293
|
-
content: [{ type: "text", text: "Created post" }],
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
await duplicatePost(mockClient, {
|
|
297
|
-
teamName: "test-team",
|
|
298
|
-
postNumber: 123,
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
expect(mockClient.GET).toHaveBeenCalledWith(
|
|
302
|
-
"/v1/teams/{team_name}/posts/new",
|
|
303
|
-
{
|
|
304
|
-
params: {
|
|
305
|
-
path: { team_name: "test-team" },
|
|
306
|
-
query: {
|
|
307
|
-
parent_post_id: 123,
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
expect(mockCreatePost).toHaveBeenCalledWith(mockClient, {
|
|
314
|
-
teamName: "test-team",
|
|
315
|
-
name: "docs/api/authentication",
|
|
316
|
-
bodyMd: "# Authentication\n\nThis is the authentication guide.",
|
|
317
|
-
wip: true,
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
it("should use targetTeamName when provided", async () => {
|
|
322
|
-
const mockPostNew = {
|
|
323
|
-
name: "simple-post",
|
|
324
|
-
body_md: "This is a simple post.",
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
mockClient.GET.mockResolvedValue({
|
|
328
|
-
data: { post: mockPostNew },
|
|
329
|
-
error: undefined,
|
|
330
|
-
response: {
|
|
331
|
-
ok: true,
|
|
332
|
-
status: 200,
|
|
333
|
-
} as Response,
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
mockCreatePost.mockResolvedValue({
|
|
337
|
-
content: [{ type: "text", text: "Created post" }],
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
await duplicatePost(mockClient, {
|
|
341
|
-
teamName: "test-team",
|
|
342
|
-
postNumber: 456,
|
|
343
|
-
targetTeamName: "another-team",
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
expect(mockClient.GET).toHaveBeenCalledWith(
|
|
347
|
-
"/v1/teams/{team_name}/posts/new",
|
|
348
|
-
{
|
|
349
|
-
params: {
|
|
350
|
-
path: { team_name: "test-team" },
|
|
351
|
-
query: {
|
|
352
|
-
parent_post_id: 456,
|
|
353
|
-
},
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
expect(mockCreatePost).toHaveBeenCalledWith(mockClient, {
|
|
359
|
-
teamName: "another-team",
|
|
360
|
-
name: "simple-post",
|
|
361
|
-
bodyMd: "This is a simple post.",
|
|
362
|
-
wip: true,
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it("should handle API error", async () => {
|
|
367
|
-
const mockError = { error: "not_found", message: "Post not found" };
|
|
368
|
-
|
|
369
|
-
mockClient.GET.mockResolvedValue({
|
|
370
|
-
data: undefined,
|
|
371
|
-
error: mockError,
|
|
372
|
-
response: {
|
|
373
|
-
ok: false,
|
|
374
|
-
status: 404,
|
|
375
|
-
} as Response,
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
const result = await duplicatePost(mockClient, {
|
|
379
|
-
teamName: "test-team",
|
|
380
|
-
postNumber: 999,
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
expect(result.content[0].text).toContain("not_found");
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
it("should handle response not ok", async () => {
|
|
387
|
-
mockClient.GET.mockResolvedValue({
|
|
388
|
-
data: undefined,
|
|
389
|
-
error: undefined,
|
|
390
|
-
response: {
|
|
391
|
-
ok: false,
|
|
392
|
-
status: 500,
|
|
393
|
-
} as Response,
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
const result = await duplicatePost(mockClient, {
|
|
397
|
-
teamName: "test-team",
|
|
398
|
-
postNumber: 789,
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
expect(result.content[0].text).toContain("500");
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it("should handle network errors", async () => {
|
|
405
|
-
const networkError = new Error("Network connection failed");
|
|
406
|
-
|
|
407
|
-
mockClient.GET.mockRejectedValue(networkError);
|
|
408
|
-
|
|
409
|
-
const result = await duplicatePost(mockClient, {
|
|
410
|
-
teamName: "test-team",
|
|
411
|
-
postNumber: 123,
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
expect(result.content[0].text).toContain("Network connection failed");
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it("should handle non-Error exceptions", async () => {
|
|
418
|
-
mockClient.GET.mockRejectedValue("Unexpected error");
|
|
419
|
-
|
|
420
|
-
const result = await duplicatePost(mockClient, {
|
|
421
|
-
teamName: "test-team",
|
|
422
|
-
postNumber: 123,
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
expect(result.content[0].text).toContain("Unexpected error");
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it("should throw MissingTeamNameError when teamName is empty", async () => {
|
|
429
|
-
const result = await duplicatePost(mockClient, {
|
|
430
|
-
teamName: "",
|
|
431
|
-
postNumber: 123,
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
expect(result).toEqual({
|
|
435
|
-
content: [
|
|
436
|
-
{
|
|
437
|
-
type: "text",
|
|
438
|
-
text: "Error: Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.",
|
|
439
|
-
},
|
|
440
|
-
],
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
expect(mockClient.GET).not.toHaveBeenCalled();
|
|
444
|
-
});
|
|
445
|
-
});
|