From 4228d66dfb5ee2466125b8311d265e8f71f4009d Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:35:49 +0000 Subject: [PATCH 1/5] GitHub Classroom Feedback --- .github/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/.keep diff --git a/.github/.keep b/.github/.keep new file mode 100644 index 0000000..e69de29 From 59fde1b443dd731c0ed2458b3be27a88595f4475 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:35:49 +0000 Subject: [PATCH 2/5] Setting up GitHub Classroom Feedback From 2dadbfd0ace8eebcac4ddf92df3e1318f6f7267e Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:35:52 +0000 Subject: [PATCH 3/5] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cab3c08..76507d5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/W3nV4mdD) # Banking management ## Overview From 3ec6a1049ccd9a78ee8d27ce438b45ce0fccb8d6 Mon Sep 17 00:00:00 2001 From: shreya-mishra11 Date: Thu, 21 Aug 2025 23:00:03 +0530 Subject: [PATCH 4/5] implement Bank, BankAccount, User, and TransactionService classes --- src/models/bank-account.ts | 26 ++++++++++++++++++++++++++ src/models/bank.ts | 26 ++++++++++++++++++++++++++ src/models/user.ts | 11 +++++++++++ src/services/TransactionService.ts | 2 ++ 4 files changed, 65 insertions(+) create mode 100644 src/models/bank-account.ts create mode 100644 src/models/bank.ts create mode 100644 src/models/user.ts create mode 100644 src/services/TransactionService.ts diff --git a/src/models/bank-account.ts b/src/models/bank-account.ts new file mode 100644 index 0000000..9646b3f --- /dev/null +++ b/src/models/bank-account.ts @@ -0,0 +1,26 @@ +class BankAccount { + private id: string; + private balance: number; + + constructor(initialBalance: number) { + this.id = crypto.randomUUID(); + this.balance = initialBalance; + } + + getId(): string { + return this.id; + } + + getBalance(): number { + return this.balance; + } + + deposit(amount: number): void { + this.balance += amount; + } + + withdraw(amount: number): void { + this.balance -= amount; + } +} +export default BankAccount; diff --git a/src/models/bank.ts b/src/models/bank.ts new file mode 100644 index 0000000..81565bc --- /dev/null +++ b/src/models/bank.ts @@ -0,0 +1,26 @@ +import BankAccount from "./bank-account"; + +class Bank { + private id: string; + private accounts: BankAccount[] = []; + private balance: number; + constructor() { + this.id = crypto.randomUUID(); + this.balance = 0; + } + + // this is static as it creates a new instance of the Bank class also [const bank = Bank.create()] + static create(): Bank { + return new Bank(); + } + // this is not static as it operates on an instance of the Bank class [bank.getId()] + getId(): string { + return this.id; + } + createAccount(initialBalance: number): BankAccount { + const account = new BankAccount(initialBalance); + this.accounts.push(account); + return account; + } +} +export default Bank; diff --git a/src/models/user.ts b/src/models/user.ts new file mode 100644 index 0000000..7f1fb9d --- /dev/null +++ b/src/models/user.ts @@ -0,0 +1,11 @@ +class User { + constructor(private name: string, private bankAccountIds: string[]) { + this.name = name; + this.bankAccountIds = bankAccountIds; + } + + private static create(): User { + return new User("", []); + } +} +export default User; diff --git a/src/services/TransactionService.ts b/src/services/TransactionService.ts new file mode 100644 index 0000000..46a245e --- /dev/null +++ b/src/services/TransactionService.ts @@ -0,0 +1,2 @@ +class TransactionService {} +export default TransactionService; From 21f2c54ce630c56f51587b2f82dfa18576de1ab1 Mon Sep 17 00:00:00 2001 From: shreya-mishra11 Date: Fri, 22 Aug 2025 07:28:21 +0530 Subject: [PATCH 5/5] Refactor banking system: implement user, bank, and transaction models; enhance account management; add global registry for user and bank management; improve test fixtures and tests. --- src/__tests__/real.test.ts | 35 ++++++---- src/__tests__/simple.test.ts | 46 +++++++------ src/helpers/TestFactory.ts | 31 +++++++++ src/models/bank-account.ts | 37 ++++++++-- src/models/bank.ts | 104 +++++++++++++++++++++++++---- src/models/index.ts | 3 + src/models/user.ts | 46 +++++++++++-- src/services/GlobalRegistry.ts | 33 +++++++++ src/services/TransactionService.ts | 36 +++++++++- src/types/common.ts | 2 + 10 files changed, 311 insertions(+), 62 deletions(-) create mode 100644 src/helpers/TestFactory.ts create mode 100644 src/models/index.ts create mode 100644 src/services/GlobalRegistry.ts create mode 100644 src/types/common.ts diff --git a/src/__tests__/real.test.ts b/src/__tests__/real.test.ts index 443229c..c958918 100644 --- a/src/__tests__/real.test.ts +++ b/src/__tests__/real.test.ts @@ -1,16 +1,17 @@ -import { describe, beforeEach, it, expect } from 'vitest'; -import { TestFactory } from './helpers/TestFactory'; -import type { TestFixtures } from './helpers/TestFactory'; +import { describe, beforeEach, it, expect } from "vitest"; +import { TestFactory } from "./helpers/TestFactory"; +import type { TestFixtures } from "./helpers/TestFactory"; -describe('Bank Transfer Tests', () => { +describe("Bank Transfer Tests", () => { let fixtures: TestFixtures; beforeEach(() => { fixtures = TestFactory.createFixtures(); }); - it('should allow transfer between accounts', () => { - const { bank, aliceUserId, bobUserId, aliceAccountId, bobAccountId } = fixtures; + it("should allow transfer between accounts", () => { + const { bank, aliceUserId, bobUserId, aliceAccountId, bobAccountId } = + fixtures; // Initial balances const aliceAccount = bank.getAccount(aliceAccountId); @@ -26,19 +27,27 @@ describe('Bank Transfer Tests', () => { expect(bobAccount.getBalance()).toBe(800); }); - - it('should not allow transfer with insufficient funds', () => { + it("should not allow transfer with insufficient funds", () => { const { bank, aliceUserId, bobUserId } = fixtures; expect(() => { bank.send(aliceUserId, bobUserId, 2000); - }).toThrow('Insufficient funds'); + }).toThrow("Insufficient funds"); }); - it('should allow transfer with negative balance', () => { - const { bank, bankAllowsNegative, aliceUserId, bobUserId, aliceAccountAllowsNegativeId, bobAccountId } = fixtures; - - const aliceAccountAllowsNegative = bankAllowsNegative.getAccount(aliceAccountAllowsNegativeId); + it("should allow transfer with negative balance", () => { + const { + bank, + bankAllowsNegative, + aliceUserId, + bobUserId, + aliceAccountAllowsNegativeId, + bobAccountId, + } = fixtures; + + const aliceAccountAllowsNegative = bankAllowsNegative.getAccount( + aliceAccountAllowsNegativeId + ); const bobBankId = bank.getId(); const bobAccount = bank.getAccount(bobAccountId); diff --git a/src/__tests__/simple.test.ts b/src/__tests__/simple.test.ts index 49bb5f6..2d9ad42 100644 --- a/src/__tests__/simple.test.ts +++ b/src/__tests__/simple.test.ts @@ -1,57 +1,59 @@ -import { describe, it, expect } from 'vitest'; -import User from '@/models/user'; -import Bank from '@/models/bank'; -import BankAccount from '@/models/bank-account'; -import TransactionService from '@/services/TransactionService'; - - -describe('Banks', () => { - it('has a bank class', () => { +import { describe, it, expect } from "vitest"; +import User from "@/models/user"; +import Bank from "@/models/bank"; +import BankAccount from "@/models/bank-account"; +import TransactionService from "@/services/TransactionService"; + +describe("Banks", () => { + it("has a bank class", () => { expect(Bank).toBeDefined(); }); - it('can create a bank', () => { + it("can create a bank", () => { const bank = Bank.create(); expect(bank.getId()).toBeDefined(); }); - it('can create a bank account', () => { + it("can create a bank account", () => { const bank = Bank.create(); const bankAccount = bank.createAccount(100); expect(bankAccount.getId()).toBeDefined(); }); }); - -describe('Users', () => { - it('has a user class', () => { +describe("Users", () => { + it("has a user class", () => { expect(User).toBeDefined(); }); - it('can create a user', () => { - const user = User.create('Firstname Lastname', []); + it("can create a user", () => { + const user = User.create("Firstname Lastname", []); expect(user).toBeDefined(); }); - it('can create a user with accounts', () => { + it("can create a user with accounts", () => { const bank = Bank.create(); const bankAccount1Id = bank.createAccount(100).getId(); const bankAccount2Id = bank.createAccount(200).getId(); const bankAccount3Id = bank.createAccount(300).getId(); - const user = User.create('Firstname Lastname', [bankAccount1Id, bankAccount2Id, bankAccount3Id]); + const user = User.create("Firstname Lastname", [ + bankAccount1Id, + bankAccount2Id, + bankAccount3Id, + ]); expect(user).toBeDefined(); }); }); -describe('Accounts', () => { - it('has an account class', () => { +describe("Accounts", () => { + it("has an account class", () => { expect(BankAccount).toBeDefined(); }); }); -describe('Transactions', () => { - it('has a transaction service', () => { +describe("Transactions", () => { + it("has a transaction service", () => { expect(TransactionService).toBeDefined(); }); }); diff --git a/src/helpers/TestFactory.ts b/src/helpers/TestFactory.ts new file mode 100644 index 0000000..b8cc876 --- /dev/null +++ b/src/helpers/TestFactory.ts @@ -0,0 +1,31 @@ +// ...existing imports... + +export class TestFactory { + static createFixtures(): TestFixtures { + GlobalRegistry.clear(); + + // Create users first + const aliceUser = User.create("Alice"); + const bobUser = User.create("Bob"); + + // Create accounts and associate with users + const aliceAccount = bank.createAccount(1000, aliceUser.getId()); + const bobAccount = bank.createAccount(500, bobUser.getId()); + const aliceAccountAllowsNegative = bankAllowsNegative.createAccount( + 200, + aliceUser.getId() + ); + + // ...rest of your code... + + return { + bank, + bankAllowsNegative, + aliceUserId: aliceUser.getId(), + bobUserId: bobUser.getId(), + aliceAccountId: aliceAccount.getId(), + bobAccountId: bobAccount.getId(), + aliceAccountAllowsNegativeId: aliceAccountAllowsNegative.getId(), + }; + } +} diff --git a/src/models/bank-account.ts b/src/models/bank-account.ts index 9646b3f..f193021 100644 --- a/src/models/bank-account.ts +++ b/src/models/bank-account.ts @@ -1,13 +1,21 @@ -class BankAccount { - private id: string; +import { BankAccountId } from "@/types/Common"; + +export default class BankAccount { + private id: BankAccountId; private balance: number; + private isNegativeAllowed: boolean; - constructor(initialBalance: number) { - this.id = crypto.randomUUID(); + constructor(initialBalance: number, isNegativeAllowed: boolean = false) { + this.id = this.generateId(); this.balance = initialBalance; + this.isNegativeAllowed = isNegativeAllowed; + } + + private generateId(): BankAccountId { + return `account_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } - getId(): string { + getId(): BankAccountId { return this.id; } @@ -16,11 +24,28 @@ class BankAccount { } deposit(amount: number): void { + if (amount <= 0) { + throw new Error("Deposit amount must be positive"); + } this.balance += amount; } withdraw(amount: number): void { + if (amount <= 0) { + throw new Error("Withdrawal amount must be positive"); + } + + if (!this.isNegativeAllowed && this.balance < amount) { + throw new Error("Insufficient funds"); + } + this.balance -= amount; } + + setBalance(balance: number): void { + if (!this.isNegativeAllowed && balance < 0) { + throw new Error("Negative balance not allowed"); + } + this.balance = balance; + } } -export default BankAccount; diff --git a/src/models/bank.ts b/src/models/bank.ts index 81565bc..3e79c04 100644 --- a/src/models/bank.ts +++ b/src/models/bank.ts @@ -1,26 +1,104 @@ +import { BankAccountId, UserId } from "@/types/Common"; import BankAccount from "./bank-account"; +import GlobalRegistry from "@/services/GlobalRegistry"; -class Bank { +interface BankOptions { + isNegativeAllowed?: boolean; +} + +export default class Bank { private id: string; - private accounts: BankAccount[] = []; - private balance: number; - constructor() { - this.id = crypto.randomUUID(); - this.balance = 0; + private accounts: Map; + private isNegativeAllowed: boolean; + + constructor(options: BankOptions = {}) { + this.id = this.generateId(); + this.accounts = new Map(); + this.isNegativeAllowed = options.isNegativeAllowed || false; + GlobalRegistry.registerBank(this); } - // this is static as it creates a new instance of the Bank class also [const bank = Bank.create()] - static create(): Bank { - return new Bank(); + private generateId(): string { + return `bank_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } - // this is not static as it operates on an instance of the Bank class [bank.getId()] + + static create(options?: BankOptions): Bank { + return new Bank(options); + } + getId(): string { return this.id; } + createAccount(initialBalance: number): BankAccount { - const account = new BankAccount(initialBalance); - this.accounts.push(account); + const account = new BankAccount(initialBalance, this.isNegativeAllowed); + this.accounts.set(account.getId(), account); + return account; + } + + getAccount(accountId: BankAccountId): BankAccount { + const account = this.accounts.get(accountId); + if (!account) { + throw new Error(`Account ${accountId} not found`); + } return account; } + + hasAccount(accountId: BankAccountId): boolean { + return this.accounts.has(accountId); + } + + send( + fromUserId: UserId, + toUserId: UserId, + amount: number, + toBankId?: string + ): void { + const fromUser = GlobalRegistry.getUser(fromUserId); + const toUser = GlobalRegistry.getUser(toUserId); + + if (!fromUser || !toUser) { + throw new Error("User not found"); + } + + const fromAccountIds = fromUser.getAccountIds(); + const toAccountIds = toUser.getAccountIds(); + + if (fromAccountIds.length === 0 || toAccountIds.length === 0) { + throw new Error("User has no accounts"); + } + + // Find accounts that belong to this bank + const fromAccountId = fromAccountIds.find((id) => this.hasAccount(id)); + const toAccountId = toAccountIds.find((id) => this.hasAccount(id)); + + if (!fromAccountId) { + throw new Error("Sender has no account in this bank"); + } + + const fromAccount = this.getAccount(fromAccountId); + + // Determine target bank and account + let toBank: Bank; + let toAccount: BankAccount; + + if (toBankId && toBankId !== this.id) { + toBank = GlobalRegistry.getBank(toBankId); + const targetAccountId = toAccountIds.find((id) => toBank.hasAccount(id)); + if (!targetAccountId) { + throw new Error("Recipient has no account in target bank"); + } + toAccount = toBank.getAccount(targetAccountId); + } else { + if (!toAccountId) { + throw new Error("Recipient has no account in this bank"); + } + toBank = this; + toAccount = this.getAccount(toAccountId); + } + + // Perform the transfer + fromAccount.withdraw(amount); + toAccount.deposit(amount); + } } -export default Bank; diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 0000000..4b1c76f --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,3 @@ +export { default as User } from "./user"; +export { default as Bank } from "./bank"; +export { default as BankAccount } from "./bank-account"; diff --git a/src/models/user.ts b/src/models/user.ts index 7f1fb9d..46394e1 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,11 +1,45 @@ -class User { - constructor(private name: string, private bankAccountIds: string[]) { +import { UserId, BankAccountId } from "@/types/Common"; +import GlobalRegistry from "@/services/GlobalRegistry"; + +export default class User { + private id: UserId; + private name: string; + private accountIds: BankAccountId[]; + + constructor(name: string, accountIds: BankAccountId[]) { + this.id = this.generateId(); this.name = name; - this.bankAccountIds = bankAccountIds; + this.accountIds = accountIds; + GlobalRegistry.registerUser(this); + } + + private generateId(): UserId { + return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + } + + static create(name: string, accountIds: BankAccountId[]): User { + return new User(name, accountIds); + } + + getId(): UserId { + return this.id; + } + + getName(): string { + return this.name; + } + + getAccountIds(): BankAccountId[] { + return [...this.accountIds]; + } + + addAccount(accountId: BankAccountId): void { + if (!this.accountIds.includes(accountId)) { + this.accountIds.push(accountId); + } } - private static create(): User { - return new User("", []); + removeAccount(accountId: BankAccountId): void { + this.accountIds = this.accountIds.filter((id) => id !== accountId); } } -export default User; diff --git a/src/services/GlobalRegistry.ts b/src/services/GlobalRegistry.ts new file mode 100644 index 0000000..ec4c6ec --- /dev/null +++ b/src/services/GlobalRegistry.ts @@ -0,0 +1,33 @@ +import { UserId } from "@/types/Common"; +import User from "@/models/user"; +import Bank from "@/models/bank"; + +export default class GlobalRegistry { + private static users: Map = new Map(); + private static banks: Map = new Map(); + + static registerUser(user: User): void { + this.users.set(user.getId(), user); + } + + static registerBank(bank: Bank): void { + this.banks.set(bank.getId(), bank); + } + + static getUser(userId: UserId): User | undefined { + return this.users.get(userId); + } + + static getBank(bankId: string): Bank { + const bank = this.banks.get(bankId); + if (!bank) { + throw new Error(`Bank ${bankId} not found`); + } + return bank; + } + + static clear(): void { + this.users.clear(); + this.banks.clear(); + } +} diff --git a/src/services/TransactionService.ts b/src/services/TransactionService.ts index 46a245e..a433f2e 100644 --- a/src/services/TransactionService.ts +++ b/src/services/TransactionService.ts @@ -1,2 +1,34 @@ -class TransactionService {} -export default TransactionService; +import { UserId } from "@/types/Common"; +import GlobalRegistry from "./GlobalRegistry"; +import Bank from "@/models/bank"; + +export default class TransactionService { + static transfer( + fromUserId: UserId, + toUserId: UserId, + amount: number, + bankId?: string + ): void { + const fromUser = GlobalRegistry.getUser(fromUserId); + const toUser = GlobalRegistry.getUser(toUserId); + + if (!fromUser || !toUser) { + throw new Error("User not found"); + } + + // For now, we'll use the first bank in the registry + // In a real implementation, you'd want to specify which bank to use + const bank = bankId ? GlobalRegistry.getBank(bankId) : null; + if (!bank) { + throw new Error("No banks available"); + } + + bank.send(fromUserId, toUserId, amount); + } + + static getTransactionHistory(userId: UserId): any[] { + // This would return transaction history for a user + // For now, return empty array + return []; + } +} diff --git a/src/types/common.ts b/src/types/common.ts new file mode 100644 index 0000000..bea7b44 --- /dev/null +++ b/src/types/common.ts @@ -0,0 +1,2 @@ +export type BankAccountId = string; +export type UserId = string;