|
|
@@ -306,6 +306,197 @@ describe("truncateOutput", () => {
|
|
|
const expectedLines = ["line1", "", "[...10 lines omitted...]", "", "line12", "line13", "line14", "line15"]
|
|
|
expect(resultLines).toEqual(expectedLines)
|
|
|
})
|
|
|
+
|
|
|
+ describe("character limit functionality", () => {
|
|
|
+ it("returns original content when no character limit provided", () => {
|
|
|
+ const content = "a".repeat(1000)
|
|
|
+ expect(truncateOutput(content, undefined, undefined)).toBe(content)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("returns original content when characters are under limit", () => {
|
|
|
+ const content = "a".repeat(100)
|
|
|
+ expect(truncateOutput(content, undefined, 200)).toBe(content)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("truncates content by character limit with 20/80 split", () => {
|
|
|
+ // Create content with 1000 characters
|
|
|
+ const content = "a".repeat(1000)
|
|
|
+
|
|
|
+ // Set character limit to 100
|
|
|
+ const result = truncateOutput(content, undefined, 100)
|
|
|
+
|
|
|
+ // Should keep:
|
|
|
+ // - First 20 characters (20% of 100)
|
|
|
+ // - Last 80 characters (80% of 100)
|
|
|
+ // - Omission indicator in between
|
|
|
+ const expectedStart = "a".repeat(20)
|
|
|
+ const expectedEnd = "a".repeat(80)
|
|
|
+ const expected = expectedStart + "\n[...900 characters omitted...]\n" + expectedEnd
|
|
|
+
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("prioritizes character limit over line limit", () => {
|
|
|
+ // Create content with few lines but many characters per line
|
|
|
+ const longLine = "a".repeat(500)
|
|
|
+ const content = `${longLine}\n${longLine}\n${longLine}`
|
|
|
+
|
|
|
+ // Set both limits - character limit should take precedence
|
|
|
+ const result = truncateOutput(content, 10, 100)
|
|
|
+
|
|
|
+ // Should truncate by character limit, not line limit
|
|
|
+ const expectedStart = "a".repeat(20)
|
|
|
+ const expectedEnd = "a".repeat(80)
|
|
|
+ // Total content: 1502 chars, limit: 100, so 1402 chars omitted
|
|
|
+ const expected = expectedStart + "\n[...1402 characters omitted...]\n" + expectedEnd
|
|
|
+
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("falls back to line limit when character limit is satisfied", () => {
|
|
|
+ // Create content with many short lines
|
|
|
+ const lines = Array.from({ length: 25 }, (_, i) => `line${i + 1}`)
|
|
|
+ const content = lines.join("\n")
|
|
|
+
|
|
|
+ // Character limit is high enough, so line limit should apply
|
|
|
+ const result = truncateOutput(content, 10, 10000)
|
|
|
+
|
|
|
+ // Should truncate by line limit
|
|
|
+ const expectedLines = [
|
|
|
+ "line1",
|
|
|
+ "line2",
|
|
|
+ "",
|
|
|
+ "[...15 lines omitted...]",
|
|
|
+ "",
|
|
|
+ "line18",
|
|
|
+ "line19",
|
|
|
+ "line20",
|
|
|
+ "line21",
|
|
|
+ "line22",
|
|
|
+ "line23",
|
|
|
+ "line24",
|
|
|
+ "line25",
|
|
|
+ ]
|
|
|
+ expect(result).toBe(expectedLines.join("\n"))
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles edge case where character limit equals content length", () => {
|
|
|
+ const content = "exactly100chars".repeat(6) + "1234" // exactly 100 chars
|
|
|
+ const result = truncateOutput(content, undefined, 100)
|
|
|
+ expect(result).toBe(content)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles very small character limits", () => {
|
|
|
+ const content = "a".repeat(1000)
|
|
|
+ const result = truncateOutput(content, undefined, 10)
|
|
|
+
|
|
|
+ // 20% of 10 = 2, 80% of 10 = 8
|
|
|
+ const expected = "aa\n[...990 characters omitted...]\n" + "a".repeat(8)
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit with mixed content", () => {
|
|
|
+ const content = "Hello world! This is a test with mixed content including numbers 123 and symbols @#$%"
|
|
|
+ const result = truncateOutput(content, undefined, 50)
|
|
|
+
|
|
|
+ // 20% of 50 = 10, 80% of 50 = 40
|
|
|
+ const expectedStart = content.slice(0, 10) // "Hello worl"
|
|
|
+ const expectedEnd = content.slice(-40) // last 40 chars
|
|
|
+ const omittedChars = content.length - 50
|
|
|
+ const expected = expectedStart + `\n[...${omittedChars} characters omitted...]\n` + expectedEnd
|
|
|
+
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ describe("edge cases with very small character limits", () => {
|
|
|
+ it("handles character limit of 1", () => {
|
|
|
+ const content = "abcdefghijklmnopqrstuvwxyz"
|
|
|
+ const result = truncateOutput(content, undefined, 1)
|
|
|
+
|
|
|
+ // 20% of 1 = 0.2 (floor = 0), so beforeLimit = 0
|
|
|
+ // afterLimit = 1 - 0 = 1
|
|
|
+ // Should keep 0 chars from start and 1 char from end
|
|
|
+ const expected = "\n[...25 characters omitted...]\nz"
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit of 2", () => {
|
|
|
+ const content = "abcdefghijklmnopqrstuvwxyz"
|
|
|
+ const result = truncateOutput(content, undefined, 2)
|
|
|
+
|
|
|
+ // 20% of 2 = 0.4 (floor = 0), so beforeLimit = 0
|
|
|
+ // afterLimit = 2 - 0 = 2
|
|
|
+ // Should keep 0 chars from start and 2 chars from end
|
|
|
+ const expected = "\n[...24 characters omitted...]\nyz"
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit of 5", () => {
|
|
|
+ const content = "abcdefghijklmnopqrstuvwxyz"
|
|
|
+ const result = truncateOutput(content, undefined, 5)
|
|
|
+
|
|
|
+ // 20% of 5 = 1, so beforeLimit = 1
|
|
|
+ // afterLimit = 5 - 1 = 4
|
|
|
+ // Should keep 1 char from start and 4 chars from end
|
|
|
+ const expected = "a\n[...21 characters omitted...]\nwxyz"
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit with multi-byte characters", () => {
|
|
|
+ const content = "🚀🎉🔥💻🌟🎨🎯🎪🎭🎬" // 10 emojis, each is multi-byte
|
|
|
+ const result = truncateOutput(content, undefined, 10)
|
|
|
+
|
|
|
+ // Character limit works on string length, not byte count
|
|
|
+ // 20% of 10 = 2, 80% of 10 = 8
|
|
|
+ // Note: In JavaScript, each emoji is actually 2 characters (surrogate pair)
|
|
|
+ // So the content is actually 20 characters long, not 10
|
|
|
+ const expected = "🚀\n[...10 characters omitted...]\n🎯🎪🎭🎬"
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit with newlines in content", () => {
|
|
|
+ const content = "line1\nline2\nline3\nline4\nline5"
|
|
|
+ const result = truncateOutput(content, undefined, 15)
|
|
|
+
|
|
|
+ // Total length is 29 chars (including newlines)
|
|
|
+ // 20% of 15 = 3, 80% of 15 = 12
|
|
|
+ // The slice will take first 3 chars: "lin"
|
|
|
+ // And last 12 chars: "e4\nline5" (counting backwards)
|
|
|
+ const expected = "lin\n[...14 characters omitted...]\n\nline4\nline5"
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit exactly matching content with omission message", () => {
|
|
|
+ // Edge case: when the omission message would make output longer than original
|
|
|
+ const content = "short"
|
|
|
+ const result = truncateOutput(content, undefined, 10)
|
|
|
+
|
|
|
+ // Content is 5 chars, limit is 10, so no truncation needed
|
|
|
+ expect(result).toBe(content)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("handles character limit smaller than omission message", () => {
|
|
|
+ const content = "a".repeat(100)
|
|
|
+ const result = truncateOutput(content, undefined, 3)
|
|
|
+
|
|
|
+ // 20% of 3 = 0.6 (floor = 0), so beforeLimit = 0
|
|
|
+ // afterLimit = 3 - 0 = 3
|
|
|
+ const expected = "\n[...97 characters omitted...]\naaa"
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+
|
|
|
+ it("prioritizes character limit even with very high line limit", () => {
|
|
|
+ const content = "a".repeat(1000)
|
|
|
+ const result = truncateOutput(content, 999999, 50)
|
|
|
+
|
|
|
+ // Character limit should still apply despite high line limit
|
|
|
+ const expectedStart = "a".repeat(10) // 20% of 50
|
|
|
+ const expectedEnd = "a".repeat(40) // 80% of 50
|
|
|
+ const expected = expectedStart + "\n[...950 characters omitted...]\n" + expectedEnd
|
|
|
+ expect(result).toBe(expected)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
describe("applyRunLengthEncoding", () => {
|