Skip to content

Commit fa8814d

Browse files
authored
Add bank-account exercise (#347)
* Add bank-account exercise Without parallelism/concurrency * instructions append: no parallelism * move exercise earlier in config
1 parent 13e6aac commit fa8814d

File tree

9 files changed

+268
-0
lines changed

9 files changed

+268
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@
7272
"prerequisites": [],
7373
"difficulty": 2
7474
},
75+
{
76+
"slug": "bank-account",
77+
"name": "Bank Account",
78+
"uuid": "27f36909-8005-4537-80b4-4c81a3325b5b",
79+
"practices": [],
80+
"prerequisites": [],
81+
"difficulty": 3
82+
},
7583
{
7684
"slug": "beer-song",
7785
"name": "Beer Song",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# ignore
2+
3+
## CoffeeScript-specific Instructions
4+
5+
For this exercise, we will not be testing parallelism.
6+
Your task is to implement the BankAccount methods without worrying about parallel execution.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Instructions
2+
3+
Your task is to implement bank accounts supporting opening/closing, withdrawals, and deposits of money.
4+
5+
As bank accounts can be accessed in many different ways (internet, mobile phones, automatic charges), your bank software must allow accounts to be safely accessed from multiple threads/processes (terminology depends on your programming language) in parallel.
6+
For example, there may be many deposits and withdrawals occurring in parallel; you need to ensure there are no [race conditions][wikipedia] between when you read the account balance and set the new balance.
7+
8+
It should be possible to close an account; operations against a closed account must fail.
9+
10+
[wikipedia]: https://en.wikipedia.org/wiki/Race_condition#In_software
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Introduction
2+
3+
After years of filling out forms and waiting, you've finally acquired your banking license.
4+
This means you are now officially eligible to open your own bank, hurray!
5+
6+
Your first priority is to get the IT systems up and running.
7+
After a day of hard work, you can already open and close accounts, as well as handle withdrawals and deposits.
8+
9+
Since you couldn't be bothered writing tests, you invite some friends to help test the system.
10+
However, after just five minutes, one of your friends claims they've lost money!
11+
While you're confident your code is bug-free, you start looking through the logs to investigate.
12+
13+
Ah yes, just as you suspected, your friend is at fault!
14+
They shared their test credentials with another friend, and together they conspired to make deposits and withdrawals from the same account _in parallel_.
15+
Who would do such a thing?
16+
17+
While you argue that it's physically _impossible_ for someone to access their account in parallel, your friend smugly notifies you that the banking rules _require_ you to support this.
18+
Thus, no parallel banking support, no go-live signal.
19+
Sighing, you create a mental note to work on this tomorrow.
20+
This will set your launch date back at _least_ one more day, but well...
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"authors": [
3+
"glennj"
4+
],
5+
"files": {
6+
"solution": [
7+
"bank-account.coffee"
8+
],
9+
"test": [
10+
"bank-account.spec.coffee"
11+
],
12+
"example": [
13+
".meta/example.coffee"
14+
]
15+
},
16+
"blurb": "Simulate a bank account supporting opening/closing, withdraws, and deposits of money. Watch out for concurrent transactions!"
17+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class BankAccount
2+
constructor: ->
3+
@_open = false
4+
5+
open: ->
6+
throw Error 'account already open' if @_open
7+
@_open = true
8+
@_balance = 0
9+
10+
close: ->
11+
throw Error 'account not open' unless @_open
12+
@_open = false
13+
14+
balance: ->
15+
throw Error 'account not open' unless @_open
16+
@_balance
17+
18+
deposit: (money) ->
19+
throw Error 'account not open' unless @_open
20+
throw Error "amount must be greater than 0" unless money > 0
21+
@_balance += money
22+
23+
withdraw: (money) ->
24+
throw Error 'account not open' unless @_open
25+
throw Error "amount must be greater than 0" unless money > 0
26+
throw Error "amount must be less than balance" unless money <= @_balance
27+
@_balance -= money
28+
29+
module.exports = BankAccount
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[983a1528-4ceb-45e5-8257-8ce01aceb5ed]
13+
description = "Newly opened account has zero balance"
14+
15+
[e88d4ec3-c6bf-4752-8e59-5046c44e3ba7]
16+
description = "Single deposit"
17+
18+
[3d9147d4-63f4-4844-8d2b-1fee2e9a2a0d]
19+
description = "Multiple deposits"
20+
21+
[08f1af07-27ae-4b38-aa19-770bde558064]
22+
description = "Withdraw once"
23+
24+
[6f6d242f-8c31-4ac6-8995-a90d42cad59f]
25+
description = "Withdraw twice"
26+
27+
[45161c94-a094-4c77-9cec-998b70429bda]
28+
description = "Can do multiple operations sequentially"
29+
30+
[f9facfaa-d824-486e-8381-48832c4bbffd]
31+
description = "Cannot check balance of closed account"
32+
33+
[7a65ba52-e35c-4fd2-8159-bda2bde6e59c]
34+
description = "Cannot deposit into closed account"
35+
36+
[a0a1835d-faae-4ad4-a6f3-1fcc2121380b]
37+
description = "Cannot deposit into unopened account"
38+
39+
[570dfaa5-0532-4c1f-a7d3-0f65c3265608]
40+
description = "Cannot withdraw from closed account"
41+
42+
[c396d233-1c49-4272-98dc-7f502dbb9470]
43+
description = "Cannot close an account that was not opened"
44+
45+
[c06f534f-bdc2-4a02-a388-1063400684de]
46+
description = "Cannot open an already opened account"
47+
48+
[0722d404-6116-4f92-ba3b-da7f88f1669c]
49+
description = "Reopened account does not retain balance"
50+
51+
[ec42245f-9361-4341-8231-a22e8d19c52f]
52+
description = "Cannot withdraw more than deposited"
53+
54+
[4f381ef8-10ef-4507-8e1d-0631ecc8ee72]
55+
description = "Cannot withdraw negative"
56+
57+
[d45df9ea-1db0-47f3-b18c-d365db49d938]
58+
description = "Cannot deposit negative"
59+
60+
[ba0c1e0b-0f00-416f-8097-a7dfc97871ff]
61+
description = "Can handle concurrent transactions"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class BankAccount
2+
constructor: (args) ->
3+
4+
open: () ->
5+
6+
close: () ->
7+
8+
balance: () ->
9+
10+
deposit: (args) ->
11+
12+
withdraw: (args) ->
13+
14+
module.exports = BankAccount
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
BankAccount = require './bank-account'
2+
3+
describe 'BankAccount', ->
4+
it 'Newly opened account has zero balance', ->
5+
bankAccount = new BankAccount
6+
bankAccount.open()
7+
expect(bankAccount.balance()).toEqual 0
8+
9+
xit "Single deposit", ->
10+
bankAccount = new BankAccount
11+
bankAccount.open()
12+
bankAccount.deposit 100
13+
expect(bankAccount.balance()).toEqual 100
14+
15+
xit "Multiple deposits", ->
16+
bankAccount = new BankAccount
17+
bankAccount.open()
18+
bankAccount.deposit 100
19+
bankAccount.deposit 50
20+
expect(bankAccount.balance()).toEqual 150
21+
22+
xit "Withdraw once", ->
23+
bankAccount = new BankAccount
24+
bankAccount.open()
25+
bankAccount.deposit 100
26+
bankAccount.withdraw 75
27+
expect(bankAccount.balance()).toEqual 25
28+
29+
xit "Withdraw twice", ->
30+
bankAccount = new BankAccount
31+
bankAccount.open()
32+
bankAccount.deposit 100
33+
bankAccount.withdraw 80
34+
bankAccount.withdraw 20
35+
expect(bankAccount.balance()).toEqual 0
36+
37+
xit "Can do multiple operations sequentially", ->
38+
bankAccount = new BankAccount
39+
bankAccount.open()
40+
bankAccount.deposit 100
41+
bankAccount.deposit 110
42+
bankAccount.withdraw 200
43+
bankAccount.deposit 60
44+
bankAccount.withdraw 50
45+
expect(bankAccount.balance()).toEqual 20
46+
47+
xit "Cannot check balance of closed account", ->
48+
bankAccount = new BankAccount
49+
bankAccount.open()
50+
bankAccount.close()
51+
expect( -> bankAccount.balance()).toThrow "account not open"
52+
53+
xit "Cannot deposit into closed account", ->
54+
bankAccount = new BankAccount
55+
bankAccount.open()
56+
bankAccount.close()
57+
expect( -> bankAccount.deposit 50).toThrow "account not open"
58+
59+
xit "Cannot deposit into unopened account", ->
60+
bankAccount = new BankAccount
61+
expect( -> bankAccount.deposit 50).toThrow "account not open"
62+
63+
xit "Cannot withdraw from closed account", ->
64+
bankAccount = new BankAccount
65+
bankAccount.open()
66+
bankAccount.close()
67+
expect( -> bankAccount.withdraw 50).toThrow "account not open"
68+
69+
xit "Cannot close an account that was not opened", ->
70+
bankAccount = new BankAccount
71+
expect( -> bankAccount.close()).toThrow "account not open"
72+
73+
xit "Cannot open an already opened account", ->
74+
bankAccount = new BankAccount
75+
bankAccount.open()
76+
expect( -> bankAccount.open()).toThrow "account already open"
77+
78+
79+
xit "Reopened account does not retain balance", ->
80+
bankAccount = new BankAccount
81+
bankAccount.open()
82+
bankAccount.deposit 50
83+
bankAccount.close()
84+
bankAccount.open()
85+
expect(bankAccount.balance()).toEqual 0
86+
87+
xit "Cannot withdraw more than deposited", ->
88+
bankAccount = new BankAccount
89+
bankAccount.open()
90+
bankAccount.deposit 25
91+
expect( -> bankAccount.withdraw 50).toThrow "amount must be less than balance"
92+
93+
xit "Cannot withdraw negative", ->
94+
bankAccount = new BankAccount
95+
bankAccount.open()
96+
bankAccount.deposit 100
97+
expect( -> bankAccount.withdraw -50).toThrow "amount must be greater than 0"
98+
99+
xit "Cannot deposit negative", ->
100+
bankAccount = new BankAccount
101+
bankAccount.open()
102+
expect( -> bankAccount.deposit -50).toThrow "amount must be greater than 0"
103+

0 commit comments

Comments
 (0)