diff --git a/lib/account.rb b/lib/account.rb index e69de29b..4c6f3334 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -0,0 +1,80 @@ +require 'csv' + +module Bank + class Account + attr_reader :id, :balance, :owner + + def initialize(id, balance = 0) + raise ArgumentError.new("balance must be >= 0") if balance < 0 + + @id = id + @balance = balance + end + + def self.all + accounts = [] + CSV.read("support/accounts.csv").each do |line| + accounts << Account.new(line[0], line[1].to_f/100) + end + return accounts + end + + def self.find(id) + CSV.read("support/accounts.csv").each do |line| + if line[0] == id + return Account.new(line[0], line[1].to_f/100) + end + end + raise ArgumentError.new("account id doesn't exist") + end + + + def withdraw(amount) + raise ArgumentError.new("amount must be >= 0") if amount < 0 + if amount > @balance + puts "Warning: insufficient fund." + else + @balance = @balance - amount + end + return @balance + end + def deposit(amount) + raise ArgumentError.new("amount must be >= 0") if amount < 0 + @balance = @balance + amount + return @balance + # @balance += amount + end + def add_owner (owner) + @owner = owner + end + end + + class Owner + attr_reader :id, :last_name, :first_name, :street_address, :city, :state + def initialize(id, last_name, first_name, street_address, city, state) + @id = id + @last_name = last_name + @first_name = first_name + @street_address = street_address + @city = city + @state = state + end + + def self.all + owners = [] + CSV.read("support/owners.csv").each do |line| + owners << Owner.new(line[0], line[1], line[2], line[3], line[4], line[5]) + end + return owners + end + + def self.find(id) + CSV.read("support/owners.csv").each do |line| + if line[0] == id + return Owner.new(line[0], line[1], line[2], line[3], line[4], line[5]) + end + end + raise ArgumentError.new("owner id doesn't exist") + end + end +end diff --git a/lib/checking_account.rb b/lib/checking_account.rb new file mode 100644 index 00000000..d12fdc0a --- /dev/null +++ b/lib/checking_account.rb @@ -0,0 +1,43 @@ +require_relative 'account' +module Bank + class CheckingAccount < Bank::Account + attr_reader :id, :balance + + def initialize(id, balance) + @id = id + @balance = balance + @checks = 0 + end + + def withdraw(amount) + + new_balance = @balance - (amount+1) + if new_balance < 0 + puts "Warning: insufficient fund." + return @balance + end + @balance = new_balance + end + + def withdraw_using_check(amount) + raise ArgumentError.new "Please enter positive amount" if amount < 0 + @checks += 1 + if @checks > 3 + fee = 2 + else + fee = 0 + end + new_balance = @balance - (amount+fee) + + + if new_balance < -10 + puts "Warning: insufficient fund." + return @balance + end + @balance = new_balance + end + def reset_checks + @checks = 0 + end + end +end diff --git a/lib/savings_account.rb b/lib/savings_account.rb new file mode 100644 index 00000000..c6accbfe --- /dev/null +++ b/lib/savings_account.rb @@ -0,0 +1,29 @@ +require_relative 'account' +module Bank + class SavingsAccount < Bank::Account + attr_reader :id, :balance + + def initialize(id, balance = 10) + raise ArgumentError.new("balance must be >= 10") if balance < 10 + + @id = id + @balance = balance + end + + def withdraw(amount) + new_balance = @balance - (amount+2) + if new_balance < 10 + puts "Warning: insufficient fund." + return @balance + end + @balance = new_balance + end + + def add_interest(rate) + raise ArgumentError.new("rate must be >= 0") if rate < 0 + interest = @balance * rate/100 + @balance = @balance + interest + return interest + end + end +end diff --git a/specs/account_spec.rb b/specs/account_spec.rb index 6c399139..b507c675 100644 --- a/specs/account_spec.rb +++ b/specs/account_spec.rb @@ -32,7 +32,6 @@ Bank::Account.new(1337, 0) end end - describe "Account#withdraw" do it "Reduces the balance" do start_balance = 100.0 @@ -45,6 +44,7 @@ account.balance.must_equal expected_balance end + it "Returns the modified balance" do start_balance = 100.0 withdrawal_amount = 25.0 @@ -69,7 +69,6 @@ account.withdraw(withdrawal_amount) }.must_output /.+/ end - it "Doesn't modify the balance if the account would go negative" do start_balance = 100.0 withdrawal_amount = 200.0 @@ -112,60 +111,130 @@ expected_balance = start_balance + deposit_amount account.balance.must_equal expected_balance end + end - it "Returns the modified balance" do - start_balance = 100.0 - deposit_amount = 25.0 - account = Bank::Account.new(1337, start_balance) + it "Returns the modified balance" do + start_balance = 100.0 + deposit_amount = 25.0 + account = Bank::Account.new(1337, start_balance) - updated_balance = account.deposit(deposit_amount) + updated_balance = account.deposit(deposit_amount) - expected_balance = start_balance + deposit_amount - updated_balance.must_equal expected_balance - end + expected_balance = start_balance + deposit_amount + updated_balance.must_equal expected_balance + end - it "Requires a positive deposit amount" do - start_balance = 100.0 - deposit_amount = -25.0 - account = Bank::Account.new(1337, start_balance) - proc { - account.deposit(deposit_amount) - }.must_raise ArgumentError - end + it "Requires a positive deposit amount" do + start_balance = 100.0 + deposit_amount = -25.0 + account = Bank::Account.new(1337, start_balance) + + proc { + account.deposit(deposit_amount) + }.must_raise ArgumentError + end +end +describe "Account#add_owner" do + it "takes an owner and updates it" do + account = Bank::Account.new(1337, 100) + owner = Bank::Owner.new("26", "Berrie", "Kevin", "8113 Sutherland Center", "Seattle", "WA") + account.add_owner(owner) + account.owner.must_equal owner + end +end +describe "Owner#initialize" do + it "Takes a name and an address" do + first_name = "Kevin" + street_address = "8113 Sutherland Center" + owner = Bank::Owner.new("26", "Berrie", "Kevin", "8113 Sutherland Center", "Seattle", "WA") + owner.must_respond_to :first_name + owner.first_name.must_equal first_name + + owner.must_respond_to :street_address + owner.street_address.must_equal street_address end end # TODO: change 'xdescribe' to 'describe' to run these tests -xdescribe "Wave 2" do - describe "Account.all" do +describe "Wave 2" do +end + describe "Account.all" do it "Returns an array of all accounts" do - # TODO: Your test code here! # Useful checks might include: # - Account.all returns an array + Bank::Account.all.must_be_instance_of Array # - Everything in the array is an Account + Bank::Account.all.each do |x| + x.must_be_instance_of Bank::Account + end # - The number of accounts is correct + Bank::Account.all.length.must_equal 12 # - The ID and balance of the first and last # accounts match what's in the CSV file - # Feel free to split this into multiple tests if needed + Bank::Account.all[0].id.must_equal "1212" + Bank::Account.all[0].balance.must_equal 12356.67 + Bank::Account.all[11].id.must_equal "15156" + Bank::Account.all[11].balance.must_equal 43567.72 + end - end + end describe "Account.find" do it "Returns an account that exists" do - # TODO: Your test code here! + Bank::Account.find("15151").must_be_instance_of Bank::Account end it "Can find the first account from the CSV" do - # TODO: Your test code here! + Bank::Account.find("1212").must_be_instance_of Bank::Account + end it "Can find the last account from the CSV" do - # TODO: Your test code here! + Bank::Account.find("15156").must_be_instance_of Bank::Account + end it "Raises an error for an account that doesn't exist" do - # TODO: Your test code here! + proc { + Bank::Account.find("abcd").must_raise ArgumentError + } + end + end + + describe "Owner.all" do + it "Returns an array of all accounts" do + Bank::Owner.all.must_be_instance_of Array + Bank::Owner.all.each do |x| + x.must_be_instance_of Bank::Owner + end + Bank::Owner.all.length.must_equal 12 + Bank::Owner.all[0].id.must_equal "14" + Bank::Owner.all[0].last_name.must_equal "Morales" + Bank::Owner.all[11].id.must_equal "25" + Bank::Owner.all[11].last_name.must_equal "Clark" + + end + end + + describe "Owner.find" do + it "Returns an owners that exists" do + Bank::Owner.find("20").must_be_instance_of Bank::Owner + end + + it "Can find the first owner from the CSV" do + Bank::Owner.find("14").must_be_instance_of Bank::Owner + + end + + it "Can find the last owner from the CSV" do + Bank::Owner.find("25").must_be_instance_of Bank::Owner + + end + + it "Raises an error for an owner that doesn't exist" do + proc { + Bank::Owner.find("ab").must_raise ArgumentError + } end end -end diff --git a/specs/checking_account_spec.rb b/specs/checking_account_spec.rb index 7f95339e..8ba3c919 100644 --- a/specs/checking_account_spec.rb +++ b/specs/checking_account_spec.rb @@ -3,7 +3,7 @@ require 'minitest/skip_dsl' # TODO: uncomment the next line once you start wave 3 and add lib/checking_account.rb -# require_relative '../lib/checking_account' +require_relative '../lib/checking_account' # Because a CheckingAccount is a kind # of Account, and we've already tested a bunch of functionality @@ -11,70 +11,143 @@ # Here we'll only test things that are different. # TODO: change 'xdescribe' to 'describe' to run these tests -xdescribe "CheckingAccount" do +describe "CheckingAccount" do describe "#initialize" do # Check that a CheckingAccount is in fact a kind of account it "Is a kind of Account" do - account = Bank::CheckingAccount.new(12345, 100.0) + id = 12345 + balance = 100.0 + account = Bank::CheckingAccount.new(id, balance) account.must_be_kind_of Bank::Account + + account.must_respond_to :id + account.id.must_equal id + + account.must_respond_to :balance + account.balance.must_equal balance end end +end - describe "#withdraw" do - it "Applies a $1 fee each time" do - # TODO: Your test code here! - end +describe "#withdraw" do + it "Applies a $1 fee each time" do + # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 25.0 + account = Bank::CheckingAccount.new(12345, start_balance) - it "Doesn't modify the balance if the fee would put it negative" do - # TODO: Your test code here! - end + account.withdraw(withdrawal_amount) + + expected_balance = start_balance - (withdrawal_amount+1) + account.balance.must_equal expected_balance end - describe "#withdraw_using_check" do - it "Reduces the balance" do - # TODO: Your test code here! - end + it "Doesn't modify the balance if the fee would put it negative" do + # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 100.0 + account = Bank::CheckingAccount.new(12345, start_balance) - it "Returns the modified balance" do - # TODO: Your test code here! - end + updated_balance = account.withdraw(withdrawal_amount) - it "Allows the balance to go down to -$10" do + # Both the value returned and the balance in the account + # must be un-modified. + updated_balance.must_equal start_balance + account.balance.must_equal start_balance + end +end + +describe "#withdraw_using_check" do + describe "when withdrawing less than the balance" do + it "Reduces the balance" do # TODO: Your test code here! + start_balance = 100.0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + end_balance = checking_account.withdraw_using_check(75) + end_balance.must_equal 25 end - it "Outputs a warning if the account would go below -$10" do + it "Doesn't print anything" do # TODO: Your test code here! + start_balance = 100.0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + proc{checking_account.withdraw_using_check(75)}.must_be_silent end - - it "Doesn't modify the balance if the account would go below -$10" do + end + describe "when withdrawing goes into overdraft" do + it "Reduces the balance" do # TODO: Your test code here! + start_balance = 100.0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + end_balance = checking_account.withdraw_using_check(105) + end_balance.must_equal -5 end - - it "Requires a positive withdrawal amount" do + it "Doesn't print anything" do # TODO: Your test code here! + start_balance = 100.0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + proc{checking_account.withdraw_using_check(105)}.must_be_silent end - - it "Allows 3 free uses" do + end + describe "when withdrawing goes beyond overdraft limit" do + it "Doesn't reduce the balance" do # TODO: Your test code here! + start_balance = 100.0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + end_balance = checking_account.withdraw_using_check(111) + end_balance.must_equal 100 end - - it "Applies a $2 fee after the third use" do + it "Prints a warning" do # TODO: Your test code here! + start_balance = 100.0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + proc{checking_account.withdraw_using_check(111)}.must_output "Warning: insufficient fund.\n" end end - - describe "#reset_checks" do - it "Can be called without error" do - # TODO: Your test code here! + it "Requires a positive withdrawal amount" do + start_balance = 0 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + proc{checking_account.withdraw_using_check(-10)}.must_raise ArgumentError + end + + it "Allows 3 free uses" do + start_balance = 100 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + 3.times {checking_account.withdraw_using_check(10)} + checking_account.balance.must_equal start_balance - 30 + end + + it "Applies a $2 fee after the third use" do + start_balance = 100 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + 4.times {checking_account.withdraw_using_check(10)} + checking_account.balance.must_equal start_balance - 42 + end end - it "Makes the next three checks free if less than 3 checks had been used" do - # TODO: Your test code here! - end + describe "#reset_checks" do + it "Can be called without error" do + start_balance = 100 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + proc{checking_account.reset_checks}.must_be_silent + end + + + it "Makes the next three checks free if less than 3 checks had been used" do + start_balance = 100 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + 2.times {checking_account.withdraw_using_check(10)} + checking_account.reset_checks + 3.times {checking_account.withdraw_using_check(10)} + checking_account.balance.must_equal start_balance - 50 +end - it "Makes the next three checks free if more than 3 checks had been used" do - # TODO: Your test code here! - end - end + it "Makes the next three checks free if more than 3 checks had been used" do + start_balance = 100 + checking_account = Bank::CheckingAccount.new(12345, start_balance) + 4.times {checking_account.withdraw_using_check(10)} + checking_account.reset_checks + 3.times {checking_account.withdraw_using_check(10)} + checking_account.balance.must_equal start_balance - 72 end + end diff --git a/specs/savings_account_spec.rb b/specs/savings_account_spec.rb index 3f4d1e4a..92fb2655 100644 --- a/specs/savings_account_spec.rb +++ b/specs/savings_account_spec.rb @@ -3,7 +3,7 @@ require 'minitest/skip_dsl' # TODO: uncomment the next line once you start wave 3 and add lib/savings_account.rb -# require_relative '../lib/savings_account' +require_relative '../lib/savings_account' # Because a SavingsAccount is a kind # of Account, and we've already tested a bunch of functionality @@ -11,48 +11,103 @@ # Here we'll only test things that are different. # TODO: change 'xdescribe' to 'describe' to run these tests -xdescribe "SavingsAccount" do +describe "SavingsAccount" do describe "#initialize" do it "Is a kind of Account" do # Check that a SavingsAccount is in fact a kind of account - account = Bank::SavingsAccount.new(12345, 100.0) + id = 12345 + balance = 100.0 + account = Bank::SavingsAccount.new(id, balance) account.must_be_kind_of Bank::Account - end - it "Requires an initial balance of at least $10" do - # TODO: Your test code here! + account.must_respond_to :id + account.id.must_equal id + + account.must_respond_to :balance + account.balance.must_equal balance end end + # it "Requires an initial balance of at least $10" do + # # TODO: Your test code here! + it "Raises an ArgumentError when initial balance is less than $10" do + proc { + Bank::SavingsAccount.new(13371, 5.0) + }.must_raise ArgumentError + end +end +describe "#withdraw" do + it "Applies a $2 fee each time" do + # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 25.0 + account = Bank::SavingsAccount.new(13371, start_balance) - describe "#withdraw" do - it "Applies a $2 fee each time" do - # TODO: Your test code here! - end + account.withdraw(withdrawal_amount) - it "Outputs a warning if the balance would go below $10" do - # TODO: Your test code here! - end + expected_balance = start_balance - (withdrawal_amount+2) + account.balance.must_equal expected_balance + end - it "Doesn't modify the balance if it would go below $10" do - # TODO: Your test code here! - end + it "Outputs a warning if the balance would go below $10" do + # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 91.0 + account = Bank::SavingsAccount.new(13371, start_balance) + proc { + account.withdraw(withdrawal_amount) + }.must_output /.+/ + end + + it "Doesn't modify the balance if it would go below $10" do + # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 91.0 + account = Bank::SavingsAccount.new(13371, start_balance) + + updated_balance = account.withdraw(withdrawal_amount) + + # Both the value returned and the balance in the account + # must be un-modified. + updated_balance.must_equal start_balance + account.balance.must_equal start_balance - it "Doesn't modify the balance if the fee would put it below $10" do - # TODO: Your test code here! - end end - describe "#add_interest" do - it "Returns the interest calculated" do - # TODO: Your test code here! - end + it "Doesn't modify the balance if the fee would put it below $10" do + # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 89.0 + account = Bank::SavingsAccount.new(13371, start_balance) - it "Updates the balance with calculated interest" do - # TODO: Your test code here! - end + updated_balance = account.withdraw(withdrawal_amount) - it "Requires a positive rate" do - # TODO: Your test code here! - end + # Both the value returned and the balance in the account + # must be un-modified. + updated_balance.must_equal start_balance + account.balance.must_equal start_balance end end + + +describe "#add_interest" do + it "Returns the interest calculated" do + # TODO: Your test code here! + account = Bank::SavingsAccount.new(13371, 10000) + account.add_interest(0.25).must_equal 25 + end + + it "Updates the balance with calculated interest" do + # TODO: Your test code here! + account = Bank::SavingsAccount.new(13371, 10000) + account.add_interest(0.25) + account.balance.must_equal 10025 + end + + it "Requires a positive rate" do + # TODO: Your test code here! + account = Bank::SavingsAccount.new(13371, 10000) + proc { + account.add_interest(-0.25) + }.must_raise ArgumentError + end + end