mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-01 20:13:51 +01:00
JEST: Add tests for b1tflum3 and destroyW0r1dD43m0n API (#1802)
This commit is contained in:
parent
9d8ac65aaf
commit
bb0b857f71
@ -10,6 +10,7 @@ import { Page } from "./ui/Router";
|
|||||||
import { prestigeSourceFile } from "./Prestige";
|
import { prestigeSourceFile } from "./Prestige";
|
||||||
import { getDefaultBitNodeOptions, setBitNodeOptions } from "./BitNode/BitNodeUtils";
|
import { getDefaultBitNodeOptions, setBitNodeOptions } from "./BitNode/BitNodeUtils";
|
||||||
import { prestigeWorkerScripts } from "./NetscriptWorker";
|
import { prestigeWorkerScripts } from "./NetscriptWorker";
|
||||||
|
import { exceptionAlert } from "./utils/helpers/exceptionAlert";
|
||||||
|
|
||||||
function giveSourceFile(bitNodeNumber: number): void {
|
function giveSourceFile(bitNodeNumber: number): void {
|
||||||
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
||||||
@ -76,14 +77,7 @@ export function enterBitNode(
|
|||||||
try {
|
try {
|
||||||
setBitNodeOptions(bitNodeOptions);
|
setBitNodeOptions(bitNodeOptions);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dialogBoxCreate(
|
exceptionAlert(error);
|
||||||
<>
|
|
||||||
Invalid BitNode options. This is a bug. Please report it to developers.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{error instanceof Error ? error.stack : String(error)}
|
|
||||||
</>,
|
|
||||||
);
|
|
||||||
// Use default options
|
// Use default options
|
||||||
setBitNodeOptions(getDefaultBitNodeOptions());
|
setBitNodeOptions(getDefaultBitNodeOptions());
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,25 @@ export function GetServer(s: string): BaseServer | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In our codebase, we usually have to call GetServer() like this:
|
||||||
|
* ```
|
||||||
|
* const server = GetServer(hostname);
|
||||||
|
* if (!server) {
|
||||||
|
* throw new Error("Error message");
|
||||||
|
* }
|
||||||
|
* // Use server
|
||||||
|
* ```
|
||||||
|
* With this utility function, we don't need to write boilerplate code.
|
||||||
|
*/
|
||||||
|
export function GetServerOrThrow(serverId: string): BaseServer {
|
||||||
|
const server = GetServer(serverId);
|
||||||
|
if (!server) {
|
||||||
|
throw new Error(`Server ${serverId} does not exist.`);
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
//Get server by IP or hostname. Returns null if invalid or unreachable.
|
//Get server by IP or hostname. Returns null if invalid or unreachable.
|
||||||
export function GetReachableServer(s: string): BaseServer | null {
|
export function GetReachableServer(s: string): BaseServer | null {
|
||||||
const server = GetServer(s);
|
const server = GetServer(s);
|
||||||
|
@ -13,9 +13,9 @@ declare const importActual: (typeof EvaluatorConfig)["doImport"];
|
|||||||
// Replace Blob/ObjectURL functions, because they don't work natively in Jest
|
// Replace Blob/ObjectURL functions, because they don't work natively in Jest
|
||||||
global.Blob = class extends Blob {
|
global.Blob = class extends Blob {
|
||||||
code: string;
|
code: string;
|
||||||
constructor(blobParts?: BlobPart[], options?: BlobPropertyBag) {
|
constructor(blobParts?: BlobPart[], __options?: BlobPropertyBag) {
|
||||||
super();
|
super();
|
||||||
this.code = (blobParts ?? [])[0] + "";
|
this.code = String((blobParts ?? [])[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
global.URL.revokeObjectURL = function () {};
|
global.URL.revokeObjectURL = function () {};
|
||||||
@ -77,11 +77,11 @@ test.each([
|
|||||||
},
|
},
|
||||||
])("Netscript execution: $name", async function ({ expected: expectedLog, scripts }) {
|
])("Netscript execution: $name", async function ({ expected: expectedLog, scripts }) {
|
||||||
global.URL.createObjectURL = function (blob) {
|
global.URL.createObjectURL = function (blob) {
|
||||||
return "data:text/javascript," + encodeURIComponent(blob.code);
|
return "data:text/javascript," + encodeURIComponent((blob as unknown as { code: string }).code);
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = {} as Server;
|
let server = {} as Server;
|
||||||
let eventDelete = () => {};
|
const eventDelete = () => {};
|
||||||
let alertDelete = () => {};
|
let alertDelete = () => {};
|
||||||
try {
|
try {
|
||||||
const alerted = new Promise((resolve) => {
|
const alerted = new Promise((resolve) => {
|
||||||
@ -98,7 +98,7 @@ test.each([
|
|||||||
|
|
||||||
const ramUsage = script.getRamUsage(server.scripts);
|
const ramUsage = script.getRamUsage(server.scripts);
|
||||||
if (!ramUsage) throw new Error(`ramUsage calculated to be ${ramUsage}`);
|
if (!ramUsage) throw new Error(`ramUsage calculated to be ${ramUsage}`);
|
||||||
const runningScript = new RunningScript(script, ramUsage as number);
|
const runningScript = new RunningScript(script, ramUsage);
|
||||||
const pid = startWorkerScript(runningScript, server);
|
const pid = startWorkerScript(runningScript, server);
|
||||||
expect(pid).toBeGreaterThan(0);
|
expect(pid).toBeGreaterThan(0);
|
||||||
// Manually attach an atExit to the now-created WorkerScript, so we can
|
// Manually attach an atExit to the now-created WorkerScript, so we can
|
||||||
@ -107,6 +107,7 @@ test.each([
|
|||||||
expect(ws).toBeDefined();
|
expect(ws).toBeDefined();
|
||||||
const result = await Promise.race([
|
const result = await Promise.race([
|
||||||
alerted,
|
alerted,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ws was asserted above
|
||||||
new Promise<void>((resolve) => (ws!.atExit = new Map([["default", resolve]]))),
|
new Promise<void>((resolve) => (ws!.atExit = new Map([["default", resolve]]))),
|
||||||
]);
|
]);
|
||||||
// If an error alert was thrown, we catch it here.
|
// If an error alert was thrown, we catch it here.
|
||||||
|
221
test/jest/Netscript/Singularity.test.ts
Normal file
221
test/jest/Netscript/Singularity.test.ts
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
import { installAugmentations } from "../../../src/Augmentation/AugmentationHelpers";
|
||||||
|
import { blackOpsArray } from "../../../src/Bladeburner/data/BlackOperations";
|
||||||
|
import { AugmentationName } from "../../../src/Enums";
|
||||||
|
import { WorkerScript } from "../../../src/Netscript/WorkerScript";
|
||||||
|
import { NetscriptFunctions, type NSFull } from "../../../src/NetscriptFunctions";
|
||||||
|
import type { ScriptFilePath } from "../../../src/Paths/ScriptFilePath";
|
||||||
|
import { PlayerObject } from "../../../src/PersonObjects/Player/PlayerObject";
|
||||||
|
import { Player, setPlayer } from "../../../src/Player";
|
||||||
|
import { RunningScript } from "../../../src/Script/RunningScript";
|
||||||
|
import { GetServerOrThrow, initForeignServers, prestigeAllServers } from "../../../src/Server/AllServers";
|
||||||
|
import { SpecialServers } from "../../../src/Server/data/SpecialServers";
|
||||||
|
import { initSourceFiles } from "../../../src/SourceFile/SourceFiles";
|
||||||
|
import { FormatsNeedToChange } from "../../../src/ui/formatNumber";
|
||||||
|
import { Router } from "../../../src/ui/GameRoot";
|
||||||
|
|
||||||
|
function setupBasicTestingEnvironment(): void {
|
||||||
|
prestigeAllServers();
|
||||||
|
setPlayer(new PlayerObject());
|
||||||
|
Player.init();
|
||||||
|
Player.sourceFiles.set(4, 3);
|
||||||
|
initForeignServers(Player.getHomeComputer());
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNumBlackOpsComplete(value: number): void {
|
||||||
|
if (!Player.bladeburner) {
|
||||||
|
throw new Error("Invalid Bladeburner data");
|
||||||
|
}
|
||||||
|
Player.bladeburner.numBlackOpsComplete = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNS(): NSFull {
|
||||||
|
const home = GetServerOrThrow(SpecialServers.Home);
|
||||||
|
home.maxRam = 1024;
|
||||||
|
const filePath = "test.js" as ScriptFilePath;
|
||||||
|
home.writeToScriptFile(filePath, "");
|
||||||
|
const script = home.scripts.get(filePath);
|
||||||
|
if (!script) {
|
||||||
|
throw new Error("Invalid script");
|
||||||
|
}
|
||||||
|
const runningScript = new RunningScript(script, 1024);
|
||||||
|
const workerScript = new WorkerScript(runningScript, 1, NetscriptFunctions);
|
||||||
|
const ns = workerScript.env.vars;
|
||||||
|
if (!ns) {
|
||||||
|
throw new Error("Invalid NS instance");
|
||||||
|
}
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to patch this function. Some APIs call it, but it only works properly after the main UI is loaded.
|
||||||
|
Router.toPage = () => {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In src\ui\formatNumber.ts, there are some variables that need to be initialized before other functions can be
|
||||||
|
* called. We have to call FormatsNeedToChange.emit() to initialize those variables.
|
||||||
|
*/
|
||||||
|
FormatsNeedToChange.emit();
|
||||||
|
|
||||||
|
initSourceFiles();
|
||||||
|
|
||||||
|
const nextBN = 3;
|
||||||
|
|
||||||
|
describe("b1tflum3", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupBasicTestingEnvironment();
|
||||||
|
Player.queueAugmentation(AugmentationName.TheRedPill);
|
||||||
|
installAugmentations();
|
||||||
|
Player.gainHackingExp(1e100);
|
||||||
|
const wdServer = GetServerOrThrow(SpecialServers.WorldDaemon);
|
||||||
|
wdServer.hasAdminRights = true;
|
||||||
|
});
|
||||||
|
// Make sure that the player is in the next BN without SF rewards.
|
||||||
|
const expectSucceedInB1tflum3 = () => {
|
||||||
|
expect(Player.bitNodeN).toStrictEqual(nextBN);
|
||||||
|
expect(Player.augmentations.length).toStrictEqual(0);
|
||||||
|
expect(Player.sourceFileLvl(1)).toStrictEqual(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Success", () => {
|
||||||
|
test("Without BN options", () => {
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.b1tflum3(nextBN);
|
||||||
|
expectSucceedInB1tflum3();
|
||||||
|
});
|
||||||
|
test("With BN options", () => {
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.b1tflum3(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
sourceFileOverrides: new Map(),
|
||||||
|
intelligenceOverride: 1,
|
||||||
|
});
|
||||||
|
expectSucceedInB1tflum3();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Failure", () => {
|
||||||
|
// Make sure that the player is still in the same BN without SF rewards.
|
||||||
|
const expectFailToB1tflum3 = () => {
|
||||||
|
expect(Player.bitNodeN).toStrictEqual(1);
|
||||||
|
expect(Player.augmentations.length).toStrictEqual(1);
|
||||||
|
expect(Player.sourceFileLvl(1)).toStrictEqual(0);
|
||||||
|
};
|
||||||
|
test("Invalid intelligenceOverride", () => {
|
||||||
|
const ns = getNS();
|
||||||
|
expect(() => {
|
||||||
|
ns.singularity.b1tflum3(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
intelligenceOverride: -1,
|
||||||
|
});
|
||||||
|
}).toThrow();
|
||||||
|
expectFailToB1tflum3();
|
||||||
|
});
|
||||||
|
test("Invalid sourceFileOverrides", () => {
|
||||||
|
const ns = getNS();
|
||||||
|
expect(() => {
|
||||||
|
ns.singularity.b1tflum3(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
sourceFileOverrides: [] as unknown as Map<number, number>,
|
||||||
|
});
|
||||||
|
}).toThrow();
|
||||||
|
expectFailToB1tflum3();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("destroyW0r1dD43m0n", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupBasicTestingEnvironment();
|
||||||
|
Player.queueAugmentation(AugmentationName.TheRedPill);
|
||||||
|
installAugmentations();
|
||||||
|
Player.gainHackingExp(1e100);
|
||||||
|
const wdServer = GetServerOrThrow(SpecialServers.WorldDaemon);
|
||||||
|
wdServer.hasAdminRights = true;
|
||||||
|
Player.startBladeburner();
|
||||||
|
setNumBlackOpsComplete(blackOpsArray.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Success", () => {
|
||||||
|
// Make sure that the player is in the next BN and received SF rewards.
|
||||||
|
const expectSucceedInDestroyingWD = () => {
|
||||||
|
expect(Player.bitNodeN).toStrictEqual(nextBN);
|
||||||
|
expect(Player.augmentations.length).toStrictEqual(0);
|
||||||
|
expect(Player.sourceFileLvl(1)).toStrictEqual(1);
|
||||||
|
};
|
||||||
|
test("Hacking route", () => {
|
||||||
|
setNumBlackOpsComplete(0);
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN);
|
||||||
|
expectSucceedInDestroyingWD();
|
||||||
|
});
|
||||||
|
test("Hacking route with BN options", () => {
|
||||||
|
setNumBlackOpsComplete(0);
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
sourceFileOverrides: new Map(),
|
||||||
|
intelligenceOverride: 1,
|
||||||
|
});
|
||||||
|
expectSucceedInDestroyingWD();
|
||||||
|
});
|
||||||
|
test("Bladeburner route", () => {
|
||||||
|
Player.skills.hacking = 0;
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN);
|
||||||
|
expectSucceedInDestroyingWD();
|
||||||
|
});
|
||||||
|
test("Bladeburner route with BN options", () => {
|
||||||
|
Player.skills.hacking = 0;
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
sourceFileOverrides: new Map(),
|
||||||
|
intelligenceOverride: 1,
|
||||||
|
});
|
||||||
|
expectSucceedInDestroyingWD();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Failure", () => {
|
||||||
|
// Make sure that the player is still in the same BN without SF rewards.
|
||||||
|
const expectFailToDestroyWD = () => {
|
||||||
|
expect(Player.bitNodeN).toStrictEqual(1);
|
||||||
|
expect(Player.augmentations.length).toStrictEqual(1);
|
||||||
|
expect(Player.sourceFileLvl(1)).toStrictEqual(0);
|
||||||
|
};
|
||||||
|
test("Do not have enough hacking level and numBlackOpsComplete", () => {
|
||||||
|
Player.skills.hacking = 0;
|
||||||
|
setNumBlackOpsComplete(0);
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN);
|
||||||
|
expectFailToDestroyWD();
|
||||||
|
});
|
||||||
|
test("Do not have admin rights on WD and do not have enough numBlackOpsComplete", () => {
|
||||||
|
const wdServer = GetServerOrThrow(SpecialServers.WorldDaemon);
|
||||||
|
wdServer.hasAdminRights = false;
|
||||||
|
setNumBlackOpsComplete(0);
|
||||||
|
const ns = getNS();
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN);
|
||||||
|
expectFailToDestroyWD();
|
||||||
|
});
|
||||||
|
test("Invalid intelligenceOverride", () => {
|
||||||
|
const ns = getNS();
|
||||||
|
expect(() => {
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
intelligenceOverride: -1,
|
||||||
|
});
|
||||||
|
}).toThrow();
|
||||||
|
expectFailToDestroyWD();
|
||||||
|
});
|
||||||
|
test("Invalid sourceFileOverrides", () => {
|
||||||
|
const ns = getNS();
|
||||||
|
expect(() => {
|
||||||
|
ns.singularity.destroyW0r1dD43m0n(nextBN, undefined, {
|
||||||
|
...ns.getResetInfo().bitNodeOptions,
|
||||||
|
sourceFileOverrides: [] as unknown as Map<number, number>,
|
||||||
|
});
|
||||||
|
}).toThrow();
|
||||||
|
expectFailToDestroyWD();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user