add js-fp3.tests
This commit is contained in:
@@ -0,0 +1,143 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
.output
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp directory
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Sveltekit cache directory
|
||||||
|
.svelte-kit/
|
||||||
|
|
||||||
|
# vitepress build output
|
||||||
|
**/.vitepress/dist
|
||||||
|
|
||||||
|
# vitepress cache directory
|
||||||
|
**/.vitepress/cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# Firebase cache directory
|
||||||
|
.firebase/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
.pnpm-store
|
||||||
|
|
||||||
|
# yarn v3
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# Vite files
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
|
.vite/
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { openAccount, withdraw } from '../js-fp3/domain/account.js';
|
||||||
|
import { createMoney } from '../js-fp3/domain/money.js';
|
||||||
|
|
||||||
|
describe('account domain', () => {
|
||||||
|
it('opening an account with a negative balance returns openingBalanceMustBeNonNegative', () => {
|
||||||
|
const result = openAccount('id-1', createMoney(-1));
|
||||||
|
|
||||||
|
expect(result.error.type).toBe('OpeningBalanceMustBeNonNegative');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdrawing a zero amount returns amountMustBePositive', () => {
|
||||||
|
const account = openAccount('id-1', createMoney(100)).value;
|
||||||
|
|
||||||
|
const result = withdraw(account, createMoney(0));
|
||||||
|
|
||||||
|
expect(result.error.type).toBe('AmountMustBePositive');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdrawing a negative amount returns amountMustBePositive', () => {
|
||||||
|
const account = openAccount('id-1', createMoney(100)).value;
|
||||||
|
|
||||||
|
const result = withdraw(account, createMoney(-10));
|
||||||
|
|
||||||
|
expect(result.error.type).toBe('AmountMustBePositive');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdrawing more than the balance returns insufficientBalance', () => {
|
||||||
|
const account = openAccount('id-1', createMoney(100)).value;
|
||||||
|
|
||||||
|
const result = withdraw(account, createMoney(101));
|
||||||
|
|
||||||
|
expect(result.error.type).toBe('InsufficientBalance');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('insufficientBalance error reports the current balance and attempted amount', () => {
|
||||||
|
const account = openAccount('id-1', createMoney(100)).value;
|
||||||
|
|
||||||
|
const error = withdraw(account, createMoney(150)).error;
|
||||||
|
|
||||||
|
expect(error.type).toBe('InsufficientBalance');
|
||||||
|
expect(error.balance.amount).toBe(100);
|
||||||
|
expect(error.amount.amount).toBe(150);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('successive withdrawals are each applied to the running balance', () => {
|
||||||
|
const account = openAccount('id-1', createMoney(300)).value;
|
||||||
|
|
||||||
|
const updatedAccount1 = withdraw(account, createMoney(100)).value;
|
||||||
|
const updatedAccount2 = withdraw(updatedAccount1, createMoney(100)).value;
|
||||||
|
|
||||||
|
expect(updatedAccount2.balance.amount).toBe(100);
|
||||||
|
});
|
||||||
|
});
|
||||||
Generated
+610
@@ -0,0 +1,610 @@
|
|||||||
|
{
|
||||||
|
"name": "js-fp3-tests",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "js-fp3-tests",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
|
"version": "4.60.2",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@types/estree": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/expect": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/spy": "2.1.9",
|
||||||
|
"@vitest/utils": "2.1.9",
|
||||||
|
"chai": "^5.1.2",
|
||||||
|
"tinyrainbow": "^1.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/mocker": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/spy": "2.1.9",
|
||||||
|
"estree-walker": "^3.0.3",
|
||||||
|
"magic-string": "^0.30.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"msw": "^2.4.9",
|
||||||
|
"vite": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"msw": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vite": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/pretty-format": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tinyrainbow": "^1.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/runner": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/utils": "2.1.9",
|
||||||
|
"pathe": "^1.1.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/snapshot": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/pretty-format": "2.1.9",
|
||||||
|
"magic-string": "^0.30.12",
|
||||||
|
"pathe": "^1.1.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/spy": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tinyspy": "^3.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitest/utils": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/pretty-format": "2.1.9",
|
||||||
|
"loupe": "^3.1.2",
|
||||||
|
"tinyrainbow": "^1.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/assertion-error": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cac": {
|
||||||
|
"version": "6.7.14",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chai": {
|
||||||
|
"version": "5.3.3",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"assertion-error": "^2.0.1",
|
||||||
|
"check-error": "^2.1.1",
|
||||||
|
"deep-eql": "^5.0.1",
|
||||||
|
"loupe": "^3.1.0",
|
||||||
|
"pathval": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/check-error": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.4.3",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/deep-eql": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-module-lexer": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.21.5",
|
||||||
|
"@esbuild/android-arm": "0.21.5",
|
||||||
|
"@esbuild/android-arm64": "0.21.5",
|
||||||
|
"@esbuild/android-x64": "0.21.5",
|
||||||
|
"@esbuild/darwin-arm64": "0.21.5",
|
||||||
|
"@esbuild/darwin-x64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-arm64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-x64": "0.21.5",
|
||||||
|
"@esbuild/linux-arm": "0.21.5",
|
||||||
|
"@esbuild/linux-arm64": "0.21.5",
|
||||||
|
"@esbuild/linux-ia32": "0.21.5",
|
||||||
|
"@esbuild/linux-loong64": "0.21.5",
|
||||||
|
"@esbuild/linux-mips64el": "0.21.5",
|
||||||
|
"@esbuild/linux-ppc64": "0.21.5",
|
||||||
|
"@esbuild/linux-riscv64": "0.21.5",
|
||||||
|
"@esbuild/linux-s390x": "0.21.5",
|
||||||
|
"@esbuild/linux-x64": "0.21.5",
|
||||||
|
"@esbuild/netbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/openbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/sunos-x64": "0.21.5",
|
||||||
|
"@esbuild/win32-arm64": "0.21.5",
|
||||||
|
"@esbuild/win32-ia32": "0.21.5",
|
||||||
|
"@esbuild/win32-x64": "0.21.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/expect-type": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/loupe": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.30.21",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.11",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pathe": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pathval": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.5.12",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.11",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "4.60.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "1.0.8"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-android-arm-eabi": "4.60.2",
|
||||||
|
"@rollup/rollup-android-arm64": "4.60.2",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.60.2",
|
||||||
|
"@rollup/rollup-darwin-x64": "4.60.2",
|
||||||
|
"@rollup/rollup-freebsd-arm64": "4.60.2",
|
||||||
|
"@rollup/rollup-freebsd-x64": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-arm64-musl": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-loong64-musl": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-ppc64-musl": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-linux-x64-musl": "4.60.2",
|
||||||
|
"@rollup/rollup-openbsd-x64": "4.60.2",
|
||||||
|
"@rollup/rollup-openharmony-arm64": "4.60.2",
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": "4.60.2",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.60.2",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.60.2",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.60.2",
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/siginfo": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/stackback": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/std-env": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tinybench": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tinyexec": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tinypool": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyrainbow": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyspy": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "5.4.21",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "^0.21.3",
|
||||||
|
"postcss": "^8.4.43",
|
||||||
|
"rollup": "^4.20.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": "^18.0.0 || >=20.0.0",
|
||||||
|
"less": "*",
|
||||||
|
"lightningcss": "^1.21.0",
|
||||||
|
"sass": "*",
|
||||||
|
"sass-embedded": "*",
|
||||||
|
"stylus": "*",
|
||||||
|
"sugarss": "*",
|
||||||
|
"terser": "^5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite-node": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cac": "^6.7.14",
|
||||||
|
"debug": "^4.3.7",
|
||||||
|
"es-module-lexer": "^1.5.4",
|
||||||
|
"pathe": "^1.1.2",
|
||||||
|
"vite": "^5.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite-node": "vite-node.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vitest": {
|
||||||
|
"version": "2.1.9",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/expect": "2.1.9",
|
||||||
|
"@vitest/mocker": "2.1.9",
|
||||||
|
"@vitest/pretty-format": "^2.1.9",
|
||||||
|
"@vitest/runner": "2.1.9",
|
||||||
|
"@vitest/snapshot": "2.1.9",
|
||||||
|
"@vitest/spy": "2.1.9",
|
||||||
|
"@vitest/utils": "2.1.9",
|
||||||
|
"chai": "^5.1.2",
|
||||||
|
"debug": "^4.3.7",
|
||||||
|
"expect-type": "^1.1.0",
|
||||||
|
"magic-string": "^0.30.12",
|
||||||
|
"pathe": "^1.1.2",
|
||||||
|
"std-env": "^3.8.0",
|
||||||
|
"tinybench": "^2.9.0",
|
||||||
|
"tinyexec": "^0.3.1",
|
||||||
|
"tinypool": "^1.0.1",
|
||||||
|
"tinyrainbow": "^1.2.0",
|
||||||
|
"vite": "^5.0.0",
|
||||||
|
"vite-node": "2.1.9",
|
||||||
|
"why-is-node-running": "^2.3.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vitest": "vitest.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@edge-runtime/vm": "*",
|
||||||
|
"@types/node": "^18.0.0 || >=20.0.0",
|
||||||
|
"@vitest/browser": "2.1.9",
|
||||||
|
"@vitest/ui": "2.1.9",
|
||||||
|
"happy-dom": "*",
|
||||||
|
"jsdom": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@edge-runtime/vm": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@vitest/browser": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@vitest/ui": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"happy-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"jsdom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/why-is-node-running": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"siginfo": "^2.0.0",
|
||||||
|
"stackback": "0.0.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"why-is-node-running": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "js-fp3-tests",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Vitest tests for js-fp3",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
import { match, when, any } from '../js-fp3/library/patterns.js';
|
||||||
|
|
||||||
|
describe('when', () => {
|
||||||
|
it('returns a two-element tuple of [predicate, handler]', () => {
|
||||||
|
const predicate = () => true;
|
||||||
|
const handler = () => 'result';
|
||||||
|
|
||||||
|
const branch = when(predicate, handler);
|
||||||
|
|
||||||
|
expect(branch).toEqual([predicate, handler]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('any', () => {
|
||||||
|
it('returns true for any value', () => {
|
||||||
|
expect(any(0)).toBe(true);
|
||||||
|
expect(any(null)).toBe(true);
|
||||||
|
expect(any('string')).toBe(true);
|
||||||
|
expect(any({ x: 1 })).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('match', () => {
|
||||||
|
it('executes the handler of the first matching branch', () => {
|
||||||
|
const result = match(
|
||||||
|
when((v) => v === 1, () => 'one'),
|
||||||
|
when((v) => v === 2, () => 'two'),
|
||||||
|
when(any, () => 'other')
|
||||||
|
)(1);
|
||||||
|
|
||||||
|
expect(result).toBe('one');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls through to the next branch when the first does not match', () => {
|
||||||
|
const result = match(
|
||||||
|
when((v) => v === 1, () => 'one'),
|
||||||
|
when((v) => v === 2, () => 'two'),
|
||||||
|
when(any, () => 'other')
|
||||||
|
)(2);
|
||||||
|
|
||||||
|
expect(result).toBe('two');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the any catch-all when no earlier branch matches', () => {
|
||||||
|
const result = match(
|
||||||
|
when((v) => v === 1, () => 'one'),
|
||||||
|
when(any, () => 'catch-all')
|
||||||
|
)(99);
|
||||||
|
|
||||||
|
expect(result).toBe('catch-all');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes the value to the handler', () => {
|
||||||
|
const result = match(
|
||||||
|
when(any, (v) => v * 2)
|
||||||
|
)(7);
|
||||||
|
|
||||||
|
expect(result).toBe(14);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when no branch matches', () => {
|
||||||
|
expect(() =>
|
||||||
|
match(
|
||||||
|
when((v) => v === 1, () => 'one')
|
||||||
|
)(99)
|
||||||
|
).toThrow('No matching pattern');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call handlers for branches that were not selected', () => {
|
||||||
|
const skipped = vi.fn(() => 'skipped');
|
||||||
|
const selected = vi.fn(() => 'selected');
|
||||||
|
|
||||||
|
match(
|
||||||
|
when((v) => v === 1, selected),
|
||||||
|
when(any, skipped)
|
||||||
|
)(1);
|
||||||
|
|
||||||
|
expect(selected).toHaveBeenCalledOnce();
|
||||||
|
expect(skipped).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
import {
|
||||||
|
ok,
|
||||||
|
fail,
|
||||||
|
catchResult,
|
||||||
|
bind,
|
||||||
|
map,
|
||||||
|
tap,
|
||||||
|
tapError,
|
||||||
|
mapError,
|
||||||
|
match,
|
||||||
|
switch_,
|
||||||
|
pipe,
|
||||||
|
} from '../js-fp3/library/result.js';
|
||||||
|
|
||||||
|
describe('ok / fail construction', () => {
|
||||||
|
it('ok is success', () => {
|
||||||
|
const result = ok(42);
|
||||||
|
|
||||||
|
expect(result.isSuccess).toBe(true);
|
||||||
|
expect(result.isFailure).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fail is failure', () => {
|
||||||
|
const result = fail('oops');
|
||||||
|
|
||||||
|
expect(result.isFailure).toBe(true);
|
||||||
|
expect(result.isSuccess).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ok exposes value', () => {
|
||||||
|
const result = ok(42);
|
||||||
|
|
||||||
|
expect(result.value).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fail exposes error', () => {
|
||||||
|
const result = fail('oops');
|
||||||
|
|
||||||
|
expect(result.error).toBe('oops');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('match', () => {
|
||||||
|
it('calls onSuccess for ok', () => {
|
||||||
|
const result = ok(10);
|
||||||
|
|
||||||
|
const output = match((v) => `value:${v}`, (e) => `error:${e}`)(result);
|
||||||
|
|
||||||
|
expect(output).toBe('value:10');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onFailure for fail', () => {
|
||||||
|
const result = fail('bad');
|
||||||
|
|
||||||
|
const output = match((v) => `value:${v}`, (e) => `error:${e}`)(result);
|
||||||
|
|
||||||
|
expect(output).toBe('error:bad');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('switch_', () => {
|
||||||
|
it('calls onSuccess for ok', () => {
|
||||||
|
const result = ok(7);
|
||||||
|
let called = false;
|
||||||
|
|
||||||
|
switch_(() => { called = true; }, () => {})(result);
|
||||||
|
|
||||||
|
expect(called).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onFailure for fail', () => {
|
||||||
|
const result = fail('err');
|
||||||
|
let called = false;
|
||||||
|
|
||||||
|
switch_(() => {}, () => { called = true; })(result);
|
||||||
|
|
||||||
|
expect(called).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('catchResult', () => {
|
||||||
|
it('returns ok when no exception is thrown', () => {
|
||||||
|
const result = catchResult(() => 99, (ex) => ex.message);
|
||||||
|
|
||||||
|
expect(result.isSuccess).toBe(true);
|
||||||
|
expect(result.value).toBe(99);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail when an exception is thrown', () => {
|
||||||
|
const result = catchResult(
|
||||||
|
() => { throw new Error('boom'); },
|
||||||
|
(ex) => ex.message
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.isFailure).toBe(true);
|
||||||
|
expect(result.error).toBe('boom');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bind', () => {
|
||||||
|
it('chains to the next result on success', () => {
|
||||||
|
const result = bind((v) => ok(`got ${v}`))(ok(5));
|
||||||
|
|
||||||
|
expect(result.value).toBe('got 5');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('short-circuits on failure without calling the binder', () => {
|
||||||
|
const binder = vi.fn((v) => ok(`got ${v}`));
|
||||||
|
const result = bind(binder)(fail('nope'));
|
||||||
|
|
||||||
|
expect(binder).not.toHaveBeenCalled();
|
||||||
|
expect(result.error).toBe('nope');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('propagates the inner failure when the binder returns fail', () => {
|
||||||
|
const result = bind(() => fail('inner fail'))(ok(5));
|
||||||
|
|
||||||
|
expect(result.error).toBe('inner fail');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('map', () => {
|
||||||
|
it('transforms the value on success', () => {
|
||||||
|
const result = map((v) => v * 2)(ok(3));
|
||||||
|
|
||||||
|
expect(result.value).toBe(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call the mapper on failure', () => {
|
||||||
|
const mapper = vi.fn((v) => v * 2);
|
||||||
|
const result = map(mapper)(fail('err'));
|
||||||
|
|
||||||
|
expect(mapper).not.toHaveBeenCalled();
|
||||||
|
expect(result.error).toBe('err');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tap', () => {
|
||||||
|
it('calls the action and returns the original result on success', () => {
|
||||||
|
let seen = -1;
|
||||||
|
const result = tap((v) => { seen = v; })(ok(8));
|
||||||
|
|
||||||
|
expect(seen).toBe(8);
|
||||||
|
expect(result.value).toBe(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call the action on failure', () => {
|
||||||
|
const action = vi.fn();
|
||||||
|
tap(action)(fail('err'));
|
||||||
|
|
||||||
|
expect(action).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tapError', () => {
|
||||||
|
it('calls the action and returns the original result on failure', () => {
|
||||||
|
let seen = '';
|
||||||
|
const result = tapError((e) => { seen = e; })(fail('bad'));
|
||||||
|
|
||||||
|
expect(seen).toBe('bad');
|
||||||
|
expect(result.error).toBe('bad');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call the action on success', () => {
|
||||||
|
const action = vi.fn();
|
||||||
|
tapError(action)(ok(1));
|
||||||
|
|
||||||
|
expect(action).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mapError', () => {
|
||||||
|
it('transforms the error on failure', () => {
|
||||||
|
const result = mapError((e) => e.length)(fail('oops'));
|
||||||
|
|
||||||
|
expect(result.error).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call the mapper on success', () => {
|
||||||
|
const mapper = vi.fn((e) => e.length);
|
||||||
|
const result = mapError(mapper)(ok(1));
|
||||||
|
|
||||||
|
expect(mapper).not.toHaveBeenCalled();
|
||||||
|
expect(result.value).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('pipe', () => {
|
||||||
|
it('applies functions left to right starting from the initial result', () => {
|
||||||
|
const result = pipe(ok(2), map((v) => v + 1), map((v) => v * 3));
|
||||||
|
|
||||||
|
expect(result.value).toBe(9);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('short-circuits on the first failure', () => {
|
||||||
|
const mapper = vi.fn((v) => v * 2);
|
||||||
|
const result = pipe(fail('stop'), map(mapper));
|
||||||
|
|
||||||
|
expect(mapper).not.toHaveBeenCalled();
|
||||||
|
expect(result.error).toBe('stop');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { createWithdrawMoney } from '../js-fp3/application/accountApp.js';
|
||||||
|
import { openAccount } from '../js-fp3/domain/account.js';
|
||||||
|
import { createMoney } from '../js-fp3/domain/money.js';
|
||||||
|
import { createInMemoryRepository } from '../js-fp3/infrastructure/repository.js';
|
||||||
|
|
||||||
|
const buildHandler = () => {
|
||||||
|
const repo = createInMemoryRepository();
|
||||||
|
const withdraw = createWithdrawMoney(repo).value;
|
||||||
|
return { withdraw, repo };
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('withdrawMoney application use case', () => {
|
||||||
|
it('withdrawing from an account reduces its balance by the withdrawn amount', () => {
|
||||||
|
const { withdraw, repo } = buildHandler();
|
||||||
|
const accountId = 'acc-1';
|
||||||
|
repo.saveAccount(openAccount(accountId, createMoney(200)).value);
|
||||||
|
|
||||||
|
withdraw(accountId, 75);
|
||||||
|
|
||||||
|
const account = repo.loadAccount(accountId).value;
|
||||||
|
expect(account.balance.amount).toBe(125);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdrawing the entire balance leaves the account at zero', () => {
|
||||||
|
const { withdraw, repo } = buildHandler();
|
||||||
|
const accountId = 'acc-2';
|
||||||
|
repo.saveAccount(openAccount(accountId, createMoney(100)).value);
|
||||||
|
|
||||||
|
withdraw(accountId, 100);
|
||||||
|
|
||||||
|
const account = repo.loadAccount(accountId).value;
|
||||||
|
expect(account.balance.amount).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdrawing from a non-existent account returns accountNotFound', () => {
|
||||||
|
const { withdraw } = buildHandler();
|
||||||
|
|
||||||
|
const result = withdraw('no-such-id', 50);
|
||||||
|
|
||||||
|
expect(result.error.type).toBe('AccountNotFound');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdrawing more than the available balance returns insufficientBalance', () => {
|
||||||
|
const { withdraw, repo } = buildHandler();
|
||||||
|
const accountId = 'acc-3';
|
||||||
|
repo.saveAccount(openAccount(accountId, createMoney(50)).value);
|
||||||
|
|
||||||
|
const result = withdraw(accountId, 100);
|
||||||
|
|
||||||
|
expect(result.error.type).toBe('InsufficientBalance');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accountNotFound error reports the requested account id', () => {
|
||||||
|
const { withdraw } = buildHandler();
|
||||||
|
const accountId = 'missing-acc';
|
||||||
|
|
||||||
|
const error = withdraw(accountId, 50).error;
|
||||||
|
|
||||||
|
expect(error.type).toBe('AccountNotFound');
|
||||||
|
expect(error.accountId).toBe(accountId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('insufficientBalance error reports the current balance and attempted amount', () => {
|
||||||
|
const { withdraw, repo } = buildHandler();
|
||||||
|
const accountId = 'acc-4';
|
||||||
|
repo.saveAccount(openAccount(accountId, createMoney(50)).value);
|
||||||
|
|
||||||
|
const error = withdraw(accountId, 120).error;
|
||||||
|
|
||||||
|
expect(error.type).toBe('InsufficientBalance');
|
||||||
|
expect(error.balance.amount).toBe(50);
|
||||||
|
expect(error.amount.amount).toBe(120);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('after a failed withdrawal the balance is unchanged', () => {
|
||||||
|
const { withdraw, repo } = buildHandler();
|
||||||
|
const accountId = 'acc-5';
|
||||||
|
repo.saveAccount(openAccount(accountId, createMoney(50)).value);
|
||||||
|
|
||||||
|
withdraw(accountId, 999);
|
||||||
|
|
||||||
|
const account = repo.loadAccount(accountId).value;
|
||||||
|
expect(account.balance.amount).toBe(50);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user