Skip to content

Sync bank-account tests with problem specifications - #541

Open
ManasDasri wants to merge 1 commit into
exercism:mainfrom
ManasDasri:sync-bank-account
Open

Sync bank-account tests with problem specifications#541
ManasDasri wants to merge 1 commit into
exercism:mainfrom
ManasDasri:sync-bank-account

Conversation

@ManasDasri

Copy link
Copy Markdown
Contributor

Description

This PR synchronizes the bank-account practice exercise with the
latest canonical test data from the Exercism problem-specifications
repository.

During the synchronization process, the existing VB.NET exercise was
compared directly against the canonical bank-account specification
rather than relying solely on the existing .meta/tests.toml
representation.

This comparison revealed that the existing exercise was based on an
older API and behavioral model centered around:

UpdateBalance(change As Decimal)

The current canonical specification instead models deposits and
withdrawals as distinct operations and defines explicit behavior for
account lifecycle, invalid operations, balance handling, insufficient
funds, and concurrent transactions.

Because the existing API could not faithfully represent the current
canonical behavior, this PR updates the exercise API, example solution,
test suite, and canonical test metadata together.


Motivation

The purpose of this change is to bring the VB.NET bank-account
exercise into alignment with the current canonical specification.

The existing implementation treated positive and negative balance
changes through a single method:

UpdateBalance(change As Decimal)

For example:

account.UpdateBalance(100D)

could be used to deposit funds, while:

account.UpdateBalance(-50D)

could be used to withdraw funds.

This model no longer matches the canonical specification.

The canonical exercise explicitly distinguishes:

Deposit
Withdraw

and defines separate validation rules for each operation.

For example:

Deposit(-50)

must be rejected as an invalid deposit rather than being interpreted as
a withdrawal.

Likewise:

Withdraw(-50)

must be rejected rather than increasing the account balance.

The canonical specification also defines account lifecycle behavior that
the old API did not model explicitly.

Therefore, simply adding the newly generated canonical tests would not
have been sufficient. The exercise itself needed to be updated so that
the canonical behavior could be represented correctly.


Canonical Test Synchronization

The current canonical bank-account specification contains 17 test
cases
.

The canonical data was inspected directly from:

problem-specifications/exercises/bank-account/canonical-data.json

The existing track metadata was then synchronized using configlet.

The resulting:

exercises/practice/bank-account/.meta/tests.toml

contains the canonical test UUIDs and descriptions.

No canonical cases were intentionally excluded.

The metadata was verified with:

./bin/configlet sync -e bank-account --tests

which reports:

The `bank-account` exercise has up-to-date tests!

This confirms that the track metadata is synchronized with the canonical
test data.


API Changes

Previous API

The previous exercise exposed:

Open()
Close()
Balance
UpdateBalance(change As Decimal)

The important limitation was that deposits and withdrawals were
represented using the same operation.

For example:

account.UpdateBalance(100D)

represented a deposit, while:

account.UpdateBalance(-50D)

represented a withdrawal.

This made it impossible to faithfully represent canonical rules such as:

A negative deposit is invalid.
A negative withdrawal is invalid.
A withdrawal cannot exceed the current balance.

The single signed UpdateBalance parameter blurred the distinction
between these operations.


New API

The exercise now exposes:

Open()
Close()

Deposit(amount As Decimal)
Withdraw(amount As Decimal)

Balance As Decimal

This matches the conceptual model used by the canonical specification.

The API is now consistent across:

  • BankAccount.vb
  • .meta/Example.vb
  • BankAccountTests.vb

The old:

UpdateBalance(...)

API has been removed from the exercise implementation and test suite.


BankAccount.vb

The student-facing exercise stub was updated to expose the new API.

The stub now requires students to implement:

Open()
Close()
Deposit(amount)
Withdraw(amount)
Balance

The methods remain intentionally unimplemented for students.

The purpose of this file is to provide the interface students are
expected to implement.

The old:

UpdateBalance()

method is no longer exposed.

This is important because the student-facing API must correspond to the
operations exercised by the canonical tests.


.meta/Example.vb

The example solution was substantially updated because the old
implementation could not satisfy the canonical behavior.

The new implementation maintains account state and protects it for
concurrent operations.

The implementation now handles:

  • account open/closed state
  • account lifecycle validation
  • balance initialization
  • deposits
  • withdrawals
  • invalid amounts
  • insufficient balance
  • reopening
  • concurrent transactions

The implementation also uses synchronization around shared account state
so that concurrent operations cannot corrupt the balance.


Account Lifecycle

Opening an account

Calling:

account.Open()

on a closed account opens the account and initializes its balance to:

0

The implementation therefore guarantees that every newly opened account
starts with a clean balance.


Opening an already-open account

Calling:

account.Open()

when the account is already open throws:

account already open

This corresponds to the canonical "already open" behavior.


Closing an account

Calling:

account.Close()

on an open account closes the account and resets the internal balance.

The balance is therefore not retained between separate account sessions.


Closing an unopened account

Calling:

account.Close()

before the account has been opened throws:

account not open

This is explicitly covered by the test suite.


Reopening an Account

The canonical specification requires that reopening an account starts a
new account session with a zero balance.

For example:

Open
Deposit 100
Close
Open

must result in:

Balance = 0

The test suite explicitly verifies this behavior.

This prevents a previous account balance from surviving a close/open
cycle.


Balance Behavior

The Balance property represents the balance of an open account.

Checking the balance while the account is closed is invalid.

For example:

Dim account As New BankAccount()

account.Open()
account.Close()

Dim balance = account.Balance

throws:

account not open

The test intentionally opens and then closes the account so that it
tests the closed state rather than an account that was never opened.


Deposit Behavior

The new:

Deposit(amount)

operation adds funds to the account.

For example:

account.Open()
account.Deposit(100D)

results in:

Balance = 100

Multiple deposits accumulate:

account.Deposit(100D)
account.Deposit(50D)

results in:

Balance = 150

Deposits are rejected when the account is not open.


Negative Deposits

The canonical specification explicitly rejects negative deposits.

For example:

account.Deposit(-50D)

must throw:

amount must be greater than 0

This is one of the reasons the old UpdateBalance API could not simply
be retained.

Under the old API, a negative value could be interpreted as a
withdrawal.

Under the new API:

Deposit(-50)

is unambiguously an invalid deposit.


Withdrawal Behavior

The new:

Withdraw(amount)

operation subtracts funds from the current balance.

For example:

account.Open()
account.Deposit(100D)
account.Withdraw(40D)

results in:

Balance = 60

Multiple withdrawals are supported while sufficient funds remain.


Negative Withdrawals

Negative withdrawals are invalid.

For example:

account.Withdraw(-50D)

throws:

amount must be greater than 0

This prevents a negative withdrawal from accidentally behaving like a
deposit.


Withdrawals Greater Than the Balance

The implementation prevents the account from becoming overdrawn.

For example:

account.Open()
account.Deposit(25D)
account.Withdraw(50D)

throws:

amount must be less than balance

The old UpdateBalance model did not naturally enforce this distinction
because a negative balance change was simply another signed adjustment.

The new API allows the canonical rule to be expressed directly.


Operations on Closed Accounts

The canonical specification requires operations on closed accounts to
fail.

The test suite explicitly distinguishes a closed account from an account
that has never been opened.

Deposit after closing

account.Open()
account.Close()
account.Deposit(50D)

results in:

account not open

Withdrawal after closing

account.Open()
account.Close()
account.Withdraw(50D)

also results in:

account not open

Checking balance after closing

account.Open()
account.Close()

Dim balance = account.Balance

results in:

account not open

These tests intentionally call Open() followed by Close() so that
they actually test the closed state.


Unopened Account Behavior

The canonical specification also contains cases where operations are
attempted on an account that has never been opened.

For example:

Dim account As New BankAccount()

account.Deposit(50D)

must fail with:

account not open

This is intentionally kept as a separate test from the closed-account
case.

The distinction is important because:

unopened account

and:

opened → closed account

are different state transitions, even though both reject operations.


Sequential Operations

The test suite covers combinations of operations.

For example:

account.Open()
account.Deposit(100D)
account.Withdraw(25D)
account.Deposit(50D)
account.Withdraw(25D)

must result in:

Balance = 100

This verifies that the implementation maintains state correctly across
multiple sequential operations rather than only handling isolated
deposits or withdrawals.


Concurrency

The existing exercise already had a concurrency test, but it was based
on the old UpdateBalance API.

The old pattern effectively performed:

UpdateBalance(+1)
UpdateBalance(-1)

repeated concurrently.

That test was updated to use the canonical API:

Deposit(1)
Withdraw(1)

The updated test launches 1,000 concurrent tasks.

Each task performs:

account.Deposit(1D)
account.Withdraw(1D)

The expected final balance is:

0

This directly exercises the canonical concurrency behavior.


Thread Safety

The example implementation protects shared account state using
SyncLock.

Operations that read or modify the account state are synchronized.

This prevents race conditions between concurrent deposits and
withdrawals.

The synchronization is particularly important because the canonical test
intentionally performs operations from multiple concurrent tasks.

Without synchronization, simultaneous balance updates could result in
lost updates or an incorrect final balance.


Exact Error Messages

The example implementation uses the canonical error messages rather than
introducing track-specific alternatives.

The following messages are used:

account not open
account already open
amount must be greater than 0
amount must be less than balance

The test suite verifies the exception messages explicitly.

For example:

Dim exception = Assert.Throws(Of InvalidOperationException)(
    Sub() account.Deposit(-50D)
)

Assert.Equal("amount must be greater than 0", exception.Message)

This ensures that the implementation is not merely throwing an
exception, but is matching the expected canonical behavior.


Imports in BankAccountTests.vb

The updated test suite uses types from namespaces that were not required
by the original test file.

The test file therefore explicitly imports the namespaces it uses:

Imports System.Threading.Tasks
Imports Xunit

System.Threading.Tasks is required for the concurrency coverage,
including:

Task
Task.Run
Task.WaitAll

The Task API is used to execute the canonical concurrent
deposit/withdraw operations.

Xunit provides the test framework APIs used throughout the file,
including:

Fact
Assert
Assert.Throws
Assert.Equal

The original test file did not require these explicit imports in the
same way because the previous track/project setup already allowed the
original tests to resolve the required test framework types.

The imports are therefore part of the updated test file's requirements
rather than unrelated formatting changes.


BankAccountTests.vb

The existing test suite was rewritten around the new API.

The previous tests were based on the old UpdateBalance behavior and
included cases where negative balance changes were used to represent
withdrawals.

Those tests were replaced with explicit deposit and withdrawal
operations.

The final suite contains 17 active tests covering the canonical
behavior.

The tests verify:

  1. An opened account starts with a zero balance.
  2. A single deposit updates the balance.
  3. Multiple deposits accumulate correctly.
  4. A withdrawal reduces the balance correctly.
  5. Multiple withdrawals accumulate correctly.
  6. Depositing into a closed account fails.
  7. Depositing into an unopened account fails.
  8. Withdrawing from a closed account fails.
  9. Checking the balance of a closed account fails.
  10. Multiple sequential operations produce the expected balance.
  11. Closing an unopened account fails.
  12. Opening an already-open account fails.
  13. Reopening an account resets its balance.
  14. Withdrawing more than the available balance fails.
  15. Negative withdrawals fail.
  16. Negative deposits fail.
  17. Concurrent deposit/withdraw operations complete with the expected
    balance.

All tests are active.

No tests are skipped.


Canonical Case Mapping

The final test suite intentionally distinguishes cases that can
otherwise appear superficially similar.

For example:

Closed account

account.Open()
account.Close()
account.Deposit(50D)

tests the behavior of an account that was opened and subsequently
closed.

Unopened account

Dim account As New BankAccount()
account.Deposit(50D)

tests the behavior of an account that has never been opened.

Both operations produce:

account not open

but they represent different canonical state transitions and therefore
remain separate tests.

This distinction was important during the synchronization because simply
counting test methods would not guarantee that all canonical behaviors
were represented correctly.


Metadata

The .meta/tests.toml file was synchronized using configlet.

The file contains the canonical UUIDs and descriptions for all 17 cases.

The generated metadata was checked rather than manually treating the
previous metadata as authoritative.

No canonical test cases were excluded.

The resulting metadata was verified with:

./bin/configlet sync -e bank-account --tests

and configlet reports:

The `bank-account` exercise has up-to-date tests!

Verification

The complete exercise test suite was run with:

pwsh ./bin/test.ps1 bank-account

Result:

Test summary: total: 17, failed: 0, succeeded: 17, skipped: 0
Build succeeded

The canonical test metadata was also verified with:

./bin/configlet sync -e bank-account --tests

Result:

The `bank-account` exercise has up-to-date tests!

The staged changes were checked with:

git diff --cached --check

No whitespace errors were reported.


Files Changed

exercises/practice/bank-account/.meta/tests.toml

  • Added/synchronized the canonical test metadata.
  • Preserved canonical UUIDs.
  • Preserved canonical descriptions.
  • Represented all 17 canonical cases.
  • No canonical cases intentionally excluded.

exercises/practice/bank-account/BankAccount.vb

  • Replaced the old UpdateBalance-based student API.
  • Added the canonical Open operation.
  • Added the canonical Close operation.
  • Added the canonical Deposit operation.
  • Added the canonical Withdraw operation.
  • Added the canonical Balance property.

exercises/practice/bank-account/.meta/Example.vb

  • Reworked the example implementation around the canonical API.
  • Added explicit account state management.
  • Added account lifecycle validation.
  • Added deposit validation.
  • Added withdrawal validation.
  • Added insufficient-balance protection.
  • Added balance reset behavior when reopening.
  • Added synchronization for concurrent operations.
  • Preserved canonical error messages.

exercises/practice/bank-account/BankAccountTests.vb

  • Removed the old UpdateBalance-based tests.
  • Replaced positive/negative balance adjustments with explicit
    deposits and withdrawals.
  • Added account lifecycle coverage.
  • Added closed-account coverage.
  • Added unopened-account coverage.
  • Added negative amount validation.
  • Added insufficient-balance validation.
  • Added sequential transaction coverage.
  • Updated concurrency coverage to use the canonical API.
  • Added the necessary namespace imports for the updated test suite.
  • Provides coverage for all 17 canonical behaviors.

Verification Summary

Check Result


Canonical test cases 17
Active VB.NET tests 17
Tests passed 17
Tests failed 0
Tests skipped 0
Build Successful
configlet sync Up to date
git diff --cached --check Clean
Canonical cases excluded 0


Conclusion

This PR brings the VB.NET bank-account practice exercise into
alignment with the current canonical specification.

The primary behavioral change is replacing the legacy UpdateBalance
model with explicit Deposit and Withdraw operations. This allows the
exercise to correctly represent the canonical validation rules, account
lifecycle semantics, insufficient-balance behavior, and concurrent
transaction behavior.

The student-facing API, example solution, tests, and canonical metadata
have all been updated consistently.

The final exercise contains all 17 canonical test cases, with all 17
tests passing successfully.

configlet confirms that the test metadata is synchronized with the
current problem-specifications data.

Final verification

17 tests passed
0 tests failed
0 tests skipped
Build succeeded
Canonical test metadata up to date

This synchronization is therefore complete and ready for review.

Closes #423

@github-actions

Copy link
Copy Markdown

This PR touches files which potentially affect the outcome of the tests of an exercise. This will cause all students' solutions to affected exercises to be re-tested.

If this PR does not affect the result of the test (or, for example, adds an edge case that is not worth rerunning all tests for), please add the following to the merge-commit message which will stops student's tests from re-running. Please copy-paste to avoid typos.

[no important files changed]

For more information, refer to the documentation. If you are unsure whether to add the message or not, please ping @exercism/maintainers-admin in a comment. Thank you!

@github-actions

Copy link
Copy Markdown

Hello. Thanks for opening a PR on Exercism 🙂

We ask that all changes to Exercism are discussed on our Community Forum before being opened on GitHub. To enforce this, we automatically close all PRs that are submitted. That doesn't mean your PR is rejected but that we want the initial discussion about it to happen on our forum where a wide range of key contributors across the Exercism ecosystem can weigh in.

You can use this link to copy this into a new topic on the forum. If we decide the PR is appropriate, we'll reopen it and continue with it, so please don't delete your local branch.

If you're interested in learning more about this auto-responder, please read this blog post.


Note: If this PR has been pre-approved, please link back to this PR on the forum thread and a maintainer or staff member will reopen it.

@github-actions github-actions Bot closed this Aug 12, 2026
@github-actions

Copy link
Copy Markdown

Hello 👋 Thanks for your PR.

This repo does not currently have dedicated maintainers. Our cross-track maintainers team will attempt to review and merge your PR, but it will likely take longer for your PR to be reviewed.

If you enjoy contributing to Exercism and have a track-record of doing so successfully, you might like to become an Exercism maintainer for this track.

Please feel free to ask any questions, or chat to us about anything to do with this PR or the reviewing process on the Exercism forum.

(cc @exercism/cross-track-maintainers)

@github-actions

Copy link
Copy Markdown

This is an unmaintained repository.

Cross-track maintainers - feel free to merge.

@BNAndras BNAndras reopened this Aug 13, 2026
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.

Sync bank-account with problem specifications

2 participants