import { describe, it, expect } from "vitest"; import { validateRemotePath, validateHostConfig } from "../utils/validation"; import { SSHHostConfig } from "../types/server"; describe("Browse E2E Flow", () => { const mockHostConfig: SSHHostConfig = { host: "browseserver", hostName: "browse.example.com", user: "browseuser", port: 22, identityFile: "~/.ssh/browse_key", }; it("should complete full browse workflow with valid inputs", () => { // Step 1: Validate remote path (home directory) const remotePath = "~"; const remoteValidation = validateRemotePath(remotePath); expect(remoteValidation.valid).toBe(true); expect(remoteValidation.error).toBeUndefined(); // Step 2: Validate host config const hostValidation = validateHostConfig(mockHostConfig); expect(hostValidation.valid).toBe(true); expect(hostValidation.error).toBeUndefined(); // Step 3: Verify path construction for navigation const subdirectory = "documents"; const constructedPath = remotePath.endsWith("/") ? `${remotePath}${subdirectory}` : `${remotePath}/${subdirectory}`; expect(constructedPath).toBe("~/documents"); }); it("should handle absolute remote path", () => { const remotePath = "/home/user/documents"; const remoteValidation = validateRemotePath(remotePath); expect(remoteValidation.valid).toBe(true); expect(remoteValidation.error).toBeUndefined(); const hostValidation = validateHostConfig(mockHostConfig); expect(hostValidation.valid).toBe(true); }); it("should handle relative remote path", () => { const remotePath = "documents/files"; const remoteValidation = validateRemotePath(remotePath); expect(remoteValidation.valid).toBe(true); expect(remoteValidation.error).toBeUndefined(); }); it("should handle empty remote path (defaults to ~)", () => { // In the browse form, empty path defaults to "~" const remotePath = ""; const remotePathValue = remotePath.trim() || "~"; expect(remotePathValue).toBe("~"); const remoteValidation = validateRemotePath(remotePathValue); expect(remoteValidation.valid).toBe(true); }); it("should construct paths correctly for directory navigation", () => { const testCases = [ { current: "~", subdir: "documents", expected: "~/documents" }, { current: "/home/user", subdir: "documents", expected: "/home/user/documents", }, { current: "/home/user/", subdir: "documents", expected: "/home/user/documents", }, { current: "~/projects", subdir: "my-project", expected: "~/projects/my-project", }, { current: "/var/www", subdir: "html", expected: "/var/www/html" }, ]; testCases.forEach(({ current, subdir, expected }) => { const constructedPath = current.endsWith("/") ? `${current}${subdir}` : `${current}/${subdir}`; expect(constructedPath).toBe(expected); }); }); it("should handle invalid remote path in browse workflow", () => { // Try to validate invalid remote path (empty without default) const remoteValidation = validateRemotePath(""); // Should fail validation expect(remoteValidation.valid).toBe(false); expect(remoteValidation.error).toBeDefined(); expect(remoteValidation.error).toContain("cannot be empty"); }); it("should handle invalid remote path with control characters", () => { const invalidPath = "/remote/path\x00/directory"; const remoteValidation = validateRemotePath(invalidPath); // Should fail validation expect(remoteValidation.valid).toBe(false); expect(remoteValidation.error).toBeDefined(); expect(remoteValidation.error).toContain("control characters"); }); it("should validate all inputs before allowing browse", () => { const browseHost: SSHHostConfig = { host: "browseserver", hostName: "browse.example.com", user: "browseuser", }; // All validations should pass const remoteValidation = validateRemotePath("~"); const hostValidation = validateHostConfig(browseHost); expect(remoteValidation.valid).toBe(true); expect(hostValidation.valid).toBe(true); }); it("should handle host without optional properties", () => { const minimalHost: SSHHostConfig = { host: "minimal-server", hostName: "minimal.example.com", }; // Should validate successfully even without user, port, and identityFile const hostValidation = validateHostConfig(minimalHost); expect(hostValidation.valid).toBe(true); }); it("should handle various remote path formats for browsing", () => { const validPaths = [ "/absolute/path/directory", "relative/path/directory", "/path/with spaces/directory", "/path/with-dashes/directory", "/path/with_underscores/directory", "/path/with.dots/directory", "~/home/path/directory", "~", ]; validPaths.forEach((remotePath) => { const validation = validateRemotePath(remotePath); expect(validation.valid).toBe(true); }); }); it("should handle path construction for nested directories", () => { // Simulate navigating through multiple directory levels let currentPath = "~"; const directories = ["documents", "projects", "my-app"]; directories.forEach((dir) => { currentPath = currentPath.endsWith("/") ? `${currentPath}${dir}` : `${currentPath}/${dir}`; }); expect(currentPath).toBe("~/documents/projects/my-app"); }); it("should handle path construction with trailing slashes", () => { const pathsWithTrailingSlash = [ { path: "/home/user/", subdir: "documents", expected: "/home/user/documents", }, { path: "~/", subdir: "projects", expected: "~/projects" }, { path: "/var/www/", subdir: "html", expected: "/var/www/html" }, ]; pathsWithTrailingSlash.forEach(({ path, subdir, expected }) => { const constructedPath = path.endsWith("/") ? `${path}${subdir}` : `${path}/${subdir}`; expect(constructedPath).toBe(expected); }); }); it("should validate host config with invalid port", () => { const invalidHost: SSHHostConfig = { host: "browseserver", hostName: "browse.example.com", port: 99999, // Invalid port }; const hostValidation = validateHostConfig(invalidHost); expect(hostValidation.valid).toBe(false); expect(hostValidation.error).toBeDefined(); expect(hostValidation.error).toContain("must be between 1 and 65535"); }); it("should handle missing hostname in host config", () => { const hostWithoutHostname: SSHHostConfig = { host: "browseserver", // hostName is optional, so this should still be valid }; const hostValidation = validateHostConfig(hostWithoutHostname); // Hostname is optional, so validation should pass expect(hostValidation.valid).toBe(true); }); describe("Remote file listing workflow", () => { it("should handle path validation before listing", () => { const remotePath = "/home/user/documents"; const remoteValidation = validateRemotePath(remotePath); expect(remoteValidation.valid).toBe(true); const hostValidation = validateHostConfig(mockHostConfig); expect(hostValidation.valid).toBe(true); // Both validations must pass before attempting to list files expect(remoteValidation.valid && hostValidation.valid).toBe(true); }); it("should handle home directory path", () => { const remotePath = "~"; const remoteValidation = validateRemotePath(remotePath); expect(remoteValidation.valid).toBe(true); }); it("should construct correct paths for file operations", () => { const basePath = "/home/user"; const fileName = "document.txt"; const filePath = basePath.endsWith("/") ? `${basePath}${fileName}` : `${basePath}/${fileName}`; expect(filePath).toBe("/home/user/document.txt"); }); it("should construct correct paths for directory operations", () => { const basePath = "/home/user"; const dirName = "documents"; const dirPath = basePath.endsWith("/") ? `${basePath}${dirName}` : `${basePath}/${dirName}`; expect(dirPath).toBe("/home/user/documents"); }); }); describe("Error handling in browse workflow", () => { it("should reject empty remote path", () => { const remoteValidation = validateRemotePath(""); expect(remoteValidation.valid).toBe(false); expect(remoteValidation.error).toBeDefined(); }); it("should reject remote path with only whitespace", () => { const remoteValidation = validateRemotePath(" "); expect(remoteValidation.valid).toBe(false); expect(remoteValidation.error).toBeDefined(); }); it("should handle invalid host configuration", () => { const invalidHost: SSHHostConfig = { host: "", // Empty host should fail hostName: "example.com", }; const hostValidation = validateHostConfig(invalidHost); expect(hostValidation.valid).toBe(false); }); }); describe("Path normalization", () => { it("should handle paths with multiple slashes", () => { // In real usage, paths might have multiple slashes that need normalization const remotePath = "/home//user///documents"; const remoteValidation = validateRemotePath(remotePath); // Validation should pass (normalization happens at SSH level) expect(remoteValidation.valid).toBe(true); }); it("should handle paths with dots", () => { const pathsWithDots = [ "/home/user/./documents", "/home/user/../documents", "/home/user/././documents", ]; pathsWithDots.forEach((path) => { const validation = validateRemotePath(path); expect(validation.valid).toBe(true); }); }); }); });