|
@@ -615,30 +615,64 @@ describe("Rules directory reading", () => {
|
|
|
} as any)
|
|
} as any)
|
|
|
|
|
|
|
|
// Simulate listing files including a symlink
|
|
// Simulate listing files including a symlink
|
|
|
- readdirMock.mockResolvedValueOnce([
|
|
|
|
|
- {
|
|
|
|
|
- name: "regular.txt",
|
|
|
|
|
- isFile: () => true,
|
|
|
|
|
- isSymbolicLink: () => false,
|
|
|
|
|
- parentPath: "/fake/path/.roo/rules",
|
|
|
|
|
- },
|
|
|
|
|
- { name: "link.txt", isFile: () => false, isSymbolicLink: () => true, parentPath: "/fake/path/.roo/rules" },
|
|
|
|
|
- ] as any)
|
|
|
|
|
|
|
+ readdirMock
|
|
|
|
|
+ .mockResolvedValueOnce([
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "regular.txt",
|
|
|
|
|
+ isFile: () => true,
|
|
|
|
|
+ isSymbolicLink: () => false,
|
|
|
|
|
+ parentPath: "/fake/path/.roo/rules",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "link.txt",
|
|
|
|
|
+ isFile: () => false,
|
|
|
|
|
+ isSymbolicLink: () => true,
|
|
|
|
|
+ parentPath: "/fake/path/.roo/rules",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "link_dir",
|
|
|
|
|
+ isFile: () => false,
|
|
|
|
|
+ isSymbolicLink: () => true,
|
|
|
|
|
+ parentPath: "/fake/path/.roo/rules",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "nested_link.txt",
|
|
|
|
|
+ isFile: () => false,
|
|
|
|
|
+ isSymbolicLink: () => true,
|
|
|
|
|
+ parentPath: "/fake/path/.roo/rules",
|
|
|
|
|
+ },
|
|
|
|
|
+ ] as any)
|
|
|
|
|
+ .mockResolvedValueOnce([
|
|
|
|
|
+ { name: "subdir_link.txt", isFile: () => true, parentPath: "/fake/path/.roo/rules/symlink-target-dir" },
|
|
|
|
|
+ ] as any)
|
|
|
|
|
|
|
|
// Simulate readlink response
|
|
// Simulate readlink response
|
|
|
- readlinkMock.mockResolvedValueOnce("../symlink-target.txt")
|
|
|
|
|
|
|
+ readlinkMock
|
|
|
|
|
+ .mockResolvedValueOnce("../symlink-target.txt")
|
|
|
|
|
+ .mockResolvedValueOnce("../symlink-target-dir")
|
|
|
|
|
+ .mockResolvedValueOnce("../nested-symlink")
|
|
|
|
|
+ .mockResolvedValueOnce("nested-symlink-target.txt")
|
|
|
|
|
|
|
|
// Reset and set up the stat mock with more granular control
|
|
// Reset and set up the stat mock with more granular control
|
|
|
statMock.mockReset()
|
|
statMock.mockReset()
|
|
|
statMock.mockImplementation((path: string) => {
|
|
statMock.mockImplementation((path: string) => {
|
|
|
// For directory check
|
|
// For directory check
|
|
|
- if (path === "/fake/path/.roo/rules") {
|
|
|
|
|
|
|
+ if (path === "/fake/path/.roo/rules" || path.endsWith("dir")) {
|
|
|
return Promise.resolve({
|
|
return Promise.resolve({
|
|
|
isDirectory: jest.fn().mockReturnValue(true),
|
|
isDirectory: jest.fn().mockReturnValue(true),
|
|
|
isFile: jest.fn().mockReturnValue(false),
|
|
isFile: jest.fn().mockReturnValue(false),
|
|
|
} as any)
|
|
} as any)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // For symlink check
|
|
|
|
|
+ if (path.endsWith("symlink")) {
|
|
|
|
|
+ return Promise.resolve({
|
|
|
|
|
+ isDirectory: jest.fn().mockReturnValue(false),
|
|
|
|
|
+ isFile: jest.fn().mockReturnValue(false),
|
|
|
|
|
+ isSymbolicLink: jest.fn().mockReturnValue(true),
|
|
|
|
|
+ } as any)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// For all files
|
|
// For all files
|
|
|
return Promise.resolve({
|
|
return Promise.resolve({
|
|
|
isFile: jest.fn().mockReturnValue(true),
|
|
isFile: jest.fn().mockReturnValue(true),
|
|
@@ -654,6 +688,12 @@ describe("Rules directory reading", () => {
|
|
|
if (filePath.toString() === "/fake/path/.roo/rules/../symlink-target.txt") {
|
|
if (filePath.toString() === "/fake/path/.roo/rules/../symlink-target.txt") {
|
|
|
return Promise.resolve("symlink target content")
|
|
return Promise.resolve("symlink target content")
|
|
|
}
|
|
}
|
|
|
|
|
+ if (filePath.toString() === "/fake/path/.roo/rules/symlink-target-dir/subdir_link.txt") {
|
|
|
|
|
+ return Promise.resolve("regular file content under symlink target dir")
|
|
|
|
|
+ }
|
|
|
|
|
+ if (filePath.toString() === "/fake/path/.roo/rules/../nested-symlink-target.txt") {
|
|
|
|
|
+ return Promise.resolve("nested symlink target content")
|
|
|
|
|
+ }
|
|
|
return Promise.reject({ code: "ENOENT" })
|
|
return Promise.reject({ code: "ENOENT" })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -664,13 +704,20 @@ describe("Rules directory reading", () => {
|
|
|
expect(result).toContain("regular file content")
|
|
expect(result).toContain("regular file content")
|
|
|
expect(result).toContain("# Rules from /fake/path/.roo/rules/../symlink-target.txt:")
|
|
expect(result).toContain("# Rules from /fake/path/.roo/rules/../symlink-target.txt:")
|
|
|
expect(result).toContain("symlink target content")
|
|
expect(result).toContain("symlink target content")
|
|
|
|
|
+ expect(result).toContain("# Rules from /fake/path/.roo/rules/symlink-target-dir/subdir_link.txt:")
|
|
|
|
|
+ expect(result).toContain("regular file content under symlink target dir")
|
|
|
|
|
+ expect(result).toContain("# Rules from /fake/path/.roo/rules/../nested-symlink-target.txt:")
|
|
|
|
|
+ expect(result).toContain("nested symlink target content")
|
|
|
|
|
|
|
|
// Verify readlink was called with the symlink path
|
|
// Verify readlink was called with the symlink path
|
|
|
expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.roo/rules/link.txt")
|
|
expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.roo/rules/link.txt")
|
|
|
|
|
+ expect(readlinkMock).toHaveBeenCalledWith("/fake/path/.roo/rules/link_dir")
|
|
|
|
|
|
|
|
// Verify both files were read
|
|
// Verify both files were read
|
|
|
expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/regular.txt", "utf-8")
|
|
expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/regular.txt", "utf-8")
|
|
|
expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/../symlink-target.txt", "utf-8")
|
|
expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/../symlink-target.txt", "utf-8")
|
|
|
|
|
+ expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/symlink-target-dir/subdir_link.txt", "utf-8")
|
|
|
|
|
+ expect(readFileMock).toHaveBeenCalledWith("/fake/path/.roo/rules/../nested-symlink-target.txt", "utf-8")
|
|
|
})
|
|
})
|
|
|
beforeEach(() => {
|
|
beforeEach(() => {
|
|
|
jest.clearAllMocks()
|
|
jest.clearAllMocks()
|