Play levels 7-13 in the app and pin E2E to the local thirteen-stage backend commit. Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
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')
|
|
})
|