bitburner-src/test/jest/Terminal/Path.test.ts
Snarling e0272ad4af
FILES: Path rework & typesafety (#479)
* Added new types for various file paths, all in the Paths folder.
* TypeSafety and other helper functions related to these types
* Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands
* Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way
* Server.textFiles is now a map
* TextFile no longer uses a fn property, now it is filename
* Added a shared ContentFile interface for shared functionality between TextFile and Script.
* related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa.
* File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root.
* Singularized the MessageFilename and LiteratureName enums
* Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath).
* Fix several issues with tab completion, which included pretty much a complete rewrite
* Changed the autocomplete display options so there's less chance it clips outside the display area.
* Turned CompletedProgramName into an enum.
* Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine.
* For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 10:26:57 -04:00

284 lines
13 KiB
TypeScript

import { FilePath, isFilePath } from "../../../src/Paths/FilePath";
import {
Directory,
getFirstDirectoryInPath,
isAbsolutePath,
isDirectoryPath,
resolveDirectory,
} from "../../../src/Paths/Directory";
const validBaseDirectory = "foo/bar/";
if (!isDirectoryPath(validBaseDirectory) || !isAbsolutePath(validBaseDirectory)) {
throw new Error("The valid base directory was actually not valid.");
}
// Actual validation is in two separate functions as above, combining them here for simplicity in tests.
function isValidDirectory(name: string) {
return isAbsolutePath(name) && isDirectoryPath(name);
}
function isValidFilePath(name: string) {
return isAbsolutePath(name) && isFilePath(name);
}
describe("Terminal Directory Tests", function () {
describe("resolveDirectory()", function () {
it("Should fail when provided multiple leading slashes", function () {
expect(resolveDirectory("///")).toBe(null);
expect(resolveDirectory("//foo")).toBe(null);
});
it("should do nothing for valid directory path", function () {
expect(resolveDirectory("")).toBe("");
expect(resolveDirectory("foo/bar/")).toBe("foo/bar/");
});
it("should provide relative pathing", function () {
// The leading slash indicates an absolute path instead of relative.
expect(resolveDirectory("/", validBaseDirectory)).toBe("");
expect(resolveDirectory("./", validBaseDirectory)).toBe("foo/bar/");
expect(resolveDirectory("../", validBaseDirectory)).toBe("foo/");
expect(resolveDirectory("../../", validBaseDirectory)).toBe("");
expect(resolveDirectory("../../../", validBaseDirectory)).toBe(null);
expect(resolveDirectory("baz", validBaseDirectory)).toBe("foo/bar/baz/");
expect(resolveDirectory("baz/", validBaseDirectory)).toBe("foo/bar/baz/");
});
});
describe("isFilePath()", function () {
// Actual validation occurs in two steps, validating the filepath structure and then validating that it's not a relative path
it("should return true for valid filenames", function () {
expect(isFilePath("test.txt")).toBe(true);
expect(isFilePath("123.script")).toBe(true);
expect(isFilePath("foo123.b")).toBe(true);
expect(isFilePath("my_script.script")).toBe(true);
expect(isFilePath("my-script.script")).toBe(true);
expect(isFilePath("_foo.lit")).toBe(true);
expect(isFilePath("mult.periods.script")).toBe(true);
expect(isFilePath("mult.per-iods.again.script")).toBe(true);
expect(isFilePath("BruteSSH.exe-50%-INC")).toBe(true);
expect(isFilePath("DeepscanV1.exe-1.01%-INC")).toBe(true);
expect(isFilePath("DeepscanV2.exe-1.00%-INC")).toBe(true);
expect(isFilePath("AutoLink.exe-1.%-INC")).toBe(true);
});
it("should return false for invalid filenames", function () {
expect(isFilePath("foo")).toBe(false);
expect(isFilePath("my script.script")).toBe(false);
//expect(isFilePath("a^.txt")).toBe(false);
//expect(isFilePath("b#.lit")).toBe(false);
//expect(isFilePath("lib().js")).toBe(false);
//expect(isFilePath("foo.script_")).toBe(false);
//expect(isFilePath("foo._script")).toBe(false);
//expect(isFilePath("foo.hyphened-ext")).toBe(false);
expect(isFilePath("")).toBe(false);
//expect(isFilePath("AutoLink-1.%-INC.exe")).toBe(false);
//expect(isFilePath("AutoLink.exe-1.%-INC.exe")).toBe(false);
//expect(isFilePath("foo%.exe")).toBe(false);
//expect(isFilePath("-1.00%-INC")).toBe(false);
});
});
describe("isDirectoryPath()", function () {
it("should return true for valid directory names", function () {
expect(isValidDirectory("a/")).toBe(true);
expect(isValidDirectory("foo/")).toBe(true);
expect(isValidDirectory("foo-dir/")).toBe(true);
expect(isValidDirectory("foo_dir/")).toBe(true);
expect(isValidDirectory(".a/")).toBe(true);
expect(isValidDirectory("1/")).toBe(true);
expect(isValidDirectory("a1/")).toBe(true);
expect(isValidDirectory(".a1/")).toBe(true);
expect(isValidDirectory("._foo/")).toBe(true);
expect(isValidDirectory("_foo/")).toBe(true);
// the changes made to support this broke mv
// see https://github.com/danielyxie/bitburner/pull/3653 if you try to re support
expect(isValidDirectory("foo.dir/")).toBe(true);
expect(isValidDirectory("foov1.0.0.1/")).toBe(true);
expect(isValidDirectory("foov1..0..0..1/")).toBe(true);
expect(isValidDirectory("foov1-0-0-1/")).toBe(true);
expect(isValidDirectory("foov1-0-0-1-/")).toBe(true);
expect(isValidDirectory("foov1--0--0--1--/")).toBe(true);
expect(isValidDirectory("foov1_0_0_1/")).toBe(true);
expect(isValidDirectory("foov1_0_0_1_/")).toBe(true);
expect(isValidDirectory("foov1__0__0__1/")).toBe(true);
});
it("should return false for invalid directory names", function () {
// expect(isValidDirectory("👨‍💻/")).toBe(false);
// expect(isValidDirectory("dir#/")).toBe(false);
// expect(isValidDirectory("dir!/")).toBe(false);
expect(isValidDirectory("dir*/")).toBe(false);
expect(isValidDirectory("./")).toBe(false);
expect(isValidDirectory("../")).toBe(false);
// expect(isValidDirectory("1./")).toBe(false);
//expect(isValidDirectory("foo./")).toBe(false);
//expect(isValidDirectory("foov1.0.0.1./")).toBe(false);
});
});
describe("isValidDirectoryPath()", function () {
it("should return true only for the forward slash if the string has length 1", function () {
//expect(isValidDirectory("/")).toBe(true);
expect(isValidDirectory(" ")).toBe(false);
expect(isValidDirectory(".")).toBe(false);
expect(isValidDirectory("a")).toBe(false);
});
it("should return true for valid directory paths", function () {
expect(isValidDirectory("a/")).toBe(true);
expect(isValidDirectory("dir/a/")).toBe(true);
expect(isValidDirectory("dir/foo/")).toBe(true);
expect(isValidDirectory(".dir/foo-dir/")).toBe(true);
expect(isValidDirectory(".dir/foo_dir/")).toBe(true);
expect(isValidDirectory(".dir/.a/")).toBe(true);
expect(isValidDirectory("dir1/1/")).toBe(true);
expect(isValidDirectory("dir1/a1/")).toBe(true);
expect(isValidDirectory("dir1/.a1/")).toBe(true);
expect(isValidDirectory("dir_/._foo/")).toBe(true);
expect(isValidDirectory("dir-/_foo/")).toBe(true);
});
it("should return false if the path has a leading slash", function () {
expect(isValidDirectory("/a")).toBe(false);
expect(isValidDirectory("/dir/a")).toBe(false);
expect(isValidDirectory("/dir/foo")).toBe(false);
expect(isValidDirectory("/.dir/foo-dir")).toBe(false);
expect(isValidDirectory("/.dir/foo_dir")).toBe(false);
expect(isValidDirectory("/.dir/.a")).toBe(false);
expect(isValidDirectory("/dir1/1")).toBe(false);
expect(isValidDirectory("/dir1/a1")).toBe(false);
expect(isValidDirectory("/dir1/.a1")).toBe(false);
expect(isValidDirectory("/dir_/._foo")).toBe(false);
expect(isValidDirectory("/dir-/_foo")).toBe(false);
});
it("should accept dot notation", function () {
// These are relative paths so we will forego the absolute check
expect(isDirectoryPath("dir/./a/")).toBe(true);
expect(isDirectoryPath("dir/../foo/")).toBe(true);
expect(isDirectoryPath(".dir/./foo-dir/")).toBe(true);
expect(isDirectoryPath(".dir/../foo_dir/")).toBe(true);
expect(isDirectoryPath(".dir/./.a/")).toBe(true);
expect(isDirectoryPath("dir1/1/./")).toBe(true);
expect(isDirectoryPath("dir1/a1/../")).toBe(true);
expect(isDirectoryPath("dir1/.a1/../")).toBe(true);
expect(isDirectoryPath("dir_/._foo/./")).toBe(true);
expect(isDirectoryPath("./dir-/_foo/")).toBe(true);
expect(isDirectoryPath("../dir-/_foo/")).toBe(true);
});
});
describe("isValidFilePath()", function () {
it("should return false for strings that are too short", function () {
expect(isValidFilePath("/a")).toBe(false);
expect(isValidFilePath("a.")).toBe(false);
expect(isValidFilePath(".a")).toBe(false);
expect(isValidFilePath("/.")).toBe(false);
});
it("should return true for arguments that are just filenames", function () {
expect(isValidFilePath("test.txt")).toBe(true);
expect(isValidFilePath("123.script")).toBe(true);
expect(isValidFilePath("foo123.b")).toBe(true);
expect(isValidFilePath("my_script.script")).toBe(true);
expect(isValidFilePath("my-script.script")).toBe(true);
expect(isValidFilePath("_foo.lit")).toBe(true);
expect(isValidFilePath("mult.periods.script")).toBe(true);
expect(isValidFilePath("mult.per-iods.again.script")).toBe(true);
});
it("should return true for valid filepaths", function () {
// Some of these include relative paths, will not check absoluteness
expect(isFilePath("foo/test.txt")).toBe(true);
expect(isFilePath("../123.script")).toBe(true);
expect(isFilePath("./foo123.b")).toBe(true);
expect(isFilePath("dir/my_script.script")).toBe(true);
expect(isFilePath("dir1/dir2/dir3/my-script.script")).toBe(true);
expect(isFilePath("dir1/dir2/././../_foo.lit")).toBe(true);
expect(isFilePath(".dir1/./../.dir2/mult.periods.script")).toBe(true);
expect(isFilePath("_dir/../dir2/mult.per-iods.again.script")).toBe(true);
});
it("should return false for strings that begin with a slash", function () {
expect(isValidFilePath("/foo/foo.txt")).toBe(false);
expect(isValidFilePath("/foo.txt/bar.script")).toBe(false);
expect(isValidFilePath("/filename.ext")).toBe(false);
expect(isValidFilePath("/_dir/test.js")).toBe(false);
});
it("should return false for invalid arguments", function () {
expect(isValidFilePath(null as unknown as string)).toBe(false);
expect(isValidFilePath(undefined as unknown as string)).toBe(false);
expect(isValidFilePath(5 as unknown as string)).toBe(false);
expect(isValidFilePath({} as unknown as string)).toBe(false);
});
});
describe("getFirstDirectoryInPath()", function () {
// Strings cannot be passed in directly, so we'll wrap some typechecking
function firstDirectory(path: string): string | null | undefined {
if (!isAbsolutePath(path)) return undefined;
if (!isFilePath(path) && !isDirectoryPath(path)) return undefined;
return getFirstDirectoryInPath(path);
}
it("should return the first parent directory in a filepath", function () {
expect(firstDirectory("dir1/foo.txt")).toBe("dir1/");
expect(firstDirectory("dir1/dir2/dir3/dir4/foo.txt")).toBe("dir1/");
expect(firstDirectory("_dir1/dir2/foo.js")).toBe("_dir1/");
});
it("should return null if there is no first parent directory", function () {
expect(firstDirectory("")).toBe(null);
expect(firstDirectory(" ")).toBe(undefined); //Invalid path
expect(firstDirectory("/")).toBe(undefined); //Invalid path
expect(firstDirectory("//")).toBe(undefined); //Invalid path
expect(firstDirectory("foo.script")).toBe(null);
expect(firstDirectory("/foo.txt")).toBe(undefined); //Invalid path;
});
});
/*
describe("getAllParentDirectories()", function () {
const getAllParentDirectories = dirHelpers.getAllParentDirectories;
it("should return all parent directories in a filepath", function () {
expect(getAllParentDirectories("/")).toBe("/");
expect(getAllParentDirectories("/home/var/foo.txt")).toBe("/home/var/");
expect(getAllParentDirectories("/home/var/")).toBe("/home/var/");
expect(getAllParentDirectories("/home/var/test/")).toBe("/home/var/test/");
});
it("should return an empty string if there are no parent directories", function () {
expect(getAllParentDirectories("foo.txt")).toBe("");
});
});
describe("isInRootDirectory()", function () {
const isInRootDirectory = dirHelpers.isInRootDirectory;
it("should return true for filepaths that refer to a file in the root directory", function () {
expect(isInRootDirectory("a.b")).toBe(true);
expect(isInRootDirectory("foo.txt")).toBe(true);
expect(isInRootDirectory("/foo.txt")).toBe(true);
});
it("should return false for filepaths that refer to a file that's NOT in the root directory", function () {
expect(isInRootDirectory("/dir/foo.txt")).toBe(false);
expect(isInRootDirectory("dir/foo.txt")).toBe(false);
expect(isInRootDirectory("/./foo.js")).toBe(false);
expect(isInRootDirectory("../foo.js")).toBe(false);
expect(isInRootDirectory("/dir1/dir2/dir3/foo.txt")).toBe(false);
});
it("should return false for invalid inputs (inputs that aren't filepaths)", function () {
expect(isInRootDirectory(null as unknown as string)).toBe(false);
expect(isInRootDirectory(undefined as unknown as string)).toBe(false);
expect(isInRootDirectory("")).toBe(false);
expect(isInRootDirectory(" ")).toBe(false);
expect(isInRootDirectory("a")).toBe(false);
expect(isInRootDirectory("/dir")).toBe(false);
expect(isInRootDirectory("/dir/")).toBe(false);
expect(isInRootDirectory("/dir/foo")).toBe(false);
});
});
*/
});