Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JEST: Fix flaky stock market test #1834

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

catloversg
Copy link
Contributor

@catloversg catloversg commented Dec 9, 2024

The stock market has a test assuming that the stock forecast (otlkMag) is always changed after each market cycle. However, in edge cases, that's not true.

Let's check the log in these actions:
https://github.com/bitburner-official/bitburner-src/actions/runs/11485607894/job/31966084518?pr=1726
https://github.com/bitburner-official/bitburner-src/actions/runs/12229503158/job/34109372108?pr=1833

Those tests failed with "Omega Software" stock (OMGA). Init value of OMGA's otlkMag is 0.5.

In each market cycle:

let otlkMagChange = stock.otlkMag * av;
if (stock.otlkMag < 5) {
if (stock.otlkMag <= 1) {
otlkMagChange = 1;
} else {
otlkMagChange *= 10;
}
}
stock.cycleForecast(otlkMagChange);

cycleForecast(changeAmt = 0.1): void {
const increaseChance = this.getForecastIncreaseChance();
if (Math.random() < increaseChance) {
// Forecast increases
if (this.b) {
this.otlkMag += changeAmt;
} else {
this.otlkMag -= changeAmt;
}
} else if (this.b) {
// Forecast decreases
this.otlkMag -= changeAmt;
} else {
this.otlkMag += changeAmt;
}
this.otlkMag = Math.min(this.otlkMag, 50);
if (this.otlkMag < 0) {
this.otlkMag *= -1;
this.b = !this.b;
}
}

When otlkMag is 0.5, otlkMagChange is set to 1. After cycleForecast, otlkMag may still be 0.5.

This PR adds special tests for this case.

To test this PR, you can use this test code:

globalThis.test = () => {
  for (let i = 0; i < 100000; i++) {
    deleteStockMarket();
    initStockMarket();
    initSymbolToStockMap();

    const initialValues: Record<string, any> = {};
    for (const stockName in StockMarket) {
      const stock = StockMarket[stockName];
      if (!(stock instanceof Stock)) {
        continue;
      }
      initialValues[stock.symbol] = { ...stock };
    }

    StockMarket.lastUpdate = new Date().getTime() - 5e3;
    processStockPrices(1e9);

    let testOmegaSoftwareStock = false;
    for (const stockName in StockMarket) {
      const stock = StockMarket[stockName];
      if (!(stock instanceof Stock)) {
        continue;
      }
      const initValue = initialValues[stock.symbol];
      if (initValue.price === stock.price) {
        console.error(`Stock price is not changed. Before: ${initValue.price}. After: ${stock.price}.`);
      }
      if (initValue.otlkMag === stock.otlkMag && initValue.b === stock.b) {
        if (stock.name === LocationName.IshimaOmegaSoftware && stock.otlkMag === 0.5) {
          testOmegaSoftwareStock = true;
          continue;
        }
        console.error(
          "expected either price or otlkMag to be different: " +
            `stock: ${stockName} otlkMag: ${stock.otlkMag} b: ${stock.b}`,
        );
      }
    }

    if (!testOmegaSoftwareStock) {
      continue;
    }
    deleteStockMarket();
    initStockMarket();
    initSymbolToStockMap();
    const currentOtlkMag = StockMarket[LocationName.IshimaOmegaSoftware].otlkMag;
    let isForeCastChanged = false;
    for (let j = 0; j < 10000; j++) {
      StockMarket.lastUpdate = new Date().getTime() - 5e3;
      processStockPrices(1e9);
      if (currentOtlkMag !== StockMarket[LocationName.IshimaOmegaSoftware].otlkMag) {
        isForeCastChanged = true;
        break;
      }
    }
    if (!isForeCastChanged) {
      throw new Error(`Forecast of OMGA is not changed: ${StockMarket[LocationName.IshimaOmegaSoftware].otlkMag}`);
    }
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant