|
1 | | -import { sanitizeToolUseId } from "../tool-id" |
| 1 | +import { sanitizeToolUseId, truncateOpenAiCallId, sanitizeOpenAiCallId, OPENAI_CALL_ID_MAX_LENGTH } from "../tool-id" |
2 | 2 |
|
3 | 3 | describe("sanitizeToolUseId", () => { |
4 | 4 | describe("valid IDs pass through unchanged", () => { |
@@ -69,3 +69,110 @@ describe("sanitizeToolUseId", () => { |
69 | 69 | }) |
70 | 70 | }) |
71 | 71 | }) |
| 72 | + |
| 73 | +describe("truncateOpenAiCallId", () => { |
| 74 | + describe("IDs within limit pass through unchanged", () => { |
| 75 | + it("should preserve short IDs", () => { |
| 76 | + expect(truncateOpenAiCallId("toolu_01AbC")).toBe("toolu_01AbC") |
| 77 | + }) |
| 78 | + |
| 79 | + it("should preserve IDs exactly at the limit", () => { |
| 80 | + const id64Chars = "a".repeat(64) |
| 81 | + expect(truncateOpenAiCallId(id64Chars)).toBe(id64Chars) |
| 82 | + }) |
| 83 | + |
| 84 | + it("should handle empty string", () => { |
| 85 | + expect(truncateOpenAiCallId("")).toBe("") |
| 86 | + }) |
| 87 | + }) |
| 88 | + |
| 89 | + describe("long IDs get truncated with hash suffix", () => { |
| 90 | + it("should truncate IDs longer than 64 characters", () => { |
| 91 | + const longId = "a".repeat(70) // 70 chars, exceeds 64 limit |
| 92 | + const result = truncateOpenAiCallId(longId) |
| 93 | + expect(result.length).toBe(64) |
| 94 | + }) |
| 95 | + |
| 96 | + it("should produce consistent results for the same input", () => { |
| 97 | + const longId = "toolu_mcp--linear--create_issue_12345678-1234-1234-1234-123456789012" |
| 98 | + const result1 = truncateOpenAiCallId(longId) |
| 99 | + const result2 = truncateOpenAiCallId(longId) |
| 100 | + expect(result1).toBe(result2) |
| 101 | + }) |
| 102 | + |
| 103 | + it("should produce different results for different inputs", () => { |
| 104 | + const longId1 = "a".repeat(70) + "_unique1" |
| 105 | + const longId2 = "a".repeat(70) + "_unique2" |
| 106 | + const result1 = truncateOpenAiCallId(longId1) |
| 107 | + const result2 = truncateOpenAiCallId(longId2) |
| 108 | + expect(result1).not.toBe(result2) |
| 109 | + }) |
| 110 | + |
| 111 | + it("should preserve the prefix and add hash suffix", () => { |
| 112 | + const longId = "toolu_mcp--linear--create_issue_" + "x".repeat(50) |
| 113 | + const result = truncateOpenAiCallId(longId) |
| 114 | + // Should start with the prefix (first 55 chars) |
| 115 | + expect(result.startsWith("toolu_mcp--linear--create_issue_")).toBe(true) |
| 116 | + // Should contain a separator and hash |
| 117 | + expect(result).toContain("_") |
| 118 | + }) |
| 119 | + |
| 120 | + it("should handle the exact reported issue length (69 chars)", () => { |
| 121 | + // The original error mentioned 69 characters |
| 122 | + const id69Chars = "toolu_mcp--posthog--query_run_" + "a".repeat(39) // total 69 chars |
| 123 | + expect(id69Chars.length).toBe(69) |
| 124 | + const result = truncateOpenAiCallId(id69Chars) |
| 125 | + expect(result.length).toBe(64) |
| 126 | + }) |
| 127 | + }) |
| 128 | + |
| 129 | + describe("custom max length", () => { |
| 130 | + it("should support custom max length", () => { |
| 131 | + const longId = "a".repeat(50) |
| 132 | + const result = truncateOpenAiCallId(longId, 32) |
| 133 | + expect(result.length).toBe(32) |
| 134 | + }) |
| 135 | + |
| 136 | + it("should not truncate if within custom limit", () => { |
| 137 | + const id = "short_id" |
| 138 | + expect(truncateOpenAiCallId(id, 100)).toBe(id) |
| 139 | + }) |
| 140 | + }) |
| 141 | +}) |
| 142 | + |
| 143 | +describe("sanitizeOpenAiCallId", () => { |
| 144 | + it("should sanitize characters and truncate if needed", () => { |
| 145 | + // ID with invalid chars and too long |
| 146 | + const longIdWithInvalidChars = "toolu_mcp.server:tool/name_" + "x".repeat(50) |
| 147 | + const result = sanitizeOpenAiCallId(longIdWithInvalidChars) |
| 148 | + // Should be within limit |
| 149 | + expect(result.length).toBeLessThanOrEqual(64) |
| 150 | + // Should not contain invalid characters |
| 151 | + expect(result).toMatch(/^[a-zA-Z0-9_-]+$/) |
| 152 | + }) |
| 153 | + |
| 154 | + it("should only sanitize if length is within limit", () => { |
| 155 | + const shortIdWithInvalidChars = "tool.with.dots" |
| 156 | + const result = sanitizeOpenAiCallId(shortIdWithInvalidChars) |
| 157 | + expect(result).toBe("tool_with_dots") |
| 158 | + }) |
| 159 | + |
| 160 | + it("should handle real-world MCP tool IDs", () => { |
| 161 | + // Real MCP tool ID that might exceed 64 chars |
| 162 | + const mcpToolId = "call_mcp--posthog--dashboard_create_12345678-1234-1234-1234-123456789012" |
| 163 | + const result = sanitizeOpenAiCallId(mcpToolId) |
| 164 | + expect(result.length).toBeLessThanOrEqual(64) |
| 165 | + expect(result).toMatch(/^[a-zA-Z0-9_-]+$/) |
| 166 | + }) |
| 167 | + |
| 168 | + it("should preserve IDs that are already valid and within limit", () => { |
| 169 | + const validId = "toolu_01AbC-xyz_789" |
| 170 | + expect(sanitizeOpenAiCallId(validId)).toBe(validId) |
| 171 | + }) |
| 172 | +}) |
| 173 | + |
| 174 | +describe("OPENAI_CALL_ID_MAX_LENGTH constant", () => { |
| 175 | + it("should be 64", () => { |
| 176 | + expect(OPENAI_CALL_ID_MAX_LENGTH).toBe(64) |
| 177 | + }) |
| 178 | +}) |
0 commit comments