diff --git a/lib/account.rb b/lib/account.rb index e69de29b..560d813b 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -0,0 +1,51 @@ +# Update the Account class to be able to handle all of these fields from the CSV file used as input. +# For example, manually choose the data from the first line of the CSV file and ensure you can create a new instance of your Account using that data + +require 'csv' + + +module Bank + class Account + attr_accessor :id, :balance, :date_created + def initialize(id, balance, date_created = nil) + raise ArgumentError.new("balance must be >= 0") if balance < 0 + @id = id + @balance = balance + @date_created = date_created + + end + + def self.all + @accounts = [] + CSV.open("./support/accounts.csv").each do |line| + @accounts << self.new(line[0].to_i, line[1].to_f, line[2].to_s) + end + return @accounts + end + + def self.find(id) + account_find = Bank::Account.all + account_find.each do |account| + if account.id == id + return account + end + end + raise ArgumentError.new "Account #{id} doesn't exist, sorry" + end + + def withdraw(amount) + raise ArgumentError.new("amount must be >= 0") if amount < 0 + if @balance - amount < 0 + puts "Balance cannot be negative" + return @balance + else + return @balance -= amount + end + end + + def deposit(amount) + raise ArgumentError.new("amount must be >= 0") if amount < 0 + return @balance += amount + end + end +end diff --git a/lib/checking_account.rb b/lib/checking_account.rb new file mode 100644 index 00000000..edef10de --- /dev/null +++ b/lib/checking_account.rb @@ -0,0 +1,42 @@ +require_relative 'account' + +module Bank + class CheckingAccount< Account + attr_accessor :id, :balance + + def initialize(id, balance) + super(id, balance) + @check_count = 0 + @fee = 2 + raise ArgumentError.new("balance must be >= -1-") if balance < 10 + end + + def withdraw(amount) + if amount < (@balance - 11) + puts "Outputs a warning if the balance would go below $10" + return @balance -= (amount + 1) + else + return @balance + end + end + + def withdraw_using_check(amount) + raise ArgumentError.new("Invalid amount, try again.") if amount < 0 + + if (@check_count < 3) && (@balance - amount) >= -10 + @check_count += 1 + @balance -= amount + elsif (@check_count >= 3) && (@balance - amount) >= -12 + @balance -= (amount + 2) + else + puts "Sorry, can't go below -$10" + end + return @balance + end + + def reset_checks + @check_count = 0 + end + end + +end diff --git a/lib/savings_account.rb b/lib/savings_account.rb new file mode 100644 index 00000000..d8549ec0 --- /dev/null +++ b/lib/savings_account.rb @@ -0,0 +1,32 @@ +require_relative 'account' + +module Bank + class SavingsAccount < Account + attr_accessor :id, :balance + + def initialize(id, balance) + super(id, balance) + + raise ArgumentError.new("balance must be >= 10") if balance < 10 + + end + + def withdraw(amount) + raise ArgumentError.new("amount must be >= 0") if amount < 0 + if amount < (@balance - 12) + puts "Outputs a warning if the balance would go below $10" + return @balance -= (amount + 2) + else + return @balance + end + end + + def add_interest(rate) + raise ArgumentError.new("interest rate must be >= 0") if rate < 0 + interest_calculation = (@balance * (rate/100)) + @balance += interest_calculation + return interest_calculation + end + + end +end diff --git a/specs/account_spec.rb b/specs/account_spec.rb index 6c399139..cd28af8f 100644 --- a/specs/account_spec.rb +++ b/specs/account_spec.rb @@ -5,9 +5,10 @@ describe "Wave 1" do describe "Account#initialize" do - it "Takes an ID and an initial balance" do + it "Takes an ID, an initial balance, and date created" do id = 1337 balance = 100.0 + account = Bank::Account.new(id, balance) account.must_respond_to :id @@ -15,20 +16,18 @@ account.must_respond_to :balance account.balance.must_equal balance + end it "Raises an ArgumentError when created with a negative balance" do - # Note: we haven't talked about procs yet. You can think - # of them like blocks that sit by themselves. - # This code checks that, when the proc is executed, it - # raises an ArgumentError. + proc { Bank::Account.new(1337, -100.0) }.must_raise ArgumentError end it "Can be created with a balance of 0" do - # If this raises, the test will fail. No 'must's needed! + Bank::Account.new(1337, 0) end end @@ -61,13 +60,10 @@ withdrawal_amount = 200.0 account = Bank::Account.new(1337, start_balance) - # Another proc! This test expects something to be printed - # to the terminal, using 'must_output'. /.+/ is a regular - # expression matching one or more characters - as long as - # anything at all is printed out the test will pass. + proc { account.withdraw(withdrawal_amount) - }.must_output /.+/ + }.must_output(/.+/) end it "Doesn't modify the balance if the account would go negative" do @@ -77,8 +73,6 @@ 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 end @@ -124,7 +118,7 @@ updated_balance.must_equal expected_balance end - it "Requires a positive deposit amount" do + it "Requires a positive deposit amount" do start_balance = 100.0 deposit_amount = -25.0 account = Bank::Account.new(1337, start_balance) @@ -136,36 +130,57 @@ end end -# TODO: change 'xdescribe' to 'describe' to run these tests -xdescribe "Wave 2" do +describe "Wave 2" do 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 - # - Everything in the array is an Account - # - The number of accounts is correct - # - 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 + before do + @spec_accounts = Bank::Account.all + end + it "Accounts.all returns an array" do + @spec_accounts.class.must_equal Array, "This is not an array" + end + + it "Everything in the array is an Account" do + @spec_accounts.each do |account| + account.must_be_instance_of Bank::Account + end + end + + it "The number of accounts is correct" do + @spec_accounts.length.must_equal 12 + end + + it "The ID and balance of the first and last accounts match" do + @spec_accounts[0].id.must_equal 1212, "Oops this should match the id, but it doesn't" + @spec_accounts[0].balance.must_equal 1235667, "Rurow, this should match the balance, but it doesn't" + + @spec_accounts[11].id.must_equal 15156, "Oops this should match the id, but it doesn't" + @spec_accounts[11].balance.must_equal 4356772, "Rurow, this should match the balance, but it doesn't" + end end end describe "Account.find" do it "Returns an account that exists" do - # TODO: Your test code here! + sample_account = Bank::Account.find(1212) + sample_account.must_be_instance_of Bank::Account + sample_account.id.must_equal 1212 end it "Can find the first account from the CSV" do - # TODO: Your test code here! + first_account = Bank::Account.find(1212) + first_account.must_be_instance_of Bank::Account + first_account.id.must_equal 1212 end it "Can find the last account from the CSV" do - # TODO: Your test code here! + last_account = Bank::Account.find(15156) + last_account.must_be_instance_of Bank::Account + last_account.id.must_equal 15156 end it "Raises an error for an account that doesn't exist" do - # TODO: Your test code here! + proc { + Bank::Account.find(12345678) + }.must_raise ArgumentError end end -end diff --git a/specs/checking_account_spec.rb b/specs/checking_account_spec.rb index 7f95339e..9224242f 100644 --- a/specs/checking_account_spec.rb +++ b/specs/checking_account_spec.rb @@ -3,7 +3,8 @@ 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.rb' + # Because a CheckingAccount is a kind # of Account, and we've already tested a bunch of functionality @@ -11,7 +12,7 @@ # 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 @@ -22,59 +23,149 @@ 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(1991, start_balance) + + account.withdraw(withdrawal_amount) + + expected_balance = start_balance - withdrawal_amount - 1 + account.balance.must_equal expected_balance 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 = 130.0 + account = Bank::SavingsAccount.new(1337, start_balance) + + account.withdraw(withdrawal_amount) + account.balance.must_equal start_balance end end describe "#withdraw_using_check" do it "Reduces the balance" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 30.0 + account = Bank::CheckingAccount.new(1337, start_balance) + + account.withdraw_using_check(withdrawal_amount) + account.balance.must_be :<, start_balance end it "Returns the modified balance" do - # TODO: Your test code here! + start_balance = 200.0 + withdrawal_amount = 40.0 + + account = Bank::CheckingAccount.new(1337, start_balance) + + updated_balance = account.withdraw_using_check(withdrawal_amount) + + expected_balance = start_balance - withdrawal_amount + updated_balance.must_equal expected_balance, "Balance was not modified" end it "Allows the balance to go down to -$10" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 109.0 + + account = Bank::CheckingAccount.new(1337, start_balance) + + updated_balance = account.withdraw_using_check(withdrawal_amount) + expected_balance = start_balance - withdrawal_amount + + updated_balance.must_equal expected_balance end it "Outputs a warning if the account would go below -$10" do - # TODO: Your test code here! + start_balance = 100.0 + account = Bank::CheckingAccount.new(1337, start_balance) + + account.balance.must_equal start_balance, "Outputs a warning if the balance would go below -$10" + end it "Doesn't modify the balance if the account would go below -$10" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 120.0 + account = Bank::SavingsAccount.new(1337, start_balance) + + account.withdraw(withdrawal_amount) + account.balance.must_equal start_balance end it "Requires a positive withdrawal amount" do - # TODO: Your test code here! + account = Bank::CheckingAccount.new(1337, 100.0) + + proc { + account.withdraw_using_check(-10) + }.must_raise ArgumentError end it "Allows 3 free uses" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 10.0 + account = Bank::CheckingAccount.new(1337, start_balance) + new_balance = start_balance - (withdrawal_amount * 3) + + 3.times do + account.withdraw_using_check(withdrawal_amount) + end + account.balance.must_equal new_balance end it "Applies a $2 fee after the third use" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 10.0 + account = Bank::CheckingAccount.new(1337, start_balance) + new_balance = start_balance - ((withdrawal_amount * 4) + 2) + + 4.times do + account.withdraw_using_check(withdrawal_amount) + end + account.balance.must_equal new_balance end - end +end describe "#reset_checks" do it "Can be called without error" do - # TODO: Your test code here! + start_balance = 100.0 + check_count = 0 + account = Bank::CheckingAccount.new(1337, start_balance) + account.reset_checks.must_equal check_count end it "Makes the next three checks free if less than 3 checks had been used" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 10.0 + account = Bank::CheckingAccount.new(1337, start_balance) + new_balance = start_balance - ((withdrawal_amount * 4)) + + 1.times do + account.withdraw_using_check(withdrawal_amount) + end + account.reset_checks + 3.times do + account.withdraw_using_check(withdrawal_amount) + end + account.balance.must_equal new_balance end it "Makes the next three checks free if more than 3 checks had been used" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 10.0 + account = Bank::CheckingAccount.new(1337, start_balance) + new_balance = start_balance - ((withdrawal_amount * 7) + 4) + + 5.times do + account.withdraw_using_check(withdrawal_amount) + end + account.reset_checks + + 2.times do + account.withdraw_using_check(withdrawal_amount) + end + account.balance.must_equal new_balance end end end diff --git a/specs/savings_account_spec.rb b/specs/savings_account_spec.rb index 3f4d1e4a..a6a49e3a 100644 --- a/specs/savings_account_spec.rb +++ b/specs/savings_account_spec.rb @@ -2,8 +2,7 @@ require 'minitest/reporters' 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.rb' # Because a SavingsAccount is a kind # of Account, and we've already tested a bunch of functionality @@ -11,48 +10,84 @@ # 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) account.must_be_kind_of Bank::Account end it "Requires an initial balance of at least $10" do - # TODO: Your test code here! + proc {Bank::SavingsAccount.new(1337, 9.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(1991, start_balance) + + account.withdraw(withdrawal_amount) + + expected_balance = start_balance - withdrawal_amount - 2 + account.balance.must_equal expected_balance end it "Outputs a warning if the balance would go below $10" do - # TODO: Your test code here! + start_balance = 100.0 + withdrawal_amount = 96.0 + account = Bank::SavingsAccount.new(1337, start_balance) + + account.withdraw(withdrawal_amount) + account.balance.must_equal start_balance, "Outputs a warning if the balance would go below $10" end it "Doesn't modify the balance if it would go below $10" do - # TODO: Your test code here! + start_balance = 200.0 + withdrawal_amount = 300.0 + account = Bank::SavingsAccount.new(1337, start_balance) + + account.withdraw(withdrawal_amount) + account.balance.must_equal start_balance 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 = 95.0 + account = Bank::SavingsAccount.new(1337, start_balance) + + account.withdraw(withdrawal_amount) + + account.balance.must_equal start_balance end end describe "#add_interest" do it "Returns the interest calculated" do - # TODO: Your test code here! + interest_rate = 0.25 + start_balance = 10000.0 + account = Bank::SavingsAccount.new(1337, start_balance) + interest_account = account.add_interest(interest_rate) + interest_account.must_equal start_balance * (interest_rate/100), "Calculated interest was not returned." + end it "Updates the balance with calculated interest" do - # TODO: Your test code here! + interest_rate = 0.25 + start_balance = 10000.0 + account = Bank::SavingsAccount.new(1337, start_balance) + account.add_interest(interest_rate) + account.balance.must_equal (start_balance + (start_balance * (interest_rate/100))) end it "Requires a positive rate" do - # TODO: Your test code here! + account = Bank::SavingsAccount.new(1337, 100.0) + + proc { + account.add_interest(-0.25) + }.must_raise ArgumentError end end end