This README was produced by compiling and running the actual codebase end-to-end β not just reading the source. Every command below was executed; every walkthrough step was scripted against the real CLI and its output was captured. A "Verified Findings" section near the end documents a few real discrepancies discovered along the way.
- Requirements
- Getting Started
- Project Architecture
- Running the Automated Test Suite
- Running the Interactive CLI
- Default Seed Data & Validation Rules
- End-to-End Manual Verification Script
- Testing the Two Standalone Bonus Modules
- Exception Reference
- Verified Findings From Full Code Review
- Code Quality Summary
| Tool | Version | Notes |
|---|---|---|
| JDK | 17 or newer | build.gradle sets sourceCompatibility = '17'; the codebase also compiles and runs cleanly under JDK 21 |
| Gradle Wrapper | bundled (8.2) | No local Gradle install needed β always use ./gradlew / .\gradlew.bat |
| Git | any recent version | To clone the repository |
Check your JDK before starting:
java -versiongit clone git@github.com:mxed04/faribank.git
cd faribankCompile everything without running tests yet (useful as a fast sanity check):
# Unix / macOS
./gradlew clean compileJava compileTestJava
# Windows
.\gradlew.bat clean compileJava compileTestJavaIf this succeeds with no errors, the project is ready to test.
src/main/java/ir/ac/kntu/
βββ Main.java # Application entrypoint bootstrapping services and CLI loop
βββ domain/ # Core domain models and business entities
β βββ user/ # User identity, roles, and KYC verification state
β β βββ User.java
β β βββ Customer.java
β β βββ SupportUser.java
β β βββ KycStatus.java # KYC lifecycle: PENDING, APPROVED, REJECTED
β βββ account/ # Bank accounts, credit cards, and ledger transactions
β β βββ Account.java
β β βββ CreditCard.java
β β βββ Transaction.java
β β βββ TransactionType.java # Transaction kinds: CHARGE, TRANSFER_IN, TRANSFER_OUT
β βββ contact/ # User address book entries
β β βββ Contact.java
β βββ ticket/ # Customer support and ticketing domain
β βββ Ticket.java
β βββ TicketSection.java # Ticket sections: CONTACTS, TRANSFER, SETTINGS
β βββ TicketStatus.java # Ticket states: REGISTERED, IN_PROGRESS, CLOSED
βββ repository/ # In-memory thread-safe data persistence layer
β βββ UserRepository.java
β βββ AccountRepository.java
β βββ TransactionRepository.java
β βββ ContactRepository.java
β βββ TicketRepository.java
βββ service/ # Core business logic and operational services
β βββ AuthService.java # Registration, authentication, password verification, KYC checks
β βββ AccountService.java # Account charging, balance lookup, ledger date-filtering
β βββ TransferService.java # Fund transfers, 0.5% fee calculation, mutual contact validation, receipts
β βββ ContactService.java # Address book management and contact privacy toggling
β βββ TicketService.java # Support ticket registration, filtering, and operator replies
β βββ SearchService.java # Levenshtein string similarity engine for fuzzy search (Bonus)
βββ ui/ # Presentation layer (CLI & terminal navigation)
β βββ MenuContext.java # Active session context and logged-in user state tracking
β βββ ConsoleIO.java # Standardized I/O wrapper with ANSI colored output (Bonus)
β βββ menu/ # Menu and submenu hierarchy supporting Back and Quit routing
β β βββ Menu.java # Common menu interface or base abstraction
β β βββ StartMenu.java
β β βββ CustomerMenu.java
β β βββ SupportMenu.java
β β βββ AccountManagementMenu.java
β β βββ TransferMenu.java
β β βββ ContactsMenu.java
β β βββ SupportTicketsMenu.java
β β βββ SettingsMenu.java
β βββ report/ # Financial reporting and analytics output (Bonus)
β βββ HtmlReportGenerator.java # Standalone HTML statement and CSS cash-flow chart generator
βββ util/ # Common utilities and helper classes
β βββ Calendar.java # Simulated system clock utility (Course template)
β βββ PasswordValidator.java # Password strength and complexity enforcement
β βββ MockDataLoader.java # In-memory mock data seeder for local testing
β βββ AnsiColor.java # Terminal ANSI escape formatting sequences
βββ exception/ # Domain-specific business exceptions
βββ FaribankException.java
βββ AuthenticationException.java
βββ KycPendingException.java
βββ InsufficientBalanceException.java
βββ AccountNotFoundException.java
βββ ValidationException.java
flowchart TB
UI["π₯οΈ ui β FaribankCli Β· CustomerCli Β· SupportCli"] --> SVC
SVC["βοΈ service β Auth Β· Account Β· Transfer Β· Contact Β· Settings Β· Ticket Β· Support"] --> REPO
REPO["ποΈ repository β User Β· Account Β· Contact Β· Ticket"] --> DOM
DOM["π§© domain β User Β· Account Β· Transaction Β· Contact Β· Ticket"]
UI -.not wired.-> EXTRA["service.SearchService Β· ui.report.HtmlReportGenerator"]
The dotted line above is intentional β see Verified Findings for details on
SearchServiceandHtmlReportGenerator.
# Unix / macOS
./gradlew clean test
# Windows
.\gradlew.bat clean testResults are written to build/reports/tests/test/index.html β open it in a browser for a readable report. The GitLab CI pipeline (.gitlab-ci.yml) runs this exact command.
Useful for verifying a single phase/module without waiting on the full suite:
| Test Class | Verifies | Command |
|---|---|---|
DomainModelTest |
Core entity invariants (Phase 1) | ./gradlew test --tests "ir.ac.kntu.domain.DomainModelTest" |
AuthServiceTest |
Registration, login, KYC lifecycle (Phase 2) | ./gradlew test --tests "ir.ac.kntu.service.AuthServiceTest" |
AccountServiceTest |
Deposits, balances, ledger sorting/filtering (Phase 3) | ./gradlew test --tests "ir.ac.kntu.service.AccountServiceTest" |
TransferServiceTest |
Transfers, fees, mutual contacts (Phase 4) | ./gradlew test --tests "ir.ac.kntu.service.TransferServiceTest" |
TicketServiceTest |
Ticket lifecycle & filtering (Phase 6) | ./gradlew test --tests "ir.ac.kntu.service.TicketServiceTest" |
SupportServiceTest |
Customer search & summaries (Phase 6) | ./gradlew test --tests "ir.ac.kntu.service.SupportServiceTest" |
ConsoleIoTest |
ANSI codes & input parsing (Phase 7) | ./gradlew test --tests "ir.ac.kntu.ui.ConsoleIoTest" |
SearchServiceTest |
Levenshtein fuzzy search (Bonus) | ./gradlew test --tests "ir.ac.kntu.service.SearchServiceTest" |
HtmlReportGeneratorTest |
HTML statement export (Bonus) | ./gradlew test --tests "ir.ac.kntu.ui.report.HtmlReportGeneratorTest" |
CheckPMDTest |
PMD static analysis (src/main, 0 violations expected) |
./gradlew test --tests "ir.ac.kntu.style.CheckPMDTest" |
CheckStyleTest |
Checkstyle naming + indentation (0 errors expected) | ./gradlew test --tests "ir.ac.kntu.style.CheckStyleTest" |
β οΈ Note:CheckPMDTestandCheckStyleTestare plain JUnit tests (they call the PMD/Checkstyle libraries programmatically), not separate Gradle tasks. There is no./gradlew pmdMainor./gradlew checkstyleMainin this project β the commands above (through./gradlew test --tests ...) are the correct way to run them individually.
Across the whole suite there are 48 @Test methods in 11 test classes β this was counted directly from the source, not estimated.
build.gradle only applies the java plugin (no application plugin), so ./gradlew run does not currently exist as a task in this project. Until the application plugin is added, launch the CLI like this:
# Unix / macOS β compile, then run the compiled classes directly
./gradlew compileJava
java -cp build/classes/java/main ir.ac.kntu.Main
# Windows
.\gradlew.bat compileJava
java -cp build\classes\java\main ir.ac.kntu.Mainπ‘ Optional: add a real ./gradlew run task
Add this to build.gradle to get a proper run task:
plugins {
id 'java'
id 'application'
}
application {
mainClass = 'ir.ac.kntu.Main'
}Then ./gradlew run (or .\gradlew.bat run) will work as expected.
Once running, you'll see the main menu:
=== Welcome to Faribank - Neobank Simulation ===
1) Customer Login
2) Customer Registration
3) Support Operator Login
quit) Exit Faribank
Important navigation detail (confirmed by running the CLI): each customer/support sub-menu (Account Management, Contacts Book, Fund Transfer, Support Inquiries, Account Settings, KYC Verifications, etc.) performs one action and returns immediately to its parent menu β it does not loop internally. To do a second action in the same section (e.g. deposit, then check balance), re-select that menu number again from the parent menu.
| Item | Value / Rule |
|---|---|
| Default operator account | username admin, password Admin@1234 (seeded automatically on startup) |
| Phone number format | 09XXXXXXXXX β exactly 11 digits, starting with 09 |
| National code format | Exactly 10 digits |
| Password complexity | Minimum 6 characters, at least one uppercase, one lowercase, one digit, one special character |
| Credit card PIN | Exactly 4 digits (^\d{4}$) |
| Transfer fee | 0.5% of the transfer amount, added on top of the amount deducted from the sender |
| First account number issued | 100001 (increments per KYC approval) |
| First transaction/ticket IDs | TX-700001 (deposits), TR-800001 (transfers), TCK-900001 (tickets) |
This exact sequence was run against the compiled project and produced the transcript quoted afterward β every step is confirmed working.
| # | Menu | Input | Confirms |
|---|---|---|---|
| 1 | Main β 2 |
Register customer Ali (09121234567 / national code 1234567890 / password Str0ng@Pass) |
Registration & validation (Phase 1β2) |
| 2 | Main β 2 |
Register customer Sara (09129876543 / 0987654321 / Sara@Pass1) |
Duplicate-phone/ID checks work on a second user |
| 3 | Main β 3 |
Login as admin / Admin@1234 |
Support authentication |
| 4 | Support β 1 |
Approve Ali's phone, then Sara's phone | KYC approval, auto account+card issuance |
| 5 | Support β back β Main β 1 |
Login as Ali | Customer login gated on KYC status |
| 6 | Customer β 1 β 2 |
Deposit 10000000 |
chargeAccount (Phase 3) |
| 7 | Customer β 2 β 2 |
Add Sara as a contact | Address book (Phase 5) |
| 8 | Customer β back β Main β 1 |
Login as Sara, 2 β 2, add Ali as a contact |
Mutual-contact precondition for Step 9 |
| 9 | Customer β back β Main β 1 |
Login as Ali, 3 β 2 β Sara's phone β 2000000 β Y |
Contact-based transfer + 0.5% fee (Phase 4) |
| 10 | Customer β 4 β 1 |
Section 2 (TRANSFER), describe an issue |
Ticket creation (Phase 6) |
| 11 | Customer β 5 β 1 |
Change password | Security settings (Phase 5) |
| 12 | Customer β back β Main β 3 |
Login as admin, 2 |
Reply to the ticket, mark it CLOSED |
| 13 | Support β 3 |
Search for Ali |
Save this as smoke-test-input.txt:
2
Ali
Rezaei
09121234567
1234567890
Str0ng@Pass
2
Sara
Ahmadi
09129876543
0987654321
Sara@Pass1
3
admin
Admin@1234
1
09121234567
A
1
09129876543
A
back
1
09121234567
Str0ng@Pass
1
2
10000000
2
2
Sara
Ahmadi
09129876543
back
1
09129876543
Sara@Pass1
2
2
Ali
Rezaei
09121234567
back
1
09121234567
Str0ng@Pass
3
2
09129876543
2000000
Y
4
1
2
Transfer button is not visible on mobile app
back
3
admin
Admin@1234
2
TCK-900001
Thanks for reporting, we are looking into it.
2
3
Ali
back
quit
Then run:
java -cp build/classes/java/main ir.ac.kntu.Main < smoke-test-input.txtConfirmed output highlights from this exact run:
[SUCCESS] KYC Approved. Account & card generated for: 09121234567 (account 100001)
[SUCCESS] KYC Approved. Account & card generated for: 09129876543 (account 100002)
[SUCCESS] Deposit successful. Tracking ID: TX-700001
[SUCCESS] Contact added successfully: Sara Ahmadi
Tracking Number: TR-800001 | Amount: 2000000.0 | Fee: 10000.0 | Total Deduction: 2010000.0
[SUCCESS] Ticket registered. ID: TCK-900001
[SUCCESS] Password updated successfully.
[SUCCESS] Ticket reply saved and updated successfully.
No users found matching query: Ali β see Verified Findings below
The fee math checks out exactly: 2,000,000 Γ 0.5% = 10,000, so 2,010,000 total is debited from Ali while Sara is credited 2,000,000.
SearchService (Levenshtein fuzzy search) and HtmlReportGenerator (HTML statements) are fully implemented and unit-tested, but β confirmed by inspecting Main.java and BankServices.java β neither is wired into any CLI menu. The only ways to exercise them are their unit tests, or calling them directly. jshell (bundled with the JDK) is the fastest way to do the latter without writing a throwaway class:
./gradlew compileJava
jshell --class-path build/classes/java/mainimport ir.ac.kntu.service.SearchService;
import ir.ac.kntu.repository.*;
var ss = new SearchService(new UserRepository(), new ContactRepository());
ss.calculateLevenshtein("kitten", "sitting") // β 3 (verified correct)
ss.computeScore("Mohammad", "Mohamad") // β 0.875
ss.computeScore("Faribank", "bank") // β 0.85 (substring match)import ir.ac.kntu.repository.*;
import ir.ac.kntu.service.*;
import ir.ac.kntu.domain.user.Customer;
import ir.ac.kntu.ui.report.HtmlReportGenerator;
import java.io.File;
var userRepo = new UserRepository();
var accountRepo = new AccountRepository();
var contactRepo = new ContactRepository();
var auth = new AuthService(userRepo, accountRepo);
var acc = new AccountService(accountRepo, userRepo);
var c = new Customer("Ali", "Rezaei", "09121234567", "1234567890", "Str0ng@Pass");
auth.registerCustomer(c);
auth.approveKyc("09121234567");
acc.chargeAccount("09121234567", 5000000);
var gen = new HtmlReportGenerator();
gen.exportToFile(userRepo.findCustomerByPhone("09121234567").get(), new File("statement.html"));Open the generated statement.html in a browser to see the profile header, balance cards, cash-flow bar chart, and transaction table β this exact snippet was run to confirm it produces valid, styled HTML.
All Faribank exceptions are unchecked and extend a single root, so any UI layer can catch one type and handle every domain error consistently:
RuntimeException
βββ FaribankException (root)
βββ ValidationException general invariant/business-rule violations
βββ AuthenticationException invalid login credentials
βββ UserAlreadyExistsException duplicate phone/national code on registration
βββ AccountNotFoundException missing account, or KYC not approved
βββ InsufficientFundsException balance < amount + fee
βββ TicketNotFoundException unknown ticket ID
These were discovered by actually compiling and exercising the code (not just reading it), and are worth knowing before you rely on these features:
| # | Area | What was verified |
|---|---|---|
| 1 | Support user search is non-functional via the CLI | SupportCli.handleUserSearch() passes the same single query string as phone, first, and last to SupportService.searchCustomers, which requires all three to match (AND logic). In practice, searching by phone-only, first-name-only, or last-name-only all return "No users found" β confirmed by running all three cases. The underlying SupportService method itself is fine if called with only the relevant field populated; the bug is in how SupportCli invokes it. |
| 2 | HtmlReportGenerator misclassifies incoming transfers as outflow |
TransactionType only has CHARGE and TRANSFER (no TRANSFER_IN), but the chart/table code checks "TRANSFER_IN".equals(type.name()) β which can never be true. Confirmed by generating a report for a customer who received a transfer: their statement shows "Inflow: 0.0 IRR (0%) / Outflow: 100%" even though they only ever received money. |
| 3 | ContactService and SettingsService have no unit tests |
Confirmed by searching every test file for these class names β neither appears anywhere in src/test. Earlier phase documentation referenced ContactServiceTest/SettingsServiceTest, but these files do not exist in this codebase; the only way to exercise those two services is the CLI walkthrough above. |
| 4 | No ./gradlew run task |
build.gradle applies only the java plugin; there's no application plugin or mainClass. Covered above with a working alternative and an optional fix. |
| 5 | Large amounts print in scientific notation | Balances/amounts are printed via raw double string concatenation, so e.g. 10,000,000 displays as 1.0E7 IRR in the CLI. Not a bug, just worth expecting when eyeballing output. |
| 6 | AccountService.filterTransactions, findTransaction, and findAccountByNumber aren't exposed in the CLI |
CustomerCli's Account Management menu only offers balance/deposit/full history β date-range filtering and tracking-ID lookup exist and are unit-tested, but are only reachable programmatically or via AccountServiceTest. |
None of these block the build or the test suite β ./gradlew test still passes cleanly β but they're the kind of thing worth knowing before a live demo.
| Aspect | Result |
|---|---|
| Compilation | src/main compiles cleanly with JDK 17 and JDK 21 |
| Automated tests | 48 @Test methods across 11 classes |
| Static analysis | CheckPMDTest and CheckStyleTest both assert zero violations against src/main |
| CI | .gitlab-ci.yml runs ./gradlew clean test on every push |