@esaio/esa-mcp-server 0.2.0 → 0.2.2

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.
Files changed (78) hide show
  1. package/README.en.md +10 -6
  2. package/README.md +16 -12
  3. package/bin/{index.js → index.mjs} +1 -1
  4. package/package.json +22 -3
  5. package/.dockerignore +0 -36
  6. package/.github/dependabot.yml +0 -23
  7. package/.github/workflows/docker-publish.yml +0 -120
  8. package/.github/workflows/main.yml +0 -41
  9. package/CLAUDE.md +0 -94
  10. package/Dockerfile +0 -34
  11. package/biome.json +0 -57
  12. package/src/__tests__/fixtures/mock-comment.ts +0 -90
  13. package/src/__tests__/fixtures/mock-post.ts +0 -79
  14. package/src/__tests__/index.test.ts +0 -216
  15. package/src/api_client/__tests__/index.test.ts +0 -149
  16. package/src/api_client/__tests__/middleware.test.ts +0 -120
  17. package/src/api_client/__tests__/with-context.test.ts +0 -98
  18. package/src/api_client/index.ts +0 -29
  19. package/src/api_client/middleware.ts +0 -21
  20. package/src/api_client/with-context.ts +0 -26
  21. package/src/config/__tests__/index.test.ts +0 -65
  22. package/src/config/index.ts +0 -20
  23. package/src/context/mcp-context.ts +0 -1
  24. package/src/context/stdio-context.ts +0 -6
  25. package/src/errors/missing-team-name-error.ts +0 -8
  26. package/src/formatters/__tests__/mcp-response.test.ts +0 -106
  27. package/src/formatters/mcp-response.ts +0 -95
  28. package/src/generated/api-types.ts +0 -2968
  29. package/src/i18n/__tests__/index.test.ts +0 -53
  30. package/src/i18n/index.ts +0 -39
  31. package/src/index.ts +0 -47
  32. package/src/locales/en.json +0 -13
  33. package/src/locales/ja.json +0 -13
  34. package/src/prompts/__tests__/index.test.ts +0 -48
  35. package/src/prompts/__tests__/summarize-post.test.ts +0 -291
  36. package/src/prompts/index.ts +0 -21
  37. package/src/prompts/summarize-post.ts +0 -94
  38. package/src/resources/__tests__/index.test.ts +0 -50
  39. package/src/resources/__tests__/recent-posts-list.test.ts +0 -92
  40. package/src/resources/__tests__/recent-posts.test.ts +0 -270
  41. package/src/resources/index.ts +0 -33
  42. package/src/resources/recent-posts-list.ts +0 -22
  43. package/src/resources/recent-posts.ts +0 -45
  44. package/src/schemas/team-name-schema.ts +0 -19
  45. package/src/tools/__tests__/attachments.test.ts +0 -460
  46. package/src/tools/__tests__/categories.test.ts +0 -402
  47. package/src/tools/__tests__/comments.test.ts +0 -970
  48. package/src/tools/__tests__/helps.test.ts +0 -222
  49. package/src/tools/__tests__/index.test.ts +0 -48
  50. package/src/tools/__tests__/post-actions.test.ts +0 -445
  51. package/src/tools/__tests__/posts.test.ts +0 -917
  52. package/src/tools/__tests__/search.test.ts +0 -339
  53. package/src/tools/__tests__/teams.test.ts +0 -615
  54. package/src/tools/attachments.ts +0 -167
  55. package/src/tools/categories.ts +0 -153
  56. package/src/tools/comments.ts +0 -258
  57. package/src/tools/helps.ts +0 -50
  58. package/src/tools/index.ts +0 -351
  59. package/src/tools/post-actions.ts +0 -132
  60. package/src/tools/posts.ts +0 -179
  61. package/src/tools/search.ts +0 -98
  62. package/src/tools/teams.ts +0 -157
  63. package/src/transformers/__tests__/category-transformer.test.ts +0 -161
  64. package/src/transformers/__tests__/comment-transformer.test.ts +0 -129
  65. package/src/transformers/__tests__/post-name-normalizer.test.ts +0 -53
  66. package/src/transformers/__tests__/post-transformer.test.ts +0 -70
  67. package/src/transformers/__tests__/query-normalizer.test.ts +0 -98
  68. package/src/transformers/__tests__/team-name-normalizer.test.ts +0 -21
  69. package/src/transformers/category-transformer.ts +0 -36
  70. package/src/transformers/comment-transformer.ts +0 -34
  71. package/src/transformers/post-name-normalizer.ts +0 -30
  72. package/src/transformers/post-transformer.ts +0 -38
  73. package/src/transformers/query-normalizer.ts +0 -36
  74. package/src/transformers/team-name-normalizer.ts +0 -7
  75. package/tsconfig.build.json +0 -4
  76. package/tsconfig.json +0 -30
  77. package/tsdown.config.ts +0 -13
  78. package/vitest.config.ts +0 -24
@@ -1,98 +0,0 @@
1
- import { z } from "zod";
2
- import type { createEsaClient } from "../api_client/index.js";
3
- import { MissingTeamNameError } from "../errors/missing-team-name-error.js";
4
- import {
5
- formatToolError,
6
- formatToolResponse,
7
- } from "../formatters/mcp-response.js";
8
- import type { components } from "../generated/api-types.js";
9
- import { createSchemaWithTeamName } from "../schemas/team-name-schema.js";
10
- import { transformPost } from "../transformers/post-transformer.js";
11
- import { normalizeSearchQuery } from "../transformers/query-normalizer.js";
12
-
13
- export const searchPostsSchema = createSchemaWithTeamName({
14
- query: z
15
- .string()
16
- .describe(`Search query string. Use specific terms, not wildcards like "*". Empty string returns all posts.
17
- ## Important Note for Date Queries:
18
- **WARNING: Do NOT use 'after:', 'before:', 'since:', or 'until:' syntax (these are from GitHub/Gmail/pplog).
19
- Use esa-specific date syntax: created:>YYYY-MM-DD, created:<YYYY-MM-DD, updated:>YYYY-MM-DD, updated:<YYYY-MM-DD
20
-
21
- ## Important Note for Relative Date Queries:
22
- **CRITICAL: Always get today's actual date from the system before processing
23
- relative date queries (e.g., "today", "yesterday", "last week", "recent").
24
- When searching, apply these strategies:
25
- 1. Convert concepts to technical terms (e.g., general descriptions → specific property names, method names, or technical keywords)
26
- 2. Translate between Japanese and English technical terms (e.g., Japanese concepts → English API/property names)
27
- 3. Expand to related technical elements (e.g., one concept → multiple implementation approaches, related technologies, or alternative solutions)
28
- IMPORTANT: Space-separated terms are treated as AND conditions. Use "OR" operator for alternative terms: "word-break OR word-wrap OR overflow-wrap".
29
- Advanced search: "tag:release", "category:dev", "wip:false", "keyword:API", "title:設計書".
30
- Category search: "on:category" (posts directly in category), "in:category" (posts in category and subcategories), "on:/" (uncategorized posts).
31
- For broader results, use OR between related terms rather than listing them with spaces.`)
32
- .transform(normalizeSearchQuery),
33
- sort: z
34
- .enum([
35
- "updated",
36
- "created",
37
- "number",
38
- "stars",
39
- "watches",
40
- "comments",
41
- "best_match",
42
- ])
43
- .optional()
44
- .describe("Sort key"),
45
- order: z.enum(["desc", "asc"]).optional().describe("Sort direction"),
46
- page: z.number().int().positive().optional().describe("Page number"),
47
- perPage: z
48
- .number()
49
- .int()
50
- .min(1)
51
- .max(100)
52
- .optional()
53
- .describe("Items per page"),
54
- include: z
55
- .enum(["comments"])
56
- .optional()
57
- .describe("Specify 'comments' to include comments in the response"),
58
- });
59
-
60
- export async function searchPosts(
61
- client: ReturnType<typeof createEsaClient>,
62
- args: z.infer<typeof searchPostsSchema>,
63
- ) {
64
- try {
65
- if (!args.teamName) {
66
- throw new MissingTeamNameError();
67
- }
68
-
69
- const { data, error, response } = await client.GET(
70
- "/v1/teams/{team_name}/posts",
71
- {
72
- params: {
73
- path: { team_name: args.teamName },
74
- query: {
75
- q: args.query,
76
- sort: args.sort,
77
- order: args.order,
78
- page: args.page,
79
- per_page: args.perPage,
80
- include: args.include,
81
- },
82
- },
83
- },
84
- );
85
-
86
- if (error || !response.ok) {
87
- return formatToolError(error || response.status);
88
- }
89
- const posts: components["schemas"]["Post"][] = data.posts;
90
- const transformed = posts.map((post) =>
91
- transformPost(post, { truncateBody: 500 }),
92
- );
93
-
94
- return formatToolResponse(transformed);
95
- } catch (error) {
96
- return formatToolError(error);
97
- }
98
- }
@@ -1,157 +0,0 @@
1
- import { z } from "zod";
2
- import type { createEsaClient } from "../api_client/index.js";
3
- import { MissingTeamNameError } from "../errors/missing-team-name-error.js";
4
- import {
5
- formatToolError,
6
- formatToolResponse,
7
- } from "../formatters/mcp-response.js";
8
- import type { components } from "../generated/api-types.js";
9
- import { createSchemaWithTeamName } from "../schemas/team-name-schema.js";
10
-
11
- export const getTeamsSchema = z.object({
12
- page: z.number().optional().describe("Page number (starts from 1)"),
13
- perPage: z.number().optional().describe("Number of items per page"),
14
- role: z.enum(["member", "owner"]).optional().describe("Filter by role"),
15
- });
16
-
17
- export async function getTeams(
18
- client: ReturnType<typeof createEsaClient>,
19
- args: z.infer<typeof getTeamsSchema> = {},
20
- ) {
21
- try {
22
- const { data, error, response } = await client.GET("/v1/teams", {
23
- params: {
24
- query: {
25
- page: args.page,
26
- per_page: args.perPage,
27
- role: args.role,
28
- },
29
- },
30
- });
31
-
32
- if (error || !response.ok) {
33
- return formatToolError(error || response.status);
34
- }
35
-
36
- const transformed = {
37
- ...data,
38
- teams: data.teams?.map((team: components["schemas"]["Team"]) => ({
39
- url: team.url,
40
- name: team.name,
41
- description: team.description,
42
- })),
43
- };
44
-
45
- return formatToolResponse(transformed);
46
- } catch (error) {
47
- return formatToolError(error);
48
- }
49
- }
50
-
51
- export const getTeamStatsSchema = createSchemaWithTeamName({});
52
-
53
- export async function getTeamStats(
54
- client: ReturnType<typeof createEsaClient>,
55
- args: z.infer<typeof getTeamStatsSchema>,
56
- ) {
57
- try {
58
- if (!args.teamName) {
59
- throw new MissingTeamNameError();
60
- }
61
- const { data, error, response } = await client.GET(
62
- `/v1/teams/{team_name}/stats`,
63
- {
64
- params: {
65
- path: { team_name: args.teamName },
66
- },
67
- },
68
- );
69
-
70
- if (error || !response.ok) {
71
- return formatToolError(error || response.status);
72
- }
73
-
74
- return formatToolResponse(data);
75
- } catch (error) {
76
- return formatToolError(error);
77
- }
78
- }
79
-
80
- export const getTeamTagsSchema = createSchemaWithTeamName({
81
- page: z.number().optional().describe("Page number (starts from 1)"),
82
- perPage: z.number().optional().describe("Number of items per page"),
83
- });
84
-
85
- export async function getTeamTags(
86
- client: ReturnType<typeof createEsaClient>,
87
- args: z.infer<typeof getTeamTagsSchema>,
88
- ) {
89
- try {
90
- if (!args.teamName) {
91
- throw new MissingTeamNameError();
92
- }
93
- const { data, error, response } = await client.GET(
94
- `/v1/teams/{team_name}/tags`,
95
- {
96
- params: {
97
- path: { team_name: args.teamName },
98
- query: {
99
- page: args.page,
100
- per_page: args.perPage,
101
- },
102
- },
103
- },
104
- );
105
-
106
- if (error || !response.ok) {
107
- return formatToolError(error || response.status);
108
- }
109
-
110
- return formatToolResponse(data);
111
- } catch (error) {
112
- return formatToolError(error);
113
- }
114
- }
115
-
116
- export const getTeamMembersSchema = createSchemaWithTeamName({
117
- page: z.number().optional().describe("Page number (starts from 1)"),
118
- perPage: z.number().optional().describe("Number of items per page"),
119
- sort: z
120
- .enum(["posts_count", "joined", "last_accessed"])
121
- .optional()
122
- .describe("Sort criteria"),
123
- order: z.enum(["desc", "asc"]).optional().describe("Sort order"),
124
- });
125
-
126
- export async function getTeamMembers(
127
- client: ReturnType<typeof createEsaClient>,
128
- args: z.infer<typeof getTeamMembersSchema>,
129
- ) {
130
- try {
131
- if (!args.teamName) {
132
- throw new MissingTeamNameError();
133
- }
134
- const { data, error, response } = await client.GET(
135
- `/v1/teams/{team_name}/members`,
136
- {
137
- params: {
138
- path: { team_name: args.teamName },
139
- query: {
140
- page: args.page,
141
- per_page: args.perPage,
142
- sort: args.sort,
143
- order: args.order,
144
- },
145
- },
146
- },
147
- );
148
-
149
- if (error || !response.ok) {
150
- return formatToolError(error || response.status);
151
- }
152
-
153
- return formatToolResponse(data);
154
- } catch (error) {
155
- return formatToolError(error);
156
- }
157
- }
@@ -1,161 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- transformCategory,
4
- transformCategoryList,
5
- } from "../category-transformer.js";
6
-
7
- describe("transformCategory", () => {
8
- it("should transform category with all properties", () => {
9
- const category = {
10
- name: "api",
11
- full_name: "dev/docs/api",
12
- count: 15,
13
- has_child: true,
14
- selected: true,
15
- };
16
-
17
- const result = transformCategory(category);
18
-
19
- expect(result).toEqual({
20
- full_name: "dev/docs/api",
21
- count: 15,
22
- has_child: true,
23
- });
24
- });
25
-
26
- it("should transform category with minimal properties", () => {
27
- const category = {
28
- name: "simple",
29
- count: 5,
30
- };
31
-
32
- const result = transformCategory(category);
33
-
34
- expect(result).toEqual({
35
- full_name: undefined,
36
- count: 5,
37
- has_child: false,
38
- });
39
- });
40
- });
41
-
42
- describe("transformCategoryList", () => {
43
- it("should transform category list with all properties", () => {
44
- const categoryList = {
45
- current_category: "dev/docs",
46
- categories: [
47
- {
48
- name: "api",
49
- full_name: "dev/docs/api",
50
- count: 10,
51
- has_child: true,
52
- },
53
- {
54
- name: "guides",
55
- full_name: "dev/docs/guides",
56
- count: 5,
57
- has_child: false,
58
- },
59
- ],
60
- parent_categories: [
61
- {
62
- current_category: "dev",
63
- categories: [
64
- {
65
- name: "docs",
66
- full_name: "dev/docs",
67
- count: 15,
68
- has_child: true,
69
- },
70
- ],
71
- },
72
- ],
73
- no_category: {
74
- name: "no_category",
75
- count: 3,
76
- },
77
- descendant_posts: true,
78
- total_count: 2,
79
- per_page: 20,
80
- page: 1,
81
- prev_page: null,
82
- next_page: null,
83
- max_per_page: 100,
84
- };
85
-
86
- const result = transformCategoryList(categoryList);
87
-
88
- expect(result).toEqual({
89
- current_category: "dev/docs",
90
- categories: [
91
- {
92
- full_name: "dev/docs/api",
93
- count: 10,
94
- has_child: true,
95
- },
96
- {
97
- full_name: "dev/docs/guides",
98
- count: 5,
99
- has_child: false,
100
- },
101
- ],
102
- parent_categories: [
103
- {
104
- current_category: "dev",
105
- categories: [
106
- {
107
- full_name: "dev/docs",
108
- count: 15,
109
- has_child: true,
110
- },
111
- ],
112
- },
113
- ],
114
- readme: undefined,
115
- no_category: {
116
- full_name: undefined,
117
- count: 3,
118
- has_child: false,
119
- },
120
- descendant_posts: true,
121
- posts: undefined,
122
- total_count: 2,
123
- per_page: 20,
124
- page: 1,
125
- prev_page: null,
126
- next_page: null,
127
- max_per_page: 100,
128
- });
129
- });
130
-
131
- it("should transform category list with minimal properties", () => {
132
- const categoryList = {
133
- current_category: "",
134
- categories: [],
135
- total_count: 0,
136
- per_page: 20,
137
- page: 1,
138
- prev_page: null,
139
- next_page: null,
140
- max_per_page: 100,
141
- };
142
-
143
- const result = transformCategoryList(categoryList);
144
-
145
- expect(result).toEqual({
146
- current_category: "",
147
- categories: [],
148
- parent_categories: undefined,
149
- readme: undefined,
150
- no_category: undefined,
151
- descendant_posts: undefined,
152
- posts: undefined,
153
- total_count: 0,
154
- per_page: 20,
155
- page: 1,
156
- prev_page: null,
157
- next_page: null,
158
- max_per_page: 100,
159
- });
160
- });
161
- });
@@ -1,129 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- createLongContentComment,
4
- createMockComment,
5
- createNullBodyComment,
6
- } from "../../__tests__/fixtures/mock-comment.js";
7
- import { transformComment } from "../comment-transformer.js";
8
-
9
- describe("transformComment", () => {
10
- it("should transform comment data correctly", () => {
11
- const mockComment = createMockComment();
12
- const result = transformComment(mockComment);
13
-
14
- expect(result).toEqual({
15
- id: 123,
16
- post_number: 456,
17
- url: "https://test-team.esa.example.com/posts/456#comment-123",
18
- body_md: "This is a test comment content",
19
- created_at: "2024-01-01T00:00:00+09:00",
20
- updated_at: "2024-01-01T01:00:00+09:00",
21
- created_by: {
22
- name: "Test User",
23
- screen_name: "testuser",
24
- icon: "https://example.com/icon.png",
25
- myself: true,
26
- },
27
- stats: {
28
- stargazers_count: 5,
29
- star: true,
30
- },
31
- stargazers: [
32
- {
33
- created_at: "2024-01-01T02:00:00+09:00",
34
- body: "Great comment!",
35
- user: {
36
- name: "Stargazer User",
37
- screen_name: "stargazer",
38
- icon: "https://example.com/star-icon.png",
39
- myself: false,
40
- },
41
- name: "Stargazer User",
42
- screen_name: "stargazer",
43
- icon: "https://example.com/star-icon.png",
44
- myself: false,
45
- email: "stargazer@example.com",
46
- role: "member",
47
- posts_count: 10,
48
- joined_at: "2024-01-01T00:00:00+09:00",
49
- last_accessed_at: "2024-01-01T02:00:00+09:00",
50
- },
51
- ],
52
- });
53
- });
54
-
55
- it("should handle comment with no stargazers", () => {
56
- const commentWithoutStars = createMockComment({
57
- stargazers_count: 0,
58
- star: false,
59
- stargazers: undefined,
60
- });
61
- const result = transformComment(commentWithoutStars);
62
-
63
- expect(result.stats).toEqual({
64
- stargazers_count: 0,
65
- star: false,
66
- });
67
- expect(result.stargazers).toBe(undefined);
68
- });
69
-
70
- it("should handle comment with empty body_md", () => {
71
- const commentWithEmptyBody = createNullBodyComment();
72
- const result = transformComment(commentWithEmptyBody);
73
-
74
- expect(result.body_md).toBe("");
75
- });
76
-
77
- it("should truncate long body_md when truncateBody option is provided", () => {
78
- const longComment = createLongContentComment(400);
79
-
80
- const result = transformComment(longComment, { truncateBody: 300 });
81
-
82
- expect(result.body_md).toBe(`${"a".repeat(300)}...`);
83
- });
84
-
85
- it("should not truncate short body_md even when truncateBody option is provided", () => {
86
- const shortComment = createMockComment({ body_md: "Short comment" });
87
-
88
- const result = transformComment(shortComment, { truncateBody: 300 });
89
-
90
- expect(result.body_md).toBe("Short comment");
91
- });
92
-
93
- it("should handle comment with empty body_md when truncateBody option is provided", () => {
94
- const commentWithEmptyBody = createNullBodyComment();
95
- const result = transformComment(commentWithEmptyBody, {
96
- truncateBody: 300,
97
- });
98
-
99
- expect(result.body_md).toBe("");
100
- });
101
-
102
- it("should preserve all essential comment metadata", () => {
103
- const mockComment = createMockComment({
104
- id: 999,
105
- post_number: 789,
106
- url: "https://custom-team.esa.example.com/posts/789#comment-999",
107
- created_at: "2024-06-01T12:00:00+09:00",
108
- updated_at: "2024-06-01T13:00:00+09:00",
109
- created_by: {
110
- name: "Custom User",
111
- screen_name: "customuser",
112
- icon: "https://example.com/custom-icon.png",
113
- myself: false,
114
- },
115
- });
116
-
117
- const result = transformComment(mockComment);
118
-
119
- expect(result.id).toBe(999);
120
- expect(result.post_number).toBe(789);
121
- expect(result.url).toBe(
122
- "https://custom-team.esa.example.com/posts/789#comment-999",
123
- );
124
- expect(result.created_at).toBe("2024-06-01T12:00:00+09:00");
125
- expect(result.updated_at).toBe("2024-06-01T13:00:00+09:00");
126
- expect(result.created_by.name).toBe("Custom User");
127
- expect(result.created_by.screen_name).toBe("customuser");
128
- });
129
- });
@@ -1,53 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { normalizePostName } from "../post-name-normalizer.js";
3
-
4
- describe("normalizePostName", () => {
5
- it("should split name with slashes", () => {
6
- expect(normalizePostName("docs/api/authentication")).toEqual({
7
- name: "authentication",
8
- category: "docs/api",
9
- });
10
- });
11
-
12
- it("should not split when category is specified", () => {
13
- expect(normalizePostName("docs/guide", "custom")).toEqual({
14
- name: "docs/guide",
15
- category: "custom",
16
- });
17
- });
18
-
19
- it("should preserve empty string category", () => {
20
- expect(normalizePostName("docs/guide", "")).toEqual({
21
- name: "docs/guide",
22
- category: "",
23
- });
24
- });
25
-
26
- it("should handle name without slashes", () => {
27
- expect(normalizePostName("simple")).toEqual({
28
- name: "simple",
29
- category: undefined,
30
- });
31
- });
32
-
33
- it("should handle trailing slash as undefined name", () => {
34
- expect(normalizePostName("folder/")).toEqual({
35
- name: undefined,
36
- category: "folder",
37
- });
38
- });
39
-
40
- it("should handle leading slash", () => {
41
- expect(normalizePostName("/guide")).toEqual({
42
- name: "guide",
43
- category: "",
44
- });
45
- });
46
-
47
- it("should handle undefined name", () => {
48
- expect(normalizePostName(undefined, "category")).toEqual({
49
- name: undefined,
50
- category: "category",
51
- });
52
- });
53
- });
@@ -1,70 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- createLongContentPost,
4
- createMockPost,
5
- createNullBodyPost,
6
- } from "../../__tests__/fixtures/mock-post.js";
7
- import { transformPost } from "../post-transformer.js";
8
-
9
- describe("transformPost", () => {
10
- it("should transform post data correctly", () => {
11
- const mockPost = createMockPost();
12
- const result = transformPost(mockPost);
13
-
14
- expect(result).toEqual({
15
- url: mockPost.url,
16
- wip: "Shipped",
17
- kind: "stock",
18
- category_and_title_and_tags: mockPost.full_name,
19
- body_md: mockPost.body_md,
20
- created_at: mockPost.created_at,
21
- updated_at: mockPost.updated_at,
22
- created_by: mockPost.created_by,
23
- updated_by: mockPost.updated_by,
24
- stats: {
25
- tasks_count: 3,
26
- done_tasks_count: 2,
27
- comments_count: 5,
28
- stargazers_count: 10,
29
- watchers_count: 8,
30
- },
31
- });
32
- });
33
-
34
- it("should transform WIP post correctly", () => {
35
- const wipPost = createMockPost({ wip: true });
36
- const result = transformPost(wipPost);
37
-
38
- expect(result.wip).toBe("WIP");
39
- });
40
-
41
- it("should handle post with undefined body_md", () => {
42
- const postWithUndefinedBody = createNullBodyPost();
43
- const result = transformPost(postWithUndefinedBody);
44
-
45
- expect(result.body_md).toBe(undefined);
46
- });
47
-
48
- it("should truncate long body_md when truncateBody option is provided", () => {
49
- const longPost = createLongContentPost(600);
50
-
51
- const result = transformPost(longPost, { truncateBody: 500 });
52
-
53
- expect(result.body_md).toBe(`${"a".repeat(500)}...`);
54
- });
55
-
56
- it("should not truncate short body_md even when truncateBody option is provided", () => {
57
- const shortPost = createMockPost({ body_md: "Short content" });
58
-
59
- const result = transformPost(shortPost, { truncateBody: 500 });
60
-
61
- expect(result.body_md).toBe("Short content");
62
- });
63
-
64
- it("should handle post with undefined body_md when truncateBody option is provided", () => {
65
- const postWithUndefinedBody = createNullBodyPost();
66
- const result = transformPost(postWithUndefinedBody, { truncateBody: 500 });
67
-
68
- expect(result.body_md).toBe(undefined);
69
- });
70
- });