diff --git a/Rakefile b/Rakefile index deb52f2c..f4f704fc 100644 --- a/Rakefile +++ b/Rakefile @@ -2,7 +2,7 @@ require 'rake/testtask' Rake::TestTask.new do |t| t.libs = ["lib"] - t.warning = true + t.warning = false t.test_files = FileList['specs/*_spec.rb'] end diff --git a/lib/account.rb b/lib/account.rb index e69de29b..eea19d78 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -0,0 +1,152 @@ +require 'csv' + +#Adding this in after the reading this weekend. +#b/c both savings and money market have interest +#but are sisters not mother/daughter classes! +module Interest + + def add_interest(rate) + argument("Interest rate >=0") if rate < 0 + + interest = @balance * (rate/100) + @balance += interest + + return interest + end + +end + +module Bank + + class Account + attr_accessor :balance + attr_reader :id, :owner + + def initialize(id, start_balance, opendate = nil) + + @id = id + @opendate = opendate + @balance = set_balance(start_balance) + end + + def set_balance(start_balance) + if start_balance < 0 + argument("You cannot initialize a new account with a negative balance.") + else + start_balance + end + end + + def self.all + accounts = [] + + CSV.open("support/accounts.csv").each do |account| + accounts << Bank::Account.new(account[0].to_i, account[1].to_i, account[2]) + end + return accounts + end + + def self.find(id) + @account = nil + + CSV.open("support/accounts.csv").each do |line| + if line[0].to_i == id + @account = Bank::Account.new(line[0].to_i, line[1].to_i, line[2]) + end + end + + if @account == nil + raise ArgumentError.new "This account does not exist!" + else + return @account + end + end + + def add_owner(owner) + if owner.class == Owner + @owner = owner + else + argument("You must add a class type of Owner.") + end + end + + def withdraw(withdrawal_amount) + withdraw_positive(withdrawal_amount) + + if balance - withdrawal_amount < 0 + puts "You are going negative." + return balance + else + @balance -= withdrawal_amount + end + end + + #Should this be private?? + #b/c only used in check_withdrawals? + def withdraw_positive(withdrawal_amount) + argument("Withdrawal must be >= 0") if withdrawal_amount < 0 + end + + def deposit(deposit_amount) + if deposit_amount > 0 + @balance += deposit_amount + else + argument("Your deposit must be greater than zero.") + end + end + + def argument(output) + raise ArgumentError.new "#{ output }" + end + + end + + class Owner + attr_reader :last_name, :first_name, :street_address, :city, :state + + def initialize( + id = nil, last_name = nil, + first_name = nil, street_address = nil, + city = nil, state = nil) + @id = id + @last_name = last_name + @first_name = first_name + @street_address = street_address + @city = city + @state = state + end + + def self.all + owners = [] + CSV.open("support/owners.csv").each do |owner| + owners << Bank::Owner.new( + owner[0].to_i, owner[1], + owner[2], owner[3], + owner[4], owner[5]) + end + + return owners + end + + def self.find(id) + @owner = nil + CSV.open("support/owners.csv").each do |owner| + if owner[0].to_i == id + @owner = Bank::Owner.new( + owner[0].to_i, owner[1], + owner[2], owner[3], + owner[4], owner[5]) + end + end + + if @owner == nil + raise ArgumentError.new "This owner does not exist!" + else + return @owner + end + + end + + end + +end diff --git a/lib/checking_account.rb b/lib/checking_account.rb new file mode 100644 index 00000000..6bbd248b --- /dev/null +++ b/lib/checking_account.rb @@ -0,0 +1,57 @@ +module Bank + + class CheckingAccount < Account + + def initialize(id, balance, opendate = nil) + super + @check_withdrawals = 0 + end + + def withdraw(withdrawal_amount) + original_balance = balance + + super + + if balance == original_balance + balance + elsif balance - 1 < 0 + puts "This withdrawal and fee will take your balance below 0." + @balance = original_balance + else + withdrawal_fee + end + end + + def withdrawal_fee + @balance -= 1 + end + + def withdraw_using_check(withdrawal_amount) + withdraw_positive(withdrawal_amount) + + if @balance - withdrawal_amount < -10 + puts "You can only go negative up to -$10" + return balance + end + + @check_withdrawals += 1 + @balance -= withdrawal_amount + + check_fee + end + + def check_fee + if @check_withdrawals > 3 + @balance -= 2 + else + @balance + end + end + + def reset_checks + @check_withdrawals = 0 + end + + end + +end diff --git a/lib/money_market_account.rb b/lib/money_market_account.rb new file mode 100644 index 00000000..4ce7bf07 --- /dev/null +++ b/lib/money_market_account.rb @@ -0,0 +1,60 @@ +require_relative 'account' + +module Bank + + class MoneyMarketAccount < Bank::Account + + include Interest + + attr_reader :total_transactions + + def initialize(id, balance, opendate = nil) + super + @total_transactions = 0 + end + + def set_balance(start_balance) + if start_balance < 10000 + argument("You cannot initialize a new Money Market account with less than 10k.") + else + start_balance + end + end + + def withdraw(withdrawal_amount) + @total_transactions += 1 + + if @total_transactions > 6 + argument("You cannot make more than six transactions per month.") + end + + if @balance < 10000 + argument("You cannot make another withdrawal until you make a deposit") + end + + super + + if @balance < 10000 + @balance -= 100 + end + end + + def deposit(deposit_amount) + if @balance > 10000 + @total_transactions += 1 + end + + if @total_transactions > 6 + argument("You cannot make more than six transactions per month.") + end + + super + end + + def reset_transactions + @total_transactions = 0 + end + + end + +end diff --git a/lib/savings_account.rb b/lib/savings_account.rb new file mode 100644 index 00000000..35867e69 --- /dev/null +++ b/lib/savings_account.rb @@ -0,0 +1,35 @@ +module Bank + + class SavingsAccount < Account + + include Interest + + def initialize(id, balance, opendate = nil) + super + + if @balance < 10 + argument("You must initially deposit at least $10.00") + end + end + + def withdraw(withdrawal_amount) + original_balance = balance + super + + if balance == original_balance + balance + elsif balance - 2 <= 10 + puts "This withdrawal and fee will take your balance below $10." + @balance = original_balance + else + withdrawal_fee + end + end + + def withdrawal_fee + @balance -= 2 + end + + end + +end diff --git a/specs/account_spec.rb b/specs/account_spec.rb index 6c399139..fdfce70c 100644 --- a/specs/account_spec.rb +++ b/specs/account_spec.rb @@ -4,6 +4,19 @@ require_relative '../lib/account' describe "Wave 1" do + #writing my own test + describe "Owner#initialize" do + it "Takes Name and Phone # to initialize Owner" do + last_name = "Trickey" + first_name = "lynn" + + new_owner = Bank::Owner.new(last_name, first_name) + + new_owner.must_respond_to :last_name + new_owner.must_respond_to :first_name + end + end + describe "Account#initialize" do it "Takes an ID and an initial balance" do id = 1337 @@ -29,7 +42,23 @@ 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) + Bank::Account.new(1337, 0, "opendate") + end + end + + describe "Account#add_owner" do + before do + @account = Bank::Account.new(1337, 10) + @lynn = Bank::Owner.new("Lynn Trickey", "555-555-5555") + end + it "Adds an Owner instance and saves it as an instance variable" do + @account.add_owner(@lynn) + @account.owner.must_equal @lynn + end + + it "Raises an Argument when add_owner is called and the owner argument is not class Owner" do + #Should raise error + proc { @account.add_owner("not class Owner") }.must_raise ArgumentError end end @@ -37,7 +66,7 @@ it "Reduces the balance" do start_balance = 100.0 withdrawal_amount = 25.0 - account = Bank::Account.new(1337, start_balance) + account = Bank::Account.new(1337, start_balance,) account.withdraw(withdrawal_amount) @@ -67,7 +96,7 @@ # 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 @@ -136,36 +165,135 @@ end end -# TODO: change 'xdescribe' to 'describe' to run these tests -xdescribe "Wave 2" do +#Wave 2 +describe "Wave 2" do + + describe "Testing Owner class methods" do + before do + @all_owners = Bank::Owner.all + end + + it "Returns an array of Owner instances" do + @all_owners.must_be_instance_of(Array) + end + + it "Verifies every item in array is an Owner" do + @all_owners.each do |item| + item.class.must_equal(Bank::Owner) + end + end + + it "Number of Owners match lines in CSV file" do + csv_lines = CSV.read("support/owners.csv").length + + @all_owners.length.must_equal(csv_lines) + end + + it "Name and address of first and last match CSV file" do + first_names = [] + street_addresses = [] + CSV.open("support/owners.csv").each do |line| + first_names << line[2] + street_addresses << line[3] + end + + #checks first and last first_names + @all_owners[0].first_name.must_equal(first_names[0]) + @all_owners[-1].first_name.must_equal(first_names[-1]) + + #checks first and last street_addresses + @all_owners[0].first_name.must_equal(first_names[0]) + @all_owners[-1].first_name.must_equal(first_names[-1]) + + end + + end + + describe "Testing Owner.find" do + + it "Returns an account that exists" do + owner = Bank::Owner.find(14) + owner.must_be_instance_of(Bank::Owner) + end + + it "Can find the first account from the CSV" do + Bank::Owner.find(14) + end + + it "Can find the last account from the CSV" do + Bank::Owner.find(25) + end + + it "Raises an error for an Owner that doesn't exist" do + #should raise an error when I try to find this + proc { Bank::Owner.find(0000000) }.must_raise ArgumentError + end + + end + describe "Account.all" do + + before do + @new_bank = Bank::Account.all + end + 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 + @new_bank.class.must_equal(Array) + end + + it "Verifies every item in array is an Account" do + @new_bank.each do |item| + item.class.must_equal(Bank::Account) + end + end + + it "The number of accounts matches lines in CSV file, so number of accounts is correct" do + + csv_lines = CSV.read("support/accounts.csv").length + + @new_bank.length.must_equal(csv_lines) + end + + it "ID and balance of first & last account matches ID and balance in CSV" do + ids = [] + balances = [] + CSV.open("support/accounts.csv").each do |line| + ids << line[0].to_i + balances << line[1].to_i + end + + #checks first and last ids + @new_bank[0].id.must_equal(ids[0]) + @new_bank[-1].id.must_equal(ids[-1]) + + #checks first and last balances + @new_bank[0].balance.must_equal(balances[0]) + @new_bank[-1].balance.must_equal(balances[-1]) end + end describe "Account.find" do it "Returns an account that exists" do - # TODO: Your test code here! + account = Bank::Account.find(15151) + account.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) end it "Can find the last account from the CSV" do - # TODO: Your test code here! + Bank::Account.find(15156) end it "Raises an error for an account that doesn't exist" do - # TODO: Your test code here! + #should raise an error when I try to find this + proc { + Bank::Account.find(0000000) + }.must_raise ArgumentError end + end + end diff --git a/specs/checking_account_spec.rb b/specs/checking_account_spec.rb index 7f95339e..cd99929c 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,7 +11,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 @@ -21,60 +21,112 @@ end describe "#withdraw" do + before do + @my_checking = Bank::CheckingAccount.new(1234, 500.00) + end + it "Applies a $1 fee each time" do - # TODO: Your test code here! + @my_checking.withdraw(10) + @my_checking.balance.must_equal(489) end it "Doesn't modify the balance if the fee would put it negative" do - # TODO: Your test code here! + updated_balance = @my_checking.withdraw(500.00) + + #Both the value returned and balance in account + #must be un-modified + updated_balance.must_equal 500 + @my_checking.balance.must_equal 500 end + end describe "#withdraw_using_check" do + before do + @my_checking = Bank::CheckingAccount.new(1234, 500.00) + end + it "Reduces the balance" do - # TODO: Your test code here! + @my_checking.withdraw_using_check(20) + @my_checking.balance.must_equal(480) end it "Returns the modified balance" do - # TODO: Your test code here! + modified_balance = @my_checking.withdraw_using_check(20) + + modified_balance.must_equal(480) end it "Allows the balance to go down to -$10" do - # TODO: Your test code here! + @my_checking.withdraw_using_check(510) end it "Outputs a warning if the account would go below -$10" do - # TODO: Your test code here! + proc { @my_checking.withdraw_using_check(520) }.must_output(/.+/) end it "Doesn't modify the balance if the account would go below -$10" do - # TODO: Your test code here! + @my_checking.withdraw_using_check(520) + @my_checking.balance.must_equal(500) end it "Requires a positive withdrawal amount" do - # TODO: Your test code here! + proc { @my_checking.withdraw_using_check(-25) }.must_raise ArgumentError end it "Allows 3 free uses" do - # TODO: Your test code here! + 3.times do + @my_checking.withdraw_using_check(10) + end + + @my_checking.balance.must_equal(470) end it "Applies a $2 fee after the third use" do - # TODO: Your test code here! + 4.times do + @my_checking.withdraw_using_check(10) + end + + @my_checking.balance.must_equal(458) end end describe "#reset_checks" do + before do + @my_checking = Bank::CheckingAccount.new(1234, 500.00) + end + it "Can be called without error" do - # TODO: Your test code here! + @my_checking.reset_checks end it "Makes the next three checks free if less than 3 checks had been used" do - # TODO: Your test code here! + 2.times do + @my_checking.withdraw_using_check(10) + end + + @my_checking.reset_checks + + 3.times do + @my_checking.withdraw_using_check(10) + end + + @my_checking.balance.must_equal(450) end it "Makes the next three checks free if more than 3 checks had been used" do - # TODO: Your test code here! + 4.times do + @my_checking.withdraw_using_check(10) + end + + @my_checking.reset_checks + + 3.times do + @my_checking.withdraw_using_check(10) + end + + @my_checking.balance.must_equal(428) + end end end diff --git a/specs/money_market_account_spec.rb b/specs/money_market_account_spec.rb new file mode 100644 index 00000000..72d9a73b --- /dev/null +++ b/specs/money_market_account_spec.rb @@ -0,0 +1,136 @@ +require 'minitest/autorun' +require 'minitest/reporters' +require 'minitest/skip_dsl' + +require_relative '../lib/money_market_account' + +describe "Bank::MoneyMarketAccount" do + + describe "#initialize" do + #Check that MoneyMarketAccount is a kind of account + it "Check#Initialize" do + account = Bank::MoneyMarketAccount.new(123, 10000.00, "5/5/5") + account.must_be_kind_of Bank::Account + end + + it "Initial balance must be > 10k" do + #initial balance cannot be less than 10,000. This will raise an ArgumentError + proc { Bank::MoneyMarketAccount.new(1337, 9999) }.must_raise ArgumentError + end + + end + + describe "transactions" do + before do + @my_money_market = Bank::MoneyMarketAccount.new(1234, 1000000.00) + end + + it "Does not allow more than six withdrawals" do + #Maximum of 6 transactions allowed per month + 6.times do + @my_money_market.withdraw(10) + end + + #7th transaction should raise error + proc { @my_money_market.withdraw(10) }.must_raise ArgumentError + end + + it "Does not allow more than six deposits" do + #Maximum of 6 transactions allowed per month + 6.times do + @my_money_market.deposit(10) + end + + #7th transaction should raise error + proc { @my_money_market.deposit(10) }.must_raise ArgumentError + end + + it "Does not allow more than six mixed deposits and withdrawals together" do + #Maximum of 6 transactions allowed per month + 3.times do + @my_money_market.deposit(10) + end + + 3.times do + @my_money_market.withdraw(10) + end + + #7th transaction should raise error + proc { @my_money_market.deposit(10) }.must_raise ArgumentError + end + + end + + describe "transactions" do + before do + @my_money_market = Bank::MoneyMarketAccount.new(1234, 10000.00) + end + + it "if withdrawal takes balance below 10k, charges a fee of $100" do + @my_money_market.withdraw(500) + + @my_money_market.balance.must_equal(9400) + end + + it "if withdrawal goes below 10k, no more transactions are allowed" do + @my_money_market.withdraw(500) + + proc { @my_money_market.withdraw(500) }.must_raise ArgumentError + end + + it "if withdrawal goes below 10k, then deposit goes above 10k, should be allow to withdraw again" do + @my_money_market.withdraw(500) + @my_money_market.deposit(1000) + #should not raise an issue. + @my_money_market.withdraw(10) + end + + it "all transactions count towards 6 transactions, except for deposit to bring balance back up to 10k" do + @my_money_market.withdraw(500) + @my_money_market.deposit(1000) + #should not raise an issue. + @my_money_market.withdraw(10) + @my_money_market.total_transactions.must_equal(2) + end + + end + + describe "#reset_transactions" do + before do + @my_money_market = Bank::MoneyMarketAccount.new(1234, 100000.00) + end + + it "resets total_transactions to 0" do + + 3.times do + @my_money_market.withdraw(10) + end + + @my_money_market.reset_transactions + + @my_money_market.total_transactions.must_equal(0) + + end + + describe "#add_interest" do + before do + @my_money_market = Bank::MoneyMarketAccount.new(1234, 10000.00) + @my_interest = @my_money_market.add_interest(0.25) + end + + it "Returns the interest calculated" do + @my_interest.must_equal(25) + end + + it "Updates the balance with calculated interest" do + @my_money_market.balance.must_equal(10025.00) + end + + it "Requires a positive rate" do + proc { @my_money_market.add_interest(-0.25) }.must_raise ArgumentError + end + + end + + end +end diff --git a/specs/savings_account_spec.rb b/specs/savings_account_spec.rb index 3f4d1e4a..342dfb08 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,7 +11,8 @@ # 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 @@ -20,39 +21,67 @@ end it "Requires an initial balance of at least $10" do - # TODO: Your test code here! + proc { + Bank::SavingsAccount.new(1337, 1.00) + }.must_raise ArgumentError end + end describe "#withdraw" do + before do + @my_savings = Bank::SavingsAccount.new(1234, 500.00) + end + it "Applies a $2 fee each time" do - # TODO: Your test code here! + @my_savings.withdraw(10) + @my_savings.balance.must_equal(488.00) + + @my_savings.withdraw(10) + @my_savings.balance.must_equal(476.00) end it "Outputs a warning if the balance would go below $10" do - # TODO: Your test code here! + proc { @my_savings.withdraw(600) }.must_output(/.+/) end it "Doesn't modify the balance if it would go below $10" do - # TODO: Your test code here! + updated_balance = @my_savings.withdraw(600.00) + + # Both the value returned and the balance in the account + # must be un-modified. + updated_balance.must_equal 500 + @my_savings.balance.must_equal 500 end it "Doesn't modify the balance if the fee would put it below $10" do - # TODO: Your test code here! + updated_balance = @my_savings.withdraw(490.00) + + #Both the value returned and balance in account + #must be un-modified + updated_balance.must_equal 500 + @my_savings.balance.must_equal 500 end end describe "#add_interest" do + before do + @my_savings = Bank::SavingsAccount.new(1234, 500.00) + @my_interest = @my_savings.add_interest(0.25) + end + it "Returns the interest calculated" do - # TODO: Your test code here! + @my_interest.must_equal(1.25) end it "Updates the balance with calculated interest" do - # TODO: Your test code here! + @my_savings.balance.must_equal(501.25) end it "Requires a positive rate" do - # TODO: Your test code here! + proc { @my_savings.add_interest(-0.25) }.must_raise ArgumentError end + end + end