Skip to content

Commit b4d2b08

Browse files
feat(test): Adds comprehensive test of SVN submission information
- Add multiple test cases to verify parsing of SVN commit information, including search for specific revisions, case-insensitive revision prefix processing, and the ability to extract multi-line submission information from improved formats. - Handle tests on Chinese file names and contents to ensure correct parsing under different encoding conditions. - Enhance the processing of SVN command output to ensure that submission information and differences can be displayed correctly on different platforms.
1 parent 5d7e3de commit b4d2b08

File tree

1 file changed

+338
-3
lines changed

1 file changed

+338
-3
lines changed

src/utils/__tests__/svn.spec.ts

Lines changed: 338 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { promises as fs } from "fs"
33
import { exec } from "child_process"
44
import { promisify } from "util"
55
import * as path from "path"
6+
import * as os from "os"
67

78
// Use vi.hoisted to ensure the mock is available at the right time
89
const { mockExecAsync } = vi.hoisted(() => ({
@@ -25,9 +26,9 @@ vi.mock("vscode", () => ({
2526
appendLine: vi.fn(),
2627
show: vi.fn(),
2728
})),
28-
showErrorMessage: vi.fn(),
29-
showWarningMessage: vi.fn(),
30-
showInformationMessage: vi.fn(),
29+
showErrorMessage: vi.fn(() => Promise.resolve("Show Output")),
30+
showWarningMessage: vi.fn(() => Promise.resolve()),
31+
showInformationMessage: vi.fn(() => Promise.resolve()),
3132
},
3233
env: {
3334
openExternal: vi.fn(),
@@ -190,6 +191,82 @@ Working Copy Root Path: /test/workspace`,
190191
expect(result[0].message).toBe("Test commit message")
191192
})
192193

194+
it("should search for specific revision when query is in 'r123' format", async () => {
195+
// Mock checkSvnInstalled to return true
196+
mockExecAsync.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" }).mockResolvedValueOnce({
197+
stdout: `<?xml version="1.0" encoding="UTF-8"?>
198+
<log>
199+
<logentry revision="456">
200+
<author>jane.smith</author>
201+
<date>2023-02-15T14:30:00.000000Z</date>
202+
<msg>Specific revision commit</msg>
203+
</logentry>
204+
</log>`,
205+
stderr: "",
206+
})
207+
208+
// Mock checkSvnRepo to return true
209+
mockFsAccess.mockResolvedValue(undefined)
210+
211+
const result = await searchSvnCommits("r456", "/test/workspace")
212+
expect(result).toHaveLength(1)
213+
expect(result[0].revision).toBe("456")
214+
expect(result[0].author).toBe("jane.smith")
215+
expect(result[0].message).toBe("Specific revision commit")
216+
})
217+
218+
it("should handle case-insensitive 'r' prefix in revision search", async () => {
219+
// Mock checkSvnInstalled to return true
220+
mockExecAsync.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" }).mockResolvedValueOnce({
221+
stdout: `<?xml version="1.0" encoding="UTF-8"?>
222+
<log>
223+
<logentry revision="789">
224+
<author>test.user</author>
225+
<date>2023-03-15T16:30:00.000000Z</date>
226+
<msg>Case insensitive test</msg>
227+
</logentry>
228+
</log>`,
229+
stderr: "",
230+
})
231+
232+
// Mock checkSvnRepo to return true
233+
mockFsAccess.mockResolvedValue(undefined)
234+
235+
const result = await searchSvnCommits("R789", "/test/workspace")
236+
expect(result).toHaveLength(1)
237+
expect(result[0].revision).toBe("789")
238+
})
239+
240+
it("should NOT treat pure numbers as revision searches", async () => {
241+
// Mock checkSvnInstalled to return true
242+
mockExecAsync.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" }).mockResolvedValueOnce({
243+
stdout: `<?xml version="1.0" encoding="UTF-8"?>
244+
<log>
245+
<logentry revision="123">
246+
<author>john.doe</author>
247+
<date>2023-01-15T10:30:00.000000Z</date>
248+
<msg>Message containing 456 number</msg>
249+
</logentry>
250+
<logentry revision="456">
251+
<author>jane.smith</author>
252+
<date>2023-02-15T14:30:00.000000Z</date>
253+
<msg>Another commit</msg>
254+
</logentry>
255+
</log>`,
256+
stderr: "",
257+
})
258+
259+
// Mock checkSvnRepo to return true
260+
mockFsAccess.mockResolvedValue(undefined)
261+
262+
// Search for pure number "456" should search in message content, not as specific revision
263+
const result = await searchSvnCommits("456", "/test/workspace")
264+
// Should find the commit with "456" in the message, NOT the commit with revision 456
265+
expect(result).toHaveLength(1)
266+
expect(result[0].revision).toBe("123")
267+
expect(result[0].message).toContain("456")
268+
})
269+
193270
it("should return empty array when SVN is not available", async () => {
194271
mockExecAsync.mockRejectedValue(new Error("Command not found"))
195272

@@ -204,6 +281,8 @@ Working Copy Root Path: /test/workspace`,
204281
mockExecAsync.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" }).mockResolvedValueOnce({
205282
stdout: `------------------------------------------------------------------------
206283
r123 | john.doe | 2023-01-15 10:30:00 +0000 (Sun, 15 Jan 2023) | 1 line
284+
Changed paths:
285+
M /test.txt
207286
208287
Test commit message
209288
------------------------------------------------------------------------`,
@@ -218,13 +297,269 @@ Test commit message
218297
expect(result).toContain("Test commit message")
219298
})
220299

300+
it("should parse changed files information correctly", async () => {
301+
// Mock checkSvnInstalled and checkSvnRepo
302+
mockFsAccess.mockResolvedValue(undefined)
303+
mockExecAsync
304+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
305+
.mockResolvedValueOnce({
306+
stdout: `------------------------------------------------------------------------
307+
r456 | jane.smith | 2023-02-15 14:30:00 +0800 (Wed, 15 Feb 2023) | 2 lines
308+
Changed paths:
309+
A /src/new-file.ts
310+
M /src/existing-file.ts
311+
D /src/old-file.ts
312+
313+
Added new feature
314+
Fixed bug in existing functionality
315+
------------------------------------------------------------------------`,
316+
stderr: "",
317+
})
318+
.mockResolvedValueOnce({
319+
stdout: `Index: src/new-file.ts
320+
===================================================================
321+
--- src/new-file.ts (nonexistent)
322+
+++ src/new-file.ts (revision 456)
323+
@@ -0,0 +1,3 @@
324+
+export function newFunction() {
325+
+ return 'Hello World';
326+
+}`,
327+
stderr: "",
328+
})
329+
330+
const result = await getSvnCommitInfoForMentions("456", "/test/workspace")
331+
332+
// Should contain basic commit info
333+
expect(result).toContain("r456 by jane.smith")
334+
expect(result).toContain("Added new feature")
335+
expect(result).toContain("Fixed bug in existing functionality")
336+
337+
// Should contain changed files section
338+
expect(result).toContain("Changed files:")
339+
expect(result).toContain("A /src/new-file.ts")
340+
expect(result).toContain("M /src/existing-file.ts")
341+
expect(result).toContain("D /src/old-file.ts")
342+
343+
// Should contain diff section
344+
expect(result).toContain("Diff:")
345+
expect(result).toContain("export function newFunction()")
346+
})
347+
348+
it("should handle date parsing with Chinese day names gracefully", async () => {
349+
// Mock checkSvnInstalled and checkSvnRepo
350+
mockFsAccess.mockResolvedValue(undefined)
351+
mockExecAsync
352+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
353+
.mockResolvedValueOnce({
354+
stdout: `------------------------------------------------------------------------
355+
r789 | chinese.user | 2023-03-15 16:30:00 +0800 (星期三, 15 三月 2023) | 1 line
356+
Changed paths:
357+
M /中文文件.txt
358+
359+
测试中文提交信息
360+
------------------------------------------------------------------------`,
361+
stderr: "",
362+
})
363+
.mockResolvedValueOnce({
364+
stdout: `Index: 中文文件.txt
365+
===================================================================
366+
--- 中文文件.txt (revision 788)
367+
+++ 中文文件.txt (revision 789)
368+
@@ -1 +1 @@
369+
-旧内容
370+
+新内容`,
371+
stderr: "",
372+
})
373+
374+
const result = await getSvnCommitInfoForMentions("789", "/test/workspace")
375+
376+
// Should contain commit info with extracted date
377+
expect(result).toContain("r789 by chinese.user")
378+
expect(result).toContain("2023-03-15 16:30:00 +0800")
379+
expect(result).toContain("测试中文提交信息")
380+
381+
// Should handle Chinese file names and content
382+
expect(result).toContain("M /中文文件.txt")
383+
expect(result).toContain("新内容")
384+
})
385+
386+
it("should handle commit message extraction from improved format", async () => {
387+
// Mock checkSvnInstalled and checkSvnRepo
388+
mockFsAccess.mockResolvedValue(undefined)
389+
mockExecAsync
390+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
391+
.mockResolvedValueOnce({
392+
stdout: `------------------------------------------------------------------------
393+
r100 | developer | 2023-01-01 12:00:00 +0000 (Sun, 01 Jan 2023) | 3 lines
394+
Changed paths:
395+
M /src/component.ts
396+
397+
This is a multi-line commit message
398+
with detailed explanation
399+
and some additional notes
400+
------------------------------------------------------------------------`,
401+
stderr: "",
402+
})
403+
.mockResolvedValueOnce({
404+
stdout: "",
405+
stderr: "",
406+
})
407+
408+
const result = await getSvnCommitInfoForMentions("100", "/test/workspace")
409+
410+
// Should extract multi-line commit message correctly
411+
expect(result).toContain("This is a multi-line commit message")
412+
expect(result).toContain("with detailed explanation")
413+
expect(result).toContain("and some additional notes")
414+
})
415+
416+
it("should handle revision with 'r' prefix input", async () => {
417+
// Mock checkSvnInstalled and checkSvnRepo
418+
mockFsAccess.mockResolvedValue(undefined)
419+
mockExecAsync
420+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
421+
.mockResolvedValueOnce({
422+
stdout: `------------------------------------------------------------------------
423+
r555 | test.author | 2023-05-15 10:15:00 +0000 (Mon, 15 May 2023) | 1 line
424+
Changed paths:
425+
M /test-file.txt
426+
427+
Test with r prefix input
428+
------------------------------------------------------------------------`,
429+
stderr: "",
430+
})
431+
.mockResolvedValueOnce({
432+
stdout: "",
433+
stderr: "",
434+
})
435+
436+
const result = await getSvnCommitInfoForMentions("r555", "/test/workspace")
437+
expect(result).toContain("r555 by test.author")
438+
expect(result).toContain("Test with r prefix input")
439+
})
440+
221441
it("should return error message for invalid revision", async () => {
222442
// Mock checkSvnInstalled to fail
223443
mockExecAsync.mockRejectedValue(new Error("Command not found"))
224444

225445
const result = await getSvnCommitInfoForMentions("invalid", "/test/workspace")
226446
expect(result).toBe("Error: SVN not available or not an SVN repository")
227447
})
448+
449+
it("should handle diff output with UTF-8 encoding", async () => {
450+
// Mock checkSvnInstalled and checkSvnRepo to return true
451+
mockFsAccess.mockResolvedValue(undefined)
452+
453+
// Create a buffer with UTF-8 encoded Chinese characters
454+
const utf8Buffer = Buffer.from("测试文件内容", "utf8")
455+
456+
// Mock the svn log and diff commands
457+
mockExecAsync
458+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
459+
.mockResolvedValueOnce({
460+
stdout: `------------------------------------------------------------------------
461+
r123 | john.doe | 2023-01-15 10:30:00 +0000 (Sun, 15 Jan 2023) | 1 line
462+
Changed paths:
463+
M /test.txt
464+
465+
Test commit message
466+
------------------------------------------------------------------------`,
467+
stderr: "",
468+
})
469+
.mockResolvedValueOnce({ stdout: utf8Buffer, stderr: "" })
470+
471+
const result = await getSvnCommitInfoForMentions("123", "/test/workspace")
472+
expect(result).toContain("r123")
473+
expect(result).toContain("john.doe")
474+
expect(result).toContain("Test commit message")
475+
expect(result).toContain("测试文件内容")
476+
})
477+
478+
it("should handle diff output with problematic encoding", async () => {
479+
// Mock checkSvnInstalled and checkSvnRepo to return true
480+
mockFsAccess.mockResolvedValue(undefined)
481+
482+
// Create a buffer with problematic encoding (contains replacement characters)
483+
const problematicBuffer = Buffer.from("Test file with � characters", "utf8")
484+
485+
// Mock the svn log and diff commands
486+
mockExecAsync
487+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
488+
.mockResolvedValueOnce({
489+
stdout: `------------------------------------------------------------------------
490+
r123 | john.doe | 2023-01-15 10:30:00 +0000 (Sun, 15 Jan 2023) | 1 line
491+
Changed paths:
492+
M /test.txt
493+
494+
Test commit message
495+
------------------------------------------------------------------------`,
496+
stderr: "",
497+
})
498+
.mockResolvedValueOnce({ stdout: problematicBuffer, stderr: "" })
499+
500+
const result = await getSvnCommitInfoForMentions("123", "/test/workspace")
501+
expect(result).toContain("r123")
502+
expect(result).toContain("john.doe")
503+
expect(result).toContain("Test commit message")
504+
// Should handle the problematic encoding gracefully
505+
expect(result).toContain("Test file with")
506+
})
507+
508+
it("should handle string output from svn diff command", async () => {
509+
// Mock checkSvnInstalled and checkSvnRepo to return true
510+
mockFsAccess.mockResolvedValue(undefined)
511+
512+
// Mock the svn log command with string output instead of Buffer
513+
mockExecAsync
514+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
515+
.mockResolvedValueOnce({
516+
stdout: `------------------------------------------------------------------------
517+
r123 | john.doe | 2023-01-15 10:30:00 +0000 (Sun, 15 Jan 2023) | 1 line
518+
Changed paths:
519+
M /test.txt
520+
521+
Test commit message
522+
------------------------------------------------------------------------`,
523+
stderr: "",
524+
})
525+
.mockResolvedValueOnce({
526+
stdout: "Index: test.txt\n===================================================================\n--- test.txt\t(revision 122)\n+++ test.txt\t(revision 123)\n@@ -1 +1 @@\n-old content\n+new content",
527+
stderr: "",
528+
})
529+
530+
const result = await getSvnCommitInfoForMentions("123", "/test/workspace")
531+
expect(result).toContain("r123")
532+
expect(result).toContain("john.doe")
533+
expect(result).toContain("Test commit message")
534+
expect(result).toContain("new content")
535+
})
536+
537+
it("should handle diff command failure gracefully", async () => {
538+
// Mock checkSvnInstalled and checkSvnRepo to return true
539+
mockFsAccess.mockResolvedValue(undefined)
540+
541+
// Mock the svn log command to succeed but diff to fail
542+
mockExecAsync
543+
.mockResolvedValueOnce({ stdout: "svn, version 1.14.0\n", stderr: "" })
544+
.mockResolvedValueOnce({
545+
stdout: `------------------------------------------------------------------------
546+
r123 | john.doe | 2023-01-15 10:30:00 +0000 (Sun, 15 Jan 2023) | 1 line
547+
Changed paths:
548+
M /test.txt
549+
550+
Test commit message
551+
------------------------------------------------------------------------`,
552+
stderr: "",
553+
})
554+
.mockRejectedValueOnce(new Error("svn: E160013: File not found"))
555+
556+
const result = await getSvnCommitInfoForMentions("123", "/test/workspace")
557+
expect(result).toContain("r123")
558+
expect(result).toContain("john.doe")
559+
expect(result).toContain("Test commit message")
560+
// Should not contain diff section when diff fails
561+
expect(result).not.toContain("Diff:")
562+
})
228563
})
229564

230565
describe("getWorkspaceSvnInfo", () => {

0 commit comments

Comments
 (0)