Go - Write and test a program IV - Improvements

Source: Write and test a program in Go

From part I, part II and part III

Implement the transfer method

The ability to transfer money to another account.

Let’s start adding the test in bankcore/bank_test.go


//...

func TestTransfer(t *testing.T){
  // we need two accounts
  accountA := Account{
		Customer: Customer{
			Name:    "John",
			Address: "Los Angeles, California",
			Phone:   "(213) 555 0147",
		},
		Number:  1001,
		Balance: 0,
	}

	accountB := Account{
		Customer: Customer{
			Name:    "Mark",
			Address: "Irvine, California",
			Phone:   "(949) 555 0198",
		},
		Number:  1002,
		Balance: 0,
	}

  accountA.Deposit(100)
  err := accountA.Transfer(50, &accountB)

  if accountA.Balance != 50 && accountB.Balance != 50 {
		t.Error("transfer from account A to account B is not working", err)
	}
}

Then, let’s implement the method in bankcore/bank.go

package bank

import (
  "errors"
  "fmt"
)
// ...

// Transfer function
func (a *Account) Transfer(amount float64, dest *Account) error {
  if amount <= 0 {
		return errors.New("the amount to transfer should be greater than zero")
	}

	if a.Balance < amount {
		return errors.New("the amount to transfer should be greater than the account's balance")
	}

	a.Withdraw(amount)
	dest.Deposit(amount)
	return nil
}

We have now to write the API endpoint for the transfer method in bankapi/bank.go

func transfer(w http.ResponseWriter, req *http.Request) {
	numberqs := req.URL.Query().Get("number")
	amountqs := req.URL.Query().Get("amount")
	destqs := req.URL.Query().Get("dest")

	if numberqs == "" {
		fmt.Fprintf(w, "Account number is missing!")
		return
	}

	if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
		fmt.Fprintf(w, "Invalid account number!")
	} else if amount, err := strconv.ParseFloat(amountqs, 64); err != nil {
		fmt.Fprintf(w, "Invalid amount number!")
	} else if dest, err := strconv.ParseFloat(destqs, 64); err != nil {
		fmt.Fprintf(w, "Invalid account destination number!")
	} else {
		if accountA, ok := accounts[number]; !ok {
			fmt.Fprintf(w, "Account with number %v can't be found!", number)
		} else if accountB, ok := accounts[dest]; !ok {
			fmt.Fprintf(w, "Account with number %v can't be found!", dest)
		} else {
			err := accountA.Transfer(amount, accountB.Account)
			if err != nil {
				fmt.Fprintf(w, "%v", err)
			} else {
				fmt.Fprintf(w, accountA.Statement())
			}
		}
	}
}

Modify the statement endpoint to return a JSON object

We’d like you to make this change expecting that anyone who uses your core package might want to implement a different statement method to change the output.

We need to:

  • Create an interface with a Statement() string function
  • Create a new Statement() function in your core package that receives the interface that you created as a parameter. This function should call the Statement() method that your structures already have.

In bankapi/bank.go First we create interface with a Statement() string function

// Bank ...
type Bank interface {
	Statement() string
}

We also add the new Statement() function in it that calls the Statement() method:

func Statement(b bank) string {
  return b.Statement()
}

Now, we need to add the Statement() method in the main.go file:

// ...
// Statement ...
func (c *CustomAccount) Statement() string {
  // convert into JSON
	json, err := json.Marshal(c)
	if err != nil {
		return err.Error()
	}

	return string(json)
}

// where we define `CustomAccount` structure as
type CustomAccount struct {
  *bank.Account
}