import assert from 'node:assert/strict' import test from 'node:test' import { ApiNetworkError, StaleSessionError } from '../src/api/errors' import { invalidateSession } from '../src/api/authSession' import { bindMeMutationApply, resetMeMutationQueueForTests, runMeMutation } from '../src/api/meMutationQueue' import { adjustFund, canIncreaseFund, changeLabel, emptyFunds, fundSum, previewScenarios, remainingCoins, } from '../src/modules/gold-land/portfolio/portfolioMath' import { setRiskAnswer } from '../src/modules/gold-land/risk-return/riskChoices' const scenarios = [ { id: 'sunny', title: 'هوای آفتابی', note: 'مثال', changes: [ { fundId: 'sun' as const, percent: 20 }, { fundId: 'rain' as const, percent: -10 }, { fundId: 'wind' as const, percent: 0 }, ], }, { id: 'drop', title: 'افت همگانی', note: 'مثال', changes: [ { fundId: 'sun' as const, percent: -10 }, { fundId: 'rain' as const, percent: -10 }, { fundId: 'wind' as const, percent: -10 }, ], }, ] const funds = [ { id: 'sun' as const, label: 'آفتاب' }, { id: 'rain' as const, label: 'باران' }, { id: 'wind' as const, label: 'باد' }, ] test('allocation stays on step 10 and cannot pass 100 or go negative', () => { let amounts = emptyFunds() assert.equal(remainingCoins(100, amounts), 100) assert.equal(canIncreaseFund(amounts, 10, 100), true) for (let index = 0; index < 4; index += 1) amounts = adjustFund(amounts, 'sun', 10, 10, 100) amounts = adjustFund(amounts, 'rain', 10, 10, 100) amounts = adjustFund(amounts, 'wind', 50, 10, 100) assert.equal(fundSum(amounts), 100) assert.equal(remainingCoins(100, amounts), 0) assert.deepEqual(adjustFund(amounts, 'sun', 10, 10, 100), amounts) assert.deepEqual(adjustFund(amounts, 'rain', -20, 10, 100), amounts) const reduced = adjustFund(amounts, 'rain', -10, 10, 100) assert.equal(reduced.rain, 0) assert.equal(remainingCoins(100, reduced), 10) }) test('changing the mix refreshes all shown outcomes and loss is written in words', () => { const first = previewScenarios({ sun: 100, rain: 0, wind: 0 }, scenarios, funds) assert.equal(first[0]?.changeLabel, 'سود 20 سکه') const drop = first.find((item) => item.id === 'drop') assert.equal(drop?.total, 90) assert.equal(drop?.changeLabel, changeLabel(90)) assert.equal(drop?.changeLabel.includes('زیان'), true) const second = previewScenarios({ sun: 40, rain: 30, wind: 30 }, scenarios, funds) assert.equal(second[0]?.total, 105) assert.notEqual(second[0]?.total, first[0]?.total) }) test('review keeps answers and a network or stale session does not apply me', async () => { let answers = setRiskAnswer({}, 'q-one', 'safe') answers = setRiskAnswer(answers, 'q-one', 'depend') assert.equal(answers['q-one'], 'depend') const mix = { sun: 40, rain: 30, wind: 30 } resetMeMutationQueueForTests() let applied = 0 bindMeMutationApply(() => { applied += 1 return true }) await assert.rejects(() => runMeMutation(async () => { throw new ApiNetworkError() }), ApiNetworkError) assert.equal(applied, 0) assert.equal(mix.sun, 40) assert.equal(answers['q-one'], 'depend') await assert.rejects(() => runMeMutation(async () => { invalidateSession() return { me: { user: { coins: 1 } } } }), StaleSessionError) assert.equal(applied, 0) assert.equal(answers['q-one'], 'depend') })