mirror of
https://github.com/wahyd4/cash-code-test.git
synced 2026-08-09 05:16:05 +10:00
code before onsite
This commit is contained in:
@@ -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{}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
OPEN_ACCOUNT Tom
|
||||
DEPOSIT Tom 520.30
|
||||
BALANCE Tom
|
||||
WITHDRAW Tom 340.00
|
||||
BALANCE Tom
|
||||
TOTAL_BALANCE
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user