|
1 | 1 | import { describe, it, expect, vi, beforeEach } from "vitest"; |
2 | | -import { createFile, listFiles, pathExists, makeDirectory } from "../src/commands/files.js"; |
| 2 | +import { createFile, listFiles, pathExists, makeDirectory, renameFileOrDirectory } from "../src/commands/files.js"; |
3 | 3 | import chalk from "chalk"; |
4 | 4 | import * as PuterModule from "../src/modules/PuterModule.js"; |
5 | 5 | import * as auth from "../src/commands/auth.js"; |
@@ -42,6 +42,8 @@ const mockPuter = { |
42 | 42 | mkdir: vi.fn(), |
43 | 43 | upload: vi.fn(), |
44 | 44 | readdir: vi.fn(), |
| 45 | + move: vi.fn(), |
| 46 | + rename: vi.fn(), |
45 | 47 | }, |
46 | 48 | }; |
47 | 49 |
|
@@ -362,3 +364,92 @@ describe("makeDirectory", () => { |
362 | 364 | ); |
363 | 365 | }); |
364 | 366 | }); |
| 367 | + |
| 368 | +describe("renameFileOrDirectory", () => { |
| 369 | + beforeEach(() => { |
| 370 | + vi.clearAllMocks(); |
| 371 | + vi.spyOn(PuterModule, "getPuter").mockReturnValue(mockPuter); |
| 372 | + vi.spyOn(auth, "getCurrentDirectory").mockReturnValue("/testuser/files"); |
| 373 | + vi.spyOn(commons, "resolvePath").mockImplementation((current, newPath) => |
| 374 | + path.join(current, newPath) |
| 375 | + ); |
| 376 | + }); |
| 377 | + |
| 378 | + it("should show usage when less than 2 arguments provided", async () => { |
| 379 | + await renameFileOrDirectory([]); |
| 380 | + expect(console.log).toHaveBeenCalledWith( |
| 381 | + chalk.red("Usage: mv <source> <destination>") |
| 382 | + ); |
| 383 | + |
| 384 | + vi.clearAllMocks(); |
| 385 | + await renameFileOrDirectory(["onlyOne"]); |
| 386 | + expect(console.log).toHaveBeenCalledWith( |
| 387 | + chalk.red("Usage: mv <source> <destination>") |
| 388 | + ); |
| 389 | + |
| 390 | + expect(mockPuter.fs.stat).not.toHaveBeenCalled(); |
| 391 | + }); |
| 392 | + |
| 393 | + it("should move file to directory successfully", async () => { |
| 394 | + // Source file exists |
| 395 | + mockPuter.fs.stat |
| 396 | + .mockResolvedValueOnce({ uid: "source-uid-123", name: "file.txt" }) // source stat |
| 397 | + .mockResolvedValueOnce({ is_dir: true }); // dest stat - it's a directory |
| 398 | + |
| 399 | + mockPuter.fs.move.mockResolvedValue({ |
| 400 | + moved: { path: "/testuser/files/destdir/file.txt" }, |
| 401 | + }); |
| 402 | + |
| 403 | + await renameFileOrDirectory(["file.txt", "destdir"]); |
| 404 | + |
| 405 | + expect(mockPuter.fs.stat).toHaveBeenCalledWith("/testuser/files/file.txt"); |
| 406 | + expect(mockPuter.fs.stat).toHaveBeenCalledWith("/testuser/files/destdir"); |
| 407 | + expect(mockPuter.fs.move).toHaveBeenCalledWith( |
| 408 | + "source-uid-123", |
| 409 | + "/testuser/files/destdir", |
| 410 | + { |
| 411 | + overwrite: false, |
| 412 | + newName: "file.txt", |
| 413 | + createMissingParents: false, |
| 414 | + } |
| 415 | + ); |
| 416 | + expect(console.log).toHaveBeenCalledWith( |
| 417 | + expect.stringContaining("Successfully moved") |
| 418 | + ); |
| 419 | + }); |
| 420 | + |
| 421 | + it("should rename file successfully when destination is not a directory", async () => { |
| 422 | + // Source file exists |
| 423 | + mockPuter.fs.stat |
| 424 | + .mockResolvedValueOnce({ uid: "source-uid-456", name: "oldname.txt" }) // source stat |
| 425 | + .mockRejectedValueOnce({ code: "subject_does_not_exist" }); // dest doesn't exist |
| 426 | + |
| 427 | + mockPuter.fs.rename.mockResolvedValue({ |
| 428 | + path: "/testuser/files/newname.txt", |
| 429 | + }); |
| 430 | + |
| 431 | + await renameFileOrDirectory(["oldname.txt", "newname.txt"]); |
| 432 | + |
| 433 | + expect(mockPuter.fs.stat).toHaveBeenCalledWith("/testuser/files/oldname.txt"); |
| 434 | + expect(mockPuter.fs.rename).toHaveBeenCalledWith("source-uid-456", "newname.txt"); |
| 435 | + expect(console.log).toHaveBeenCalledWith( |
| 436 | + expect.stringContaining("Successfully renamed") |
| 437 | + ); |
| 438 | + }); |
| 439 | + |
| 440 | + it("should handle absolute paths directly", async () => { |
| 441 | + mockPuter.fs.stat |
| 442 | + .mockResolvedValueOnce({ uid: "abs-uid", name: "source.txt" }) |
| 443 | + .mockRejectedValueOnce({ code: "subject_does_not_exist" }); |
| 444 | + |
| 445 | + mockPuter.fs.rename.mockResolvedValue({ |
| 446 | + path: "/absolute/dest.txt", |
| 447 | + }); |
| 448 | + |
| 449 | + await renameFileOrDirectory(["/absolute/source.txt", "/absolute/dest.txt"]); |
| 450 | + |
| 451 | + expect(commons.resolvePath).not.toHaveBeenCalled(); |
| 452 | + expect(mockPuter.fs.stat).toHaveBeenCalledWith("/absolute/source.txt"); |
| 453 | + expect(mockPuter.fs.rename).toHaveBeenCalledWith("abs-uid", "dest.txt"); |
| 454 | + }); |
| 455 | +}); |
0 commit comments