From 9dd68947f1dcbc3f77fec82581a27b7b7349eaf6 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Fri, 10 May 2019 02:24:50 -0700 Subject: [PATCH] Added Dynamic RAM calculation unit tests --- .gitignore | 6 +- src/Netscript/WorkerScript.ts | 2 +- src/NetscriptFunctions.js | 2 +- .../Player/PlayerObjectGeneralMethods.js | 11 +- src/Prestige.js | 2 +- test/Netscript/DynamicRamCalculationTests.js | 1185 ++++++++++++++++- test/Netscript/StaticRamCalculationTests.js | 2 +- test/StockMarketTests.js | 246 +++- test/index.js | 2 +- 9 files changed, 1436 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 84fbc1448..c71e11d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,6 @@ Netburner.txt /doc/build /node_modules /dist/*.map -/tests/*.map -/tests/*.bundle.* -/tests/*.css +/test/*.map +/test/*.bundle.* +/test/*.css diff --git a/src/Netscript/WorkerScript.ts b/src/Netscript/WorkerScript.ts index 556348679..2549c06e0 100644 --- a/src/Netscript/WorkerScript.ts +++ b/src/Netscript/WorkerScript.ts @@ -76,7 +76,7 @@ export class WorkerScript { output: string = ""; /** - * Script's RAM usage. Equivalent to underlying script's RAM usage + * Script's Static RAM usage. Equivalent to underlying script's RAM usage */ ramUsage: number = 0; diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 58b9ca56e..bbdd29b4a 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -4346,7 +4346,7 @@ function NetscriptFunctions(workerScript) { if (Player.bitNodeN !== 10 && !SourceFileFlags[10]) { throw makeRuntimeRejectMsg(workerScript, "getStats() failed because you do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10"); } - updateDynamicRam("workoutAtGym", getRamCost("sleeve", "getSleeveStats")); + updateDynamicRam("getSleeveStats", getRamCost("sleeve", "getSleeveStats")); if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) { workerScript.log(`ERROR: sleeve.workoutAtGym(${sleeveNumber}) failed because it is an invalid sleeve number.`); return false; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.js b/src/PersonObjects/Player/PlayerObjectGeneralMethods.js index 6b310e58f..56ebdd0af 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.js +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.js @@ -359,21 +359,24 @@ export function hasProgram(programName) { export function setMoney(money) { if (isNaN(money)) { - console.log("ERR: NaN passed into Player.setMoney()"); return; + console.error("NaN passed into Player.setMoney()"); + return; } - this.money = money; + this.money = new Decimal(money); } export function gainMoney(money) { if (isNaN(money)) { - console.log("ERR: NaN passed into Player.gainMoney()"); return; + console.error("NaN passed into Player.gainMoney()"); + return; } this.money = this.money.plus(money); } export function loseMoney(money) { if (isNaN(money)) { - console.log("ERR: NaN passed into Player.loseMoney()"); return; + console.error("NaN passed into Player.loseMoney()"); + return; } this.money = this.money.minus(money); } diff --git a/src/Prestige.js b/src/Prestige.js index f04885eb9..ba0ba2755 100755 --- a/src/Prestige.js +++ b/src/Prestige.js @@ -97,7 +97,7 @@ function prestigeAugmentation() { } if (augmentationExists(AugmentationNames.CashRoot) && Augmentations[AugmentationNames.CashRoot].owned) { - Player.setMoney(new Decimal(1000000)); + Player.setMoney(1e6); homeComp.programs.push(Programs.BruteSSHProgram.name); } diff --git a/test/Netscript/DynamicRamCalculationTests.js b/test/Netscript/DynamicRamCalculationTests.js index edaf2360e..d242b7c1f 100644 --- a/test/Netscript/DynamicRamCalculationTests.js +++ b/test/Netscript/DynamicRamCalculationTests.js @@ -1,11 +1,1186 @@ -// TODO Importing NetscriptFunctions breaks the mochapack build -// import { NetscriptFunctions } from "../../src/NetscriptFunctions"; +import { NetscriptFunctions } from "../../src/NetscriptFunctions"; +import { getRamCost, RamCostConstants } from "../../src/Netscript/RamCostGenerator"; +import { Environment } from "../../src/Netscript/Environment"; +import { RunningScript } from "../../src/Script/RunningScript"; +import { Script } from "../../src/Script/Script"; +import { SourceFileFlags } from "../../src/SourceFile/SourceFileFlags"; + import { expect } from "chai"; console.log("Beginning Netscript Dynamic RAM Calculation/Generation Tests"); -describe("Netscript Static RAM Calculation/Generation Tests", function() { - it("should run", function() { - expect(1).to.equal(1); +console.log("Beginning Netscript Static RAM Calculation/Generation Tests"); + +const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost; + +describe("Netscript Dynamic RAM Calculation/Generation Tests", function() { + // Creates a mock RunningScript object + async function createRunningScript(code) { + const script = new Script(); + script.code = code; + await script.updateRamUsage([]); + + const runningScript = new RunningScript(script); + + return runningScript; + } + + // Tests numeric equality, allowing for floating point imprecision + function testEquality(val, expected) { + expect(val).to.be.within(expected - 100 * Number.EPSILON, expected + 100 * Number.EPSILON); + } + + // Runs a Netscript function and properly catches it if it returns promise + function runPotentiallyAsyncFunction(fn) { + let res = fn(); + if (res instanceof Promise) { + res.catch(() => {}); + } + } + + /** + * Tests that: + * 1. A function has non-zero RAM cost + * 2. Running the function properly updates the MockWorkerScript's dynamic RAM calculation + * 3. Running multiple calls of the function does not result in additional RAM cost + * @param {string[]} fnDesc - describes the name of the function being tested, + * including the namespace(s). e.g. ["gang", "getMemberNames"] + */ + async function testNonzeroDynamicRamCost(fnDesc) { + if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to testNonzeroDynamicRamCost()"); } + const expected = getRamCost(...fnDesc); + expect(expected).to.be.above(0); + + const code = `${fnDesc.join(".")}();` + + const runningScript = await createRunningScript(code); + + // We don't need a real WorkerScript + const workerScript = { + args: [], + code: code, + dynamicLoadedFns: {}, + dynamicRamUsage: RamCostConstants.ScriptBaseRamCost, + env: new Environment(null), + ramUsage: runningScript.ramUsage, + scriptRef: runningScript, + } + workerScript.env.vars = NetscriptFunctions(workerScript); + + // Run the function through the workerscript's args + const scope = workerScript.env.vars; + let curr = scope[fnDesc[0]]; + for (let i = 1; i < fnDesc.length; ++i) { + if (curr == null) { + expect.fail(`Invalid function specified: [${fnDesc}]`); + } + + if (typeof curr === "function") { + break; + } + + curr = curr[fnDesc[i]]; + } + + if (typeof curr === "function") { + // We use a try/catch because the function will probably fail since the game isn't + // actually initialized. Call the fn multiple times to test that repeated calls + // do not incur extra RAM costs. + try { + runPotentiallyAsyncFunction(curr); + runPotentiallyAsyncFunction(curr); + runPotentiallyAsyncFunction(curr); + } catch(e) {} + } else { + expect.fail(`Invalid function specified: [${fndesc}]`); + } + + const fnName = fnDesc[fnDesc.length - 1]; + testEquality(workerScript.dynamicRamUsage - ScriptBaseCost, expected); + testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage); + expect(workerScript.dynamicLoadedFns).to.have.property(fnName); + } + + /** + * Tests that: + * 1. A function has zero RAM cost + * 2. Running the function does NOT update the MockWorkerScript's dynamic RAM calculation + * 3. Running multiple calls of the function does not result in dynamic RAM calculation + * @param {string[]} fnDesc - describes the name of the function being tested, + * including the namespace(s). e.g. ["gang", "getMemberNames"] + */ + async function testZeroDynamicRamCost(fnDesc) { + if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to testZeroDynamicRamCost()"); } + const expected = getRamCost(...fnDesc); + expect(expected).to.equal(0); + + const code = `${fnDesc.join(".")}();` + + const runningScript = await createRunningScript(code); + + // We don't need a real WorkerScript + const workerScript = { + args: [], + code: code, + dynamicLoadedFns: {}, + dynamicRamUsage: RamCostConstants.ScriptBaseRamCost, + env: new Environment(null), + ramUsage: runningScript.ramUsage, + scriptRef: runningScript, + } + workerScript.env.vars = NetscriptFunctions(workerScript); + + // Run the function through the workerscript's args + const scope = workerScript.env.vars; + let curr = scope[fnDesc[0]]; + for (let i = 1; i < fnDesc.length; ++i) { + if (curr == null) { + expect.fail(`Invalid function specified: [${fnDesc}]`); + } + + if (typeof curr === "function") { + break; + } + + curr = curr[fnDesc[i]]; + } + + if (typeof curr === "function") { + // We use a try/catch because the function will probably fail since the game isn't + // actually initialized. Call the fn multiple times to test that repeated calls + // do not incur extra RAM costs. + try { + runPotentiallyAsyncFunction(curr); + runPotentiallyAsyncFunction(curr); + runPotentiallyAsyncFunction(curr); + } catch(e) {} + } else { + expect.fail(`Invalid function specified: [${fndesc}]`); + } + + testEquality(workerScript.dynamicRamUsage, ScriptBaseCost); + testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage); + } + + before(function() { + for (let i = 0; i < SourceFileFlags.length; ++i) { + SourceFileFlags[i] = 3; + } + }); + + describe("Basic Functions", async function() { + it("hack()", async function() { + const f = ["hack"]; + await testNonzeroDynamicRamCost(f); + }); + + it("grow()", async function() { + const f = ["grow"]; + await testNonzeroDynamicRamCost(f); + }); + + it("weaken()", async function() { + const f = ["weaken"]; + await testNonzeroDynamicRamCost(f); + }); + + it("hackAnalyzeThreads()", async function() { + const f = ["hackAnalyzeThreads"]; + await testNonzeroDynamicRamCost(f); + }); + + it("hackAnalyzePercent()", async function() { + const f = ["hackAnalyzePercent"]; + await testNonzeroDynamicRamCost(f); + }); + + it("hackChance()", async function() { + const f = ["hackChance"]; + await testNonzeroDynamicRamCost(f); + }); + + it("growthAnalyze()", async function() { + const f = ["growthAnalyze"]; + await testNonzeroDynamicRamCost(f); + }); + + it("sleep()", async function() { + const f = ["sleep"]; + await testZeroDynamicRamCost(f); + }); + + it("print()", async function() { + const f = ["print"]; + await testZeroDynamicRamCost(f); + }); + + it("tprint()", async function() { + const f = ["tprint"]; + await testZeroDynamicRamCost(f); + }); + + it("clearLog()", async function() { + const f = ["clearLog"]; + await testZeroDynamicRamCost(f); + }); + + it("disableLog()", async function() { + const f = ["disableLog"]; + await testZeroDynamicRamCost(f); + }); + + it("enableLog()", async function() { + const f = ["enableLog"]; + await testZeroDynamicRamCost(f); + }); + + it("isLogEnabled()", async function() { + const f = ["isLogEnabled"]; + await testZeroDynamicRamCost(f); + }); + + it("getScriptLogs()", async function() { + const f = ["getScriptLogs"]; + await testZeroDynamicRamCost(f); + }); + + it("scan()", async function() { + const f = ["scan"]; + await testNonzeroDynamicRamCost(f); + }); + + it("nuke()", async function() { + const f = ["nuke"]; + await testNonzeroDynamicRamCost(f); + }); + + it("brutessh()", async function() { + const f = ["brutessh"]; + await testNonzeroDynamicRamCost(f); + }); + + it("ftpcrack()", async function() { + const f = ["ftpcrack"]; + await testNonzeroDynamicRamCost(f); + }); + + it("relaysmtp()", async function() { + const f = ["relaysmtp"]; + await testNonzeroDynamicRamCost(f); + }); + + it("httpworm()", async function() { + const f = ["httpworm"]; + await testNonzeroDynamicRamCost(f); + }); + + it("sqlinject()", async function() { + const f = ["sqlinject"]; + await testNonzeroDynamicRamCost(f); + }); + + it("run()", async function() { + const f = ["run"]; + await testNonzeroDynamicRamCost(f); + }); + + it("exec()", async function() { + const f = ["exec"]; + await testNonzeroDynamicRamCost(f); + }); + + it("spawn()", async function() { + const f = ["spawn"]; + await testNonzeroDynamicRamCost(f); + }); + + it("kill()", async function() { + const f = ["kill"]; + await testNonzeroDynamicRamCost(f); + }); + + it("killall()", async function() { + const f = ["killall"]; + await testNonzeroDynamicRamCost(f); + }); + + it("exit()", async function() { + const f = ["exit"]; + await testZeroDynamicRamCost(f); + }); + + it("scp()", async function() { + const f = ["scp"]; + await testNonzeroDynamicRamCost(f); + }); + + it("ls()", async function() { + const f = ["ls"]; + await testNonzeroDynamicRamCost(f); + }); + + it("ps()", async function() { + const f = ["ps"]; + await testNonzeroDynamicRamCost(f); + }); + + it("hasRootAccess()", async function() { + const f = ["hasRootAccess"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getHostname()", async function() { + const f = ["getHostname"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getHackingLevel()", async function() { + const f = ["getHackingLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getHackingMultipliers()", async function() { + const f = ["getHackingMultipliers"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getHacknetMultipliers()", async function() { + const f = ["getHacknetMultipliers"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerMoneyAvailable()", async function() { + const f = ["getServerMoneyAvailable"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerMaxMoney()", async function() { + const f = ["getServerMaxMoney"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerGrowth()", async function() { + const f = ["getServerGrowth"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerSecurityLevel()", async function() { + const f = ["getServerSecurityLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerBaseSecurityLevel()", async function() { + const f = ["getServerBaseSecurityLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerMinSecurityLevel()", async function() { + const f = ["getServerMinSecurityLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerRequiredHackingLevel()", async function() { + const f = ["getServerRequiredHackingLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerNumPortsRequired()", async function() { + const f = ["getServerNumPortsRequired"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getServerRam()", async function() { + const f = ["getServerRam"]; + await testNonzeroDynamicRamCost(f); + }); + + it("serverExists()", async function() { + const f = ["serverExists"]; + await testNonzeroDynamicRamCost(f); + }); + + it("fileExists()", async function() { + const f = ["fileExists"]; + await testNonzeroDynamicRamCost(f); + }); + + it("isRunning()", async function() { + const f = ["isRunning"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getPurchasedServerCost()", async function() { + const f = ["getPurchasedServerCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchaseServer()", async function() { + const f = ["purchaseServer"]; + await testNonzeroDynamicRamCost(f); + }); + + it("deleteServer()", async function() { + const f = ["deleteServer"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getPurchasedServers()", async function() { + const f = ["getPurchasedServers"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getPurchasedServerLimit()", async function() { + const f = ["getPurchasedServerLimit"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getPurchasedServerMaxRam()", async function() { + const f = ["getPurchasedServerMaxRam"]; + await testNonzeroDynamicRamCost(f); + }); + + it("write()", async function() { + const f = ["write"]; + await testNonzeroDynamicRamCost(f); + }); + + it("tryWrite()", async function() { + const f = ["tryWrite"]; + await testNonzeroDynamicRamCost(f); + }); + + it("read()", async function() { + const f = ["read"]; + await testNonzeroDynamicRamCost(f); + }); + + it("peek()", async function() { + const f = ["peek"]; + await testNonzeroDynamicRamCost(f); + }); + + it("clear()", async function() { + const f = ["clear"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getPortHandle()", async function() { + const f = ["getPortHandle"]; + await testNonzeroDynamicRamCost(f); + }); + + it("rm()", async function() { + const f = ["rm"]; + await testNonzeroDynamicRamCost(f); + }); + + it("scriptRunning()", async function() { + const f = ["scriptRunning"]; + await testNonzeroDynamicRamCost(f); + }); + + it("scriptKill()", async function() { + const f = ["scriptKill"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getScriptName()", async function() { + const f = ["getScriptName"]; + await testZeroDynamicRamCost(f); + }); + + it("getScriptRam()", async function() { + const f = ["getScriptRam"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getHackTime()", async function() { + const f = ["getHackTime"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getGrowTime()", async function() { + const f = ["getGrowTime"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getWeakenTime()", async function() { + const f = ["getWeakenTime"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getScriptIncome()", async function() { + const f = ["getScriptIncome"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getScriptExpGain()", async function() { + const f = ["getScriptExpGain"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getTimeSinceLastAug()", async function() { + const f = ["getTimeSinceLastAug"]; + await testNonzeroDynamicRamCost(f); + }); + + it("sprintf()", async function() { + const f = ["sprintf"]; + await testZeroDynamicRamCost(f); + }); + + it("vsprintf()", async function() { + const f = ["vsprintf"]; + await testZeroDynamicRamCost(f); + }); + + it("nFormat()", async function() { + const f = ["nFormat"]; + await testZeroDynamicRamCost(f); + }); + + it("prompt()", async function() { + const f = ["prompt"]; + await testZeroDynamicRamCost(f); + }); + + it("wget()", async function() { + const f = ["wget"]; + await testZeroDynamicRamCost(f); + }); + + it("getFavorToDonate()", async function() { + const f = ["getFavorToDonate"]; + await testNonzeroDynamicRamCost(f); + }); + }); + + describe("Advanced Functions", async function() { + it("getBitNodeMultipliers()", async function() { + const f = ["getBitNodeMultipliers"]; + await testNonzeroDynamicRamCost(f); + }); + }); + + describe("TIX API", async function() { + it("getStockSymbols()", async function() { + const f = ["getStockSymbols"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockPrice()", async function() { + const f = ["getStockPrice"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockAskPrice()", async function() { + const f = ["getStockAskPrice"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockBidPrice()", async function() { + const f = ["getStockBidPrice"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockPosition()", async function() { + const f = ["getStockPosition"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockMaxShares()", async function() { + const f = ["getStockMaxShares"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockPurchaseCost()", async function() { + const f = ["getStockPurchaseCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockSaleGain()", async function() { + const f = ["getStockSaleGain"]; + await testNonzeroDynamicRamCost(f); + }); + + it("buyStock()", async function() { + const f = ["buyStock"]; + await testNonzeroDynamicRamCost(f); + }); + + it("sellStock()", async function() { + const f = ["sellStock"]; + await testNonzeroDynamicRamCost(f); + }); + + it("shortStock()", async function() { + const f = ["shortStock"]; + await testNonzeroDynamicRamCost(f); + }); + + it("sellShort()", async function() { + const f = ["sellShort"]; + await testNonzeroDynamicRamCost(f); + }); + + it("placeOrder()", async function() { + const f = ["placeOrder"]; + await testNonzeroDynamicRamCost(f); + }); + + it("cancelOrder()", async function() { + const f = ["cancelOrder"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getOrders()", async function() { + const f = ["getOrders"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockVolatility()", async function() { + const f = ["getStockVolatility"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStockForecast()", async function() { + const f = ["getStockForecast"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchase4SMarketData()", async function() { + const f = ["purchase4SMarketData"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchase4SMarketDataTixApi()", async function() { + const f = ["purchase4SMarketDataTixApi"]; + await testNonzeroDynamicRamCost(f); + }); + }); + + describe("Singularity Functions", async function() { + it("universityCourse()", async function() { + const f = ["universityCourse"]; + await testNonzeroDynamicRamCost(f); + }); + + it("gymWorkout()", async function() { + const f = ["gymWorkout"]; + await testNonzeroDynamicRamCost(f); + }); + + it("travelToCity()", async function() { + const f = ["travelToCity"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchaseTor()", async function() { + const f = ["purchaseTor"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchaseProgram()", async function() { + const f = ["purchaseProgram"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStats()", async function() { + const f = ["getStats"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCharacterInformation()", async function() { + const f = ["getCharacterInformation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("isBusy()", async function() { + const f = ["isBusy"]; + await testNonzeroDynamicRamCost(f); + }); + + it("stopAction()", async function() { + const f = ["stopAction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("upgradeHomeRam()", async function() { + const f = ["upgradeHomeRam"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getUpgradeHomeRamCost()", async function() { + const f = ["getUpgradeHomeRamCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("workForCompany()", async function() { + const f = ["workForCompany"]; + await testNonzeroDynamicRamCost(f); + }); + + it("applyToCompany()", async function() { + const f = ["applyToCompany"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCompanyRep()", async function() { + const f = ["getCompanyRep"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCompanyFavor()", async function() { + const f = ["getCompanyFavor"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCompanyFavorGain()", async function() { + const f = ["getCompanyFavorGain"]; + await testNonzeroDynamicRamCost(f); + }); + + it("checkFactionInvitations()", async function() { + const f = ["checkFactionInvitations"]; + await testNonzeroDynamicRamCost(f); + }); + + it("joinFaction()", async function() { + const f = ["joinFaction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("workForFaction()", async function() { + const f = ["workForFaction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getFactionRep()", async function() { + const f = ["getFactionRep"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getFactionFavor()", async function() { + const f = ["getFactionFavor"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getFactionFavorGain()", async function() { + const f = ["getFactionFavorGain"]; + await testNonzeroDynamicRamCost(f); + }); + + it("donateToFaction()", async function() { + const f = ["donateToFaction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("createProgram()", async function() { + const f = ["createProgram"]; + await testNonzeroDynamicRamCost(f); + }); + + it("commitCrime()", async function() { + const f = ["commitCrime"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCrimeChance()", async function() { + const f = ["getCrimeChance"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getOwnedAugmentations()", async function() { + const f = ["getOwnedAugmentations"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getOwnedSourceFiles()", async function() { + const f = ["getOwnedSourceFiles"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getAugmentationsFromFaction()", async function() { + const f = ["getAugmentationsFromFaction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getAugmentationPrereq()", async function() { + const f = ["getAugmentationPrereq"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getAugmentationCost()", async function() { + const f = ["getAugmentationCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchaseAugmentation()", async function() { + const f = ["purchaseAugmentation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("installAugmentations()", async function() { + const f = ["installAugmentations"]; + await testNonzeroDynamicRamCost(f); + }); + }); + + describe("Bladeburner API", async function() { + it("getContractNames()", async function() { + const f = ["bladeburner", "getContractNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getOperationNames()", async function() { + const f = ["bladeburner", "getOperationNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getBlackOpNames()", async function() { + const f = ["bladeburner", "getBlackOpNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getGeneralActionNames()", async function() { + const f = ["bladeburner", "getGeneralActionNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSkillNames()", async function() { + const f = ["bladeburner", "getSkillNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("startAction()", async function() { + const f = ["bladeburner", "startAction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("stopBladeburnerAction()", async function() { + const f = ["bladeburner", "stopBladeburnerAction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCurrentAction()", async function() { + const f = ["bladeburner", "getCurrentAction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionTime()", async function() { + const f = ["bladeburner", "getActionTime"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionEstimatedSuccessChance()", async function() { + const f = ["bladeburner", "getActionEstimatedSuccessChance"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionRepGain()", async function() { + const f = ["bladeburner", "getActionRepGain"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionCountRemaining()", async function() { + const f = ["bladeburner", "getActionCountRemaining"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionMaxLevel()", async function() { + const f = ["bladeburner", "getActionMaxLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionCurrentLevel()", async function() { + const f = ["bladeburner", "getActionCurrentLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getActionAutolevel()", async function() { + const f = ["bladeburner", "getActionAutolevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setActionAutolevel()", async function() { + const f = ["bladeburner", "setActionAutolevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setActionLevel()", async function() { + const f = ["bladeburner", "setActionLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getRank()", async function() { + const f = ["bladeburner", "getRank"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getBlackOpRank()", async function() { + const f = ["bladeburner", "getBlackOpRank"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSkillPoints()", async function() { + const f = ["bladeburner", "getSkillPoints"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSkillLevel()", async function() { + const f = ["bladeburner", "getSkillLevel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSkillUpgradeCost()", async function() { + const f = ["bladeburner", "getSkillUpgradeCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("upgradeSkill()", async function() { + const f = ["bladeburner", "upgradeSkill"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getTeamSize()", async function() { + const f = ["bladeburner", "getTeamSize"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setTeamSize()", async function() { + const f = ["bladeburner", "setTeamSize"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCityEstimatedPopulation()", async function() { + const f = ["bladeburner", "getCityEstimatedPopulation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCityEstimatedCommunities()", async function() { + const f = ["bladeburner", "getCityEstimatedCommunities"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCityChaos()", async function() { + const f = ["bladeburner", "getCityChaos"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getCity()", async function() { + const f = ["bladeburner", "getCity"]; + await testNonzeroDynamicRamCost(f); + }); + + it("switchCity()", async function() { + const f = ["bladeburner", "switchCity"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getStamina()", async function() { + const f = ["bladeburner", "getStamina"]; + await testNonzeroDynamicRamCost(f); + }); + + it("joinBladeburnerFaction()", async function() { + const f = ["bladeburner", "joinBladeburnerFaction"]; + await testNonzeroDynamicRamCost(f); + }); + + it("joinBladeburnerDivision()", async function() { + const f = ["bladeburner", "joinBladeburnerDivision"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getBonusTime()", async function() { + const f = ["bladeburner", "getBonusTime"]; + await testZeroDynamicRamCost(f); + }); + }); + + describe("Gang API", async function() { + it("getMemberNames()", async function() { + const f = ["gang", "getMemberNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getGangInformation()", async function() { + const f = ["gang", "getGangInformation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getOtherGangInformation()", async function() { + const f = ["gang", "getOtherGangInformation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getMemberInformation()", async function() { + const f = ["gang", "getMemberInformation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("canRecruitMember()", async function() { + const f = ["gang", "canRecruitMember"]; + await testNonzeroDynamicRamCost(f); + }); + + it("recruitMember()", async function() { + const f = ["gang", "recruitMember"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getTaskNames()", async function() { + const f = ["gang", "getTaskNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setMemberTask()", async function() { + const f = ["gang", "setMemberTask"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getEquipmentNames()", async function() { + const f = ["gang", "getEquipmentNames"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getEquipmentCost()", async function() { + const f = ["gang", "getEquipmentCost"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getEquipmentType()", async function() { + const f = ["gang", "getEquipmentType"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchaseEquipment()", async function() { + const f = ["gang", "purchaseEquipment"]; + await testNonzeroDynamicRamCost(f); + }); + + it("ascendMember()", async function() { + const f = ["gang", "ascendMember"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setTerritoryWarfare()", async function() { + const f = ["gang", "setTerritoryWarfare"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getChanceToWinClash()", async function() { + const f = ["gang", "getChanceToWinClash"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getBonusTime()", async function() { + const f = ["gang", "getBonusTime"]; + await testZeroDynamicRamCost(f); + }); + }); + + describe("Coding Contract API", async function() { + it("attempt()", async function() { + const f = ["codingcontract", "attempt"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getContractType()", async function() { + const f = ["codingcontract", "getContractType"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getDescription()", async function() { + const f = ["codingcontract", "getDescription"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getData()", async function() { + const f = ["codingcontract", "getData"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getNumTriesRemaining()", async function() { + const f = ["codingcontract", "getNumTriesRemaining"]; + await testNonzeroDynamicRamCost(f); + }); + }); + + describe("Sleeve API", async function() { + it("getNumSleeves()", async function() { + const f = ["sleeve", "getNumSleeves"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSleeveStats()", async function() { + const f = ["sleeve", "getSleeveStats"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getInformation()", async function() { + const f = ["sleeve", "getInformation"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getTask()", async function() { + const f = ["sleeve", "getTask"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToShockRecovery()", async function() { + const f = ["sleeve", "setToShockRecovery"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToSynchronize()", async function() { + const f = ["sleeve", "setToSynchronize"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToCommitCrime()", async function() { + const f = ["sleeve", "setToCommitCrime"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToFactionWork()", async function() { + const f = ["sleeve", "setToFactionWork"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToCompanyWork()", async function() { + const f = ["sleeve", "setToCompanyWork"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToUniversityCourse()", async function() { + const f = ["sleeve", "setToUniversityCourse"]; + await testNonzeroDynamicRamCost(f); + }); + + it("setToGymWorkout()", async function() { + const f = ["sleeve", "setToGymWorkout"]; + await testNonzeroDynamicRamCost(f); + }); + + it("travel()", async function() { + const f = ["sleeve", "travel"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSleeveAugmentations()", async function() { + const f = ["sleeve", "getSleeveAugmentations"]; + await testNonzeroDynamicRamCost(f); + }); + + it("getSleevePurchasableAugs()", async function() { + const f = ["sleeve", "getSleevePurchasableAugs"]; + await testNonzeroDynamicRamCost(f); + }); + + it("purchaseSleeveAug()", async function() { + const f = ["sleeve", "purchaseSleeveAug"]; + await testNonzeroDynamicRamCost(f); + }); }); }); diff --git a/test/Netscript/StaticRamCalculationTests.js b/test/Netscript/StaticRamCalculationTests.js index b00887694..2da4ac0ea 100644 --- a/test/Netscript/StaticRamCalculationTests.js +++ b/test/Netscript/StaticRamCalculationTests.js @@ -10,7 +10,7 @@ const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost; describe("Netscript Static RAM Calculation/Generation Tests", function() { // Tests numeric equality, allowing for floating point imprecision function testEquality(val, expected) { - expect(val).to.be.within(expected - 10 * Number.EPSILON, expected + 10 * Number.EPSILON); + expect(val).to.be.within(expected - 100 * Number.EPSILON, expected + 100 * Number.EPSILON); } /** diff --git a/test/StockMarketTests.js b/test/StockMarketTests.js index 93e96c5d6..afd7674aa 100644 --- a/test/StockMarketTests.js +++ b/test/StockMarketTests.js @@ -1,4 +1,11 @@ import { CONSTANTS } from "../src/Constants"; +import { Player } from "../src/Player"; +import { + buyStock, + sellStock, + shortStock, + sellShort, +} from "../src/StockMarket/BuyingAndSelling"; import { Order } from "../src/StockMarket/Order"; import { processOrders } from "../src/StockMarket/OrderProcessing"; import { Stock } from "../src/StockMarket/Stock"; @@ -7,6 +14,7 @@ import { initStockMarket, initSymbolToStockMap, loadStockMarket, + processStockPrices, StockMarket, SymbolToStockMap, } from "../src/StockMarket/StockMarket"; @@ -22,8 +30,6 @@ import { import { OrderTypes } from "../src/StockMarket/data/OrderTypes" import { PositionTypes } from "../src/StockMarket/data/PositionTypes"; -//const assert = chai.assert; -//const expect = chai.expect; import { expect } from "chai"; console.log("Beginning Stock Market Tests"); @@ -192,7 +198,9 @@ describe("Stock Market Tests", function() { it("should have properties for managing game cycles", function() { expect(StockMarket).to.have.property("storedCycles"); + expect(StockMarket["storedCycles"]).to.equal(0); expect(StockMarket).to.have.property("lastUpdate"); + expect(StockMarket["lastUpdate"]).to.equal(0); }); }); @@ -206,15 +214,59 @@ describe("Stock Market Tests", function() { }); }); - // Reset stock market for each test - beforeEach(function() { + describe("processStockPrices()", function() { + before(function() { + deleteStockMarket(); + initStockMarket(); + initSymbolToStockMap(); + }); + + it("should store cycles until it actually processes", function() { + expect(StockMarket["storedCycles"]).to.equal(0); + processStockPrices(10); + expect(StockMarket["storedCycles"]).to.equal(10); + }); + + it("should trigger a price update when it has enough cycles", function() { + // Get the initial prices + const initialValues = {}; + for (const stockName in StockMarket) { + const stock = StockMarket[stockName]; + if (!(stock instanceof Stock)) { continue; } + initialValues[stock.symbol] = { + price: stock.price, + otlkMag: stock.otlkMag, + } + } + + // Don't know or care how many exact cycles are required + processStockPrices(1e9); + + // Both price and 'otlkMag' should be different + for (const stockName in StockMarket) { + const stock = StockMarket[stockName]; + if (!(stock instanceof Stock)) { continue; } + expect(initialValues[stock.symbol].price).to.not.equal(stock.price); + expect(initialValues[stock.symbol].otlkMag).to.not.equal(stock.otlkMag); + } + }); + }); + }); + + describe("StockToSymbolMap", function() { + before(function() { deleteStockMarket(); initStockMarket(); initSymbolToStockMap(); }); - it("should properly initialize", function() { + it("should map stock symbols to their corresponding Stock Objects", function() { + for (const stockName in StockMarket) { + const stock = StockMarket[stockName]; + if (!(stock instanceof Stock)) { continue; } + expect(SymbolToStockMap[stock.symbol]).to.equal(stock); + } }); }); @@ -637,6 +689,190 @@ describe("Stock Market Tests", function() { }); }); + describe("Transaction (Buy/Sell) Functions", function() { + const suppressDialogOpt = { suppressDialog: true }; + + describe("buyStock()", function() { + it("should fail for invalid arguments", function() { + expect(buyStock({}, 1, null, suppressDialogOpt)).to.equal(false); + expect(buyStock(stock, 0, null, suppressDialogOpt)).to.equal(false); + expect(buyStock(stock, -1, null, suppressDialogOpt)).to.equal(false); + expect(buyStock(stock, NaN, null, suppressDialogOpt)).to.equal(false); + }); + + it("should fail if player doesn't have enough money", function() { + Player.setMoney(0); + expect(buyStock(stock, 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should not allow for transactions that exceed the maximum shares", function() { + const maxShares = stock.maxShares; + expect(buyStock(stock, maxShares + 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should return true and properly update stock properties for successful transactions", function() { + const shares = 1e3; + const cost = getBuyTransactionCost(stock, shares, PositionTypes.Long); + Player.setMoney(cost); + + expect(buyStock(stock, shares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShares).to.equal(shares); + expect(stock.playerAvgPx).to.be.above(0); + expect(Player.money.toNumber()).to.equal(0); + }); + }); + + describe("sellStock()", function() { + it("should fail for invalid arguments", function() { + expect(sellStock({}, 1, null, suppressDialogOpt)).to.equal(false); + expect(sellStock(stock, 0, null, suppressDialogOpt)).to.equal(false); + expect(sellStock(stock, -1, null, suppressDialogOpt)).to.equal(false); + expect(sellStock(stock, NaN, null, suppressDialogOpt)).to.equal(false); + }); + + it("should fail if player doesn't have any shares", function() { + Player.setMoney(0); + expect(sellStock(stock, 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should not allow for transactions that exceed the maximum shares", function() { + const maxShares = stock.maxShares; + expect(sellStock(stock, maxShares + 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should return true and properly update stock properties for successful transactions", function() { + const shares = 1e3; + stock.playerShares = shares; + stock.playerAvgPx = stock.price; + const gain = getSellTransactionGain(stock, shares, PositionTypes.Long); + Player.setMoney(0); + + expect(sellStock(stock, shares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShares).to.equal(0); + expect(stock.playerAvgPx).to.equal(0); + expect(Player.money.toNumber()).to.equal(gain); + }); + + it("should cap the number of sharse sold to however many the player owns", function() { + const attemptedShares = 2e3; + const actualShares = 1e3; + stock.playerShares = actualShares; + stock.playerAvgPx = stock.price; + const gain = getSellTransactionGain(stock, actualShares, PositionTypes.Long); + Player.setMoney(0); + + expect(sellStock(stock, attemptedShares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShares).to.equal(0); + expect(stock.playerAvgPx).to.equal(0); + expect(Player.money.toNumber()).to.equal(gain); + }); + + it("should properly update stock properties for partial transactions", function() { + const shares = 1e3; + const origPrice = stock.price; + stock.playerShares = 2 * shares; + stock.playerAvgPx = origPrice; + const gain = getSellTransactionGain(stock, shares, PositionTypes.Long); + Player.setMoney(0); + + expect(sellStock(stock, shares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShares).to.equal(shares); + expect(stock.playerAvgPx).to.equal(origPrice); + expect(Player.money.toNumber()).to.equal(gain); + }); + }); + + describe("shortStock()", function() { + it("should fail for invalid arguments", function() { + expect(shortStock({}, 1, null, suppressDialogOpt)).to.equal(false); + expect(shortStock(stock, 0, null, suppressDialogOpt)).to.equal(false); + expect(shortStock(stock, -1, null, suppressDialogOpt)).to.equal(false); + expect(shortStock(stock, NaN, null, suppressDialogOpt)).to.equal(false); + }); + + it("should fail if player doesn't have enough money", function() { + Player.setMoney(0); + expect(shortStock(stock, 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should not allow for transactions that exceed the maximum shares", function() { + const maxShares = stock.maxShares; + expect(shortStock(stock, maxShares + 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should return true and properly update stock properties for successful transactions", function() { + const shares = 1e3; + const cost = getBuyTransactionCost(stock, shares, PositionTypes.Short); + Player.setMoney(cost); + + expect(shortStock(stock, shares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShortShares).to.equal(shares); + expect(stock.playerAvgShortPx).to.be.above(0); + expect(Player.money.toNumber()).to.equal(0); + }); + }); + + describe("sellShort()", function() { + it("should fail for invalid arguments", function() { + expect(sellShort({}, 1, null, suppressDialogOpt)).to.equal(false); + expect(sellShort(stock, 0, null, suppressDialogOpt)).to.equal(false); + expect(sellShort(stock, -1, null, suppressDialogOpt)).to.equal(false); + expect(sellShort(stock, NaN, null, suppressDialogOpt)).to.equal(false); + }); + + it("should fail if player doesn't have any shares", function() { + Player.setMoney(0); + expect(sellShort(stock, 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should not allow for transactions that exceed the maximum shares", function() { + const maxShares = stock.maxShares; + expect(sellShort(stock, maxShares + 1, null, suppressDialogOpt)).to.equal(false); + }); + + it("should return true and properly update stock properties for successful transactions", function() { + const shares = 1e3; + stock.playerShortShares = shares; + stock.playerAvgShortPx = stock.price; + const gain = getSellTransactionGain(stock, shares, PositionTypes.Short); + Player.setMoney(0); + + expect(sellShort(stock, shares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShortShares).to.equal(0); + expect(stock.playerAvgShortPx).to.equal(0); + expect(Player.money.toNumber()).to.equal(gain); + }); + + it("should cap the number of sharse sold to however many the player owns", function() { + const attemptedShares = 2e3; + const actualShares = 1e3; + stock.playerShortShares = actualShares; + stock.playerAvgShortPx = stock.price; + const gain = getSellTransactionGain(stock, actualShares, PositionTypes.Short); + Player.setMoney(0); + + expect(sellShort(stock, attemptedShares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShortShares).to.equal(0); + expect(stock.playerAvgShortPx).to.equal(0); + expect(Player.money.toNumber()).to.equal(gain); + }); + + it("should properly update stock properties for partial transactions", function() { + const shares = 1e3; + const origPrice = stock.price; + stock.playerShortShares = 2 * shares; + stock.playerAvgShortPx = origPrice; + const gain = getSellTransactionGain(stock, shares, PositionTypes.Short); + Player.setMoney(0); + + expect(sellShort(stock, shares, null, suppressDialogOpt)).to.equal(true); + expect(stock.playerShortShares).to.equal(shares); + expect(stock.playerAvgShortPx).to.equal(origPrice); + expect(Player.money.toNumber()).to.equal(gain); + }); + }); + }); + describe("Order Class", function() { it("should throw on invalid arguments", function() { function invalid1() { diff --git a/test/index.js b/test/index.js index 344ddca0f..3724bbc70 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,3 @@ -// export * from "./Netscript/DynamicRamCalculationTests"; +export * from "./Netscript/DynamicRamCalculationTests"; export * from "./Netscript/StaticRamCalculationTests"; export * from "./StockMarketTests";