code before onsite

This commit is contained in:
2019-12-17 07:25:03 +11:00
commit 8319200b4a
27 changed files with 1527 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
package workflow
import (
"bufio"
"fmt"
"github.com/wahyd4/cash/commands"
"github.com/wahyd4/cash/domains"
"os"
"strconv"
"strings"
)
const commandSplitter = " "
// APP holds the bank app instance
type APP struct {
Bank *domains.Bank
}
// Run all the commands passed in
func (app *APP) Run(commands []commands.ICommand) error {
for _, command := range commands {
if err := command.Run(app.Bank); err != nil {
return err
}
}
return nil
}
// Load loads inputs and generate commands
func (app *APP) Load(filePath string) ([]commands.ICommand, error) {
appCommands := make([]commands.ICommand, 0)
file, err := os.Open(filePath)
if err != nil {
return appCommands, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
commandString := scanner.Text()
appCommands = append(appCommands, buildCommand(commandString))
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("fail to read file %s with error: %v", filePath, err)
}
return appCommands, nil
}
func buildCommand(commandString string) commands.ICommand {
commandArgs := strings.Split(commandString, commandSplitter)
switch commandArgs[0] {
case "OPEN_ACCOUNT":
return &commands.OpenAccountCommand{Username: commandArgs[1]}
case "DEPOSIT":
money, _ := strconv.ParseFloat(commandArgs[2], 10)
return &commands.DepositCommand{
Account: commandArgs[1],
Money: money,
}
case "WITHDRAW":
money, _ := strconv.ParseFloat(commandArgs[2], 10)
return &commands.WithdrawCommand{
Account: commandArgs[1],
Money: money,
}
case "BALANCE":
return &commands.UserBalanceCommand{
Account: commandArgs[1],
}
case "TOTAL_BALANCE":
return &commands.TotalBalanceCommand{}
default:
return &commands.TotalBalanceCommand{}
}
}
+185
View File
@@ -0,0 +1,185 @@
package workflow
import (
"errors"
"github.com/stretchr/testify/assert"
"github.com/wahyd4/cash/commands"
"github.com/wahyd4/cash/domains"
"reflect"
"testing"
)
// simpleCommand a dump implementation for test
type simpleCommand struct {
shouldErr bool
}
func (command *simpleCommand) Run(bank *domains.Bank) error {
if command.shouldErr {
return errors.New("some error")
}
return nil
}
func TestAPP_Run(t *testing.T) {
type fields struct {
Bank *domains.Bank
}
type args struct {
commands []commands.ICommand
}
bank := domains.InitBank()
bank.OpenAccount("Dan")
tests := []struct {
name string
fields fields
args args
wantErr bool
totalBalanceInFloat float64
}{
{
"can finish all commands successfully",
fields{Bank: bank},
args{[]commands.ICommand{&simpleCommand{false}}},
false,
0.00,
},
{
"fail to run some commands",
fields{Bank: bank},
args{[]commands.ICommand{&simpleCommand{true}}},
true,
0.00,
},
{
"run real commands",
fields{Bank: bank},
args{[]commands.ICommand{
&commands.OpenAccountCommand{Username: "Tom"},
&commands.DepositCommand{
Account: "Tom",
Money: 520.30,
},
&commands.UserBalanceCommand{Account: "Tom"},
&commands.WithdrawCommand{
Account: "Tom",
Money: 340.00,
},
&commands.UserBalanceCommand{Account: "Tom"},
&commands.TotalBalanceCommand{},
},},
false,
180.30,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &APP{
Bank: tt.fields.Bank,
}
err := app.Run(tt.args.commands)
if (err != nil) != tt.wantErr {
t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr)
}
if err == nil {
assert.Equal(t, tt.totalBalanceInFloat, bank.TotalBalance())
}
})
}
}
func TestAPP_Load(t *testing.T) {
bank := domains.InitBank()
type fields struct {
Bank *domains.Bank
}
type args struct {
filePath string
}
tests := []struct {
name string
fields fields
args args
want []commands.ICommand
wantErr bool
}{
{
"build commands from file input",
fields{Bank: bank},
args{filePath: "test_input.txt"},
[]commands.ICommand{
&commands.OpenAccountCommand{Username: "Tom"},
&commands.DepositCommand{
Account: "Tom",
Money: 520.30,
},
&commands.UserBalanceCommand{Account: "Tom"},
&commands.WithdrawCommand{
Account: "Tom",
Money: 340.00,
},
&commands.UserBalanceCommand{Account: "Tom"},
&commands.TotalBalanceCommand{},
},
false,
},
{
"build commands from file input for multiple users",
fields{Bank: bank},
args{filePath: "test_input_multiple_users.txt"},
[]commands.ICommand{
&commands.OpenAccountCommand{Username: "Tom"},
&commands.DepositCommand{
Account: "Tom",
Money: 520.30,
},
&commands.UserBalanceCommand{Account: "Tom"},
&commands.WithdrawCommand{
Account: "Tom",
Money: 340.00,
},
&commands.UserBalanceCommand{Account: "Tom"},
&commands.OpenAccountCommand{Username: "Lily"},
&commands.DepositCommand{
Account: "Lily",
Money: 1980.55,
},
&commands.UserBalanceCommand{Account: "Lily"},
&commands.WithdrawCommand{
Account: "Lily",
Money: 550.33,
},
&commands.UserBalanceCommand{Account: "Lily"},
&commands.TotalBalanceCommand{},
},
false,
},
{
"fail to load inputs",
fields{Bank: bank},
args{filePath: "doesnt_exist.txt"},
[]commands.ICommand{},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &APP{
Bank: tt.fields.Bank,
}
got, err := app.Load(tt.args.filePath)
if (err != nil) != tt.wantErr {
t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Load() got = %v, want %v", got, tt.want)
}
})
}
}
+6
View File
@@ -0,0 +1,6 @@
OPEN_ACCOUNT Tom
DEPOSIT Tom 520.30
BALANCE Tom
WITHDRAW Tom 340.00
BALANCE Tom
TOTAL_BALANCE
+11
View File
@@ -0,0 +1,11 @@
OPEN_ACCOUNT Tom
DEPOSIT Tom 520.30
BALANCE Tom
WITHDRAW Tom 340.00
BALANCE Tom
OPEN_ACCOUNT Lily
DEPOSIT Lily 1980.55
BALANCE Lily
WITHDRAW Lily 550.33
BALANCE Lily
TOTAL_BALANCE