From 77f689bc28cbb44450ccbc296ebde33359b58056 Mon Sep 17 00:00:00 2001 From: Jan Cajthaml Date: Mon, 24 Jun 2019 18:38:34 +0200 Subject: [PATCH] account format (#146) --- bbtest/features/api.feature | 6 ++ bbtest/features/journal.feature | 2 + bbtest/features/messaging.feature | 10 +-- bbtest/helpers/journal_helper.rb | 10 ++- bbtest/steps/account_steps.rb | 3 + bbtest/steps/journal_steps.rb | 2 + packaging/debian_amd64/DEBIAN/control | 2 +- services/vault-rest/actor/account.go | 2 +- services/vault-rest/actor/common.go | 9 ++- services/vault-rest/actor/messages.go | 6 +- services/vault-rest/model/model.go | 6 ++ services/vault-unit/actor/account.go | 77 ++++++++++--------- services/vault-unit/actor/common.go | 9 ++- services/vault-unit/actor/messages.go | 10 ++- services/vault-unit/actor/messages_test.go | 27 ++++++- services/vault-unit/model/model.go | 59 ++++++++++---- services/vault-unit/model/model_test.go | 20 +++-- services/vault-unit/persistence/journal.go | 10 ++- .../vault-unit/persistence/journal_test.go | 18 +++-- .../persistence/snapshot_updater_test.go | 4 +- 20 files changed, 190 insertions(+), 102 deletions(-) diff --git a/bbtest/features/api.feature b/bbtest/features/api.feature index 47b1580a..bb3f7ab5 100644 --- a/bbtest/features/api.feature +++ b/bbtest/features/api.feature @@ -67,6 +67,7 @@ Feature: REST """ { "name": "A", + "format": "test", "currency": "XXX", "isBalanceCheck": false } @@ -80,6 +81,7 @@ Feature: REST """ { "name": "yyy", + "format": "test", "currency": "XXX", "isBalanceCheck": false } @@ -93,6 +95,7 @@ Feature: REST """ { "name": "yyy", + "format": "test", "currency": "XXX", "isBalanceCheck": false } @@ -106,6 +109,7 @@ Feature: REST """ { "name": "B", + "format": "test", "currency": "XXX", "isBalanceCheck": false } @@ -125,6 +129,7 @@ Feature: REST """ { "name": "xxx", + "format": "test", "currency": "XXX", "isBalanceCheck": false } @@ -135,6 +140,7 @@ Feature: REST Then curl responds with 200 """ { + "format": "TEST", "currency": "XXX", "balance": "0", "blocking": "0", diff --git a/bbtest/features/journal.feature b/bbtest/features/journal.feature index 631ef76a..d1cc09b1 100644 --- a/bbtest/features/journal.feature +++ b/bbtest/features/journal.feature @@ -18,6 +18,7 @@ Feature: Persistent journal "promiseBuffer": [], "accountName": "Euro", "isBalanceCheck": false, + "format": "TEST", "currency": "EUR" } """ @@ -32,6 +33,7 @@ Feature: Persistent journal "promiseBuffer": [], "accountName": "Ripple", "isBalanceCheck": true, + "format": "TEST", "currency": "XRP" } """ diff --git a/bbtest/features/messaging.feature b/bbtest/features/messaging.feature index 73fb66c1..0e90e4f4 100644 --- a/bbtest/features/messaging.feature +++ b/bbtest/features/messaging.feature @@ -4,18 +4,18 @@ Feature: Messaging behaviour Given tenant MSG1 is onbdoarded And lake is empty - When lake recieves "VaultUnit/MSG1 VaultRest account_name_1 req_id_1 NA EUR f" + When lake recieves "VaultUnit/MSG1 VaultRest account_name_1 req_id_1 NA test EUR f" Then lake responds with "VaultRest VaultUnit/MSG1 req_id_1 account_name_1 AN" Scenario: get balance Given tenant MSG2 is onbdoarded And lake is empty - When lake recieves "VaultUnit/MSG2 VaultRest account_name_3 req_id_3 NA EUR f" + When lake recieves "VaultUnit/MSG2 VaultRest account_name_3 req_id_3 NA test EUR f" Then lake responds with "VaultRest VaultUnit/MSG2 req_id_3 account_name_3 AN" When lake recieves "VaultUnit/MSG2 VaultRest account_name_3 req_id_3 GS" - Then lake responds with "VaultRest VaultUnit/MSG2 req_id_3 account_name_3 S0 EUR f 0 0" + Then lake responds with "VaultRest VaultUnit/MSG2 req_id_3 account_name_3 S0 TEST EUR f 0 0" When lake recieves "VaultUnit/MSG2 VaultRest account_name_4 req_id_3 GS" Then lake responds with "VaultRest VaultUnit/MSG2 req_id_3 account_name_4 S1" @@ -24,7 +24,7 @@ Feature: Messaging behaviour Given tenant MSG3 is onbdoarded And lake is empty - When lake recieves "VaultUnit/MSG3 VaultRest account_name_2 req_id_2 NA EUR f" - And lake recieves "VaultUnit/MSG3 VaultRest account_name_2 req_id_2 NA EUR f" + When lake recieves "VaultUnit/MSG3 VaultRest account_name_2 req_id_2 NA test EUR f" + And lake recieves "VaultUnit/MSG3 VaultRest account_name_2 req_id_2 NA test EUR f" Then lake responds with "VaultRest VaultUnit/MSG3 req_id_2 account_name_2 AN" And lake responds with "VaultRest VaultUnit/MSG3 req_id_2 account_name_2 EE" diff --git a/bbtest/helpers/journal_helper.rb b/bbtest/helpers/journal_helper.rb index b7f0bbe6..adbbcf68 100644 --- a/bbtest/helpers/journal_helper.rb +++ b/bbtest/helpers/journal_helper.rb @@ -13,8 +13,9 @@ def self.account_snapshot(tenant, account, version) lines = data.split("\n").map(&:strip) { - :balance_check => lines[0][0] != 'F', - :currency => lines[0][1..3], + :balance_check => lines[0][lines[0].length-1..-1] != "F", + :format => lines[0][4...-2], + :currency => lines[0][0..2], :account_name => account, :version => version.to_i, :balance => '%g' % BigDecimal.new(lines[1]).to_s('F'), @@ -40,8 +41,9 @@ def self.account_latest_snapshot(tenant, account) lines = data.split("\n").map(&:strip) { - :balance_check => lines[0][0] != 'F', - :currency => lines[0][1..3], + :balance_check => lines[0][lines[0].length-1..-1] != "F", + :format => lines[0][4...-2], + :currency => lines[0][0..2], :account_name => account, :version => snapshots[0].to_i, :balance => '%g' % BigDecimal.new(lines[1]).to_s('F'), diff --git a/bbtest/steps/account_steps.rb b/bbtest/steps/account_steps.rb index be7b49d5..f5059057 100644 --- a/bbtest/steps/account_steps.rb +++ b/bbtest/steps/account_steps.rb @@ -14,6 +14,7 @@ expected_response = { balance: "0", blocking: "0", + format: meta[:format], currency: meta[:currency], isBalanceCheck: (snapshot[:activity] || false) }.to_json @@ -33,6 +34,7 @@ payload = { name: account, + format: 'test', currency: currency, isBalanceCheck: activity }.to_json @@ -44,6 +46,7 @@ @accounts[account] = { :currency => currency, + :format => 'test', :activity => activity, :balance => '%g' % BigDecimal.new(0).to_s('F'), :promised => '%g' % BigDecimal.new(0).to_s('F'), diff --git a/bbtest/steps/journal_steps.rb b/bbtest/steps/journal_steps.rb index 71db9ea4..95f86ca2 100644 --- a/bbtest/steps/journal_steps.rb +++ b/bbtest/steps/journal_steps.rb @@ -7,10 +7,12 @@ step "snapshot :account version :count should be" do |account, version, expectation| (tenant, account) = account.split('/') actual = JournalHelper.account_snapshot(tenant, account, version) + expectation = JSON.parse(expectation) expect(actual[:version]).to eq(expectation["version"]) expect(actual[:balance]).to eq(expectation["balance"]) + expect(actual[:format]).to eq(expectation["format"]) expect(actual[:promised]).to eq(expectation["promised"]) expect(actual[:account_name]).to eq(expectation["accountName"]) expect(actual[:balance_check]).to eq(expectation["isBalanceCheck"]) diff --git a/packaging/debian_amd64/DEBIAN/control b/packaging/debian_amd64/DEBIAN/control index c48f62a8..9fe9cd38 100755 --- a/packaging/debian_amd64/DEBIAN/control +++ b/packaging/debian_amd64/DEBIAN/control @@ -1,5 +1,5 @@ Package: vault -Version: 1.2.7+master +Version: 1.2.7+account-format Section: misc Priority: extra Architecture: amd64 diff --git a/services/vault-rest/actor/account.go b/services/vault-rest/actor/account.go index ea38b529..ec1b3571 100644 --- a/services/vault-rest/actor/account.go +++ b/services/vault-rest/actor/account.go @@ -51,7 +51,7 @@ func CreateAccount(sys *ActorSystem, tenant string, account model.Account) (resu ch <- context.Data }) - sys.SendRemote(CreateAccountMessage(tenant, envelope.Name, account.Name, account.Currency, account.IsBalanceCheck)) + sys.SendRemote(CreateAccountMessage(tenant, envelope.Name, account.Name, account.Format, account.Currency, account.IsBalanceCheck)) select { diff --git a/services/vault-rest/actor/common.go b/services/vault-rest/actor/common.go index 025a75f6..bea85998 100644 --- a/services/vault-rest/actor/common.go +++ b/services/vault-rest/actor/common.go @@ -77,10 +77,11 @@ func ProcessRemoteMessage(s *ActorSystem) system.ProcessRemoteMessage { case RespAccountState: message = &model.Account{ - Currency: parts[5], - IsBalanceCheck: parts[6] != "f", - Balance: parts[7], - Blocking: parts[8], + Format: parts[5], + Currency: parts[6], + IsBalanceCheck: parts[7] != "f", + Balance: parts[8], + Blocking: parts[9], } case RespAccountMissing: diff --git a/services/vault-rest/actor/messages.go b/services/vault-rest/actor/messages.go index 12c3adb7..998457e2 100644 --- a/services/vault-rest/actor/messages.go +++ b/services/vault-rest/actor/messages.go @@ -30,11 +30,11 @@ const ( ) // CreateAccountMessage is message for creation of new account -func CreateAccountMessage(tenant string, sender string, name string, currency string, isBalanceCheck bool) string { +func CreateAccountMessage(tenant string, sender string, name string, format string, currency string, isBalanceCheck bool) string { if isBalanceCheck { - return "VaultUnit/" + tenant + " VaultRest " + name + " " + sender + " " + ReqCreateAccount + " " + currency + " t" + return "VaultUnit/" + tenant + " VaultRest " + name + " " + sender + " " + ReqCreateAccount + " " + format + " " + currency + " t" } - return "VaultUnit/" + tenant + " VaultRest " + name + " " + sender + " " + ReqCreateAccount + " " + currency + " f" + return "VaultUnit/" + tenant + " VaultRest " + name + " " + sender + " " + ReqCreateAccount + " " + format + " " + currency + " f" } // GetAccountMessage is message for getting balance of account diff --git a/services/vault-rest/model/model.go b/services/vault-rest/model/model.go index 03b14e68..f09f4d33 100644 --- a/services/vault-rest/model/model.go +++ b/services/vault-rest/model/model.go @@ -33,6 +33,7 @@ type AccountMissing struct{} // Account represents account type Account struct { Name string `json:"-"` + Format string `json:"format"` Currency string `json:"currency"` Balance string `json:"balance"` Blocking string `json:"blocking"` @@ -46,6 +47,7 @@ func (entity *Account) UnmarshalJSON(data []byte) error { } all := struct { Name string `json:"name"` + Format string `json:"format"` Currency string `json:"currency"` IsBalanceCheck *bool `json:"isBalanceCheck"` }{} @@ -56,6 +58,9 @@ func (entity *Account) UnmarshalJSON(data []byte) error { if all.Name == "" { return fmt.Errorf("missing attribute \"name\"") } + if all.Format == "" { + return fmt.Errorf("missing attribute \"format\"") + } if all.Currency == "" { return fmt.Errorf("missing attribute \"currency\"") } @@ -72,6 +77,7 @@ func (entity *Account) UnmarshalJSON(data []byte) error { } entity.Name = strings.Replace(all.Name, " ", "_", -1) + entity.Format = strings.Replace(all.Format, " ", "_", -1) entity.Currency = all.Currency return nil } diff --git a/services/vault-unit/actor/account.go b/services/vault-unit/actor/account.go index 6eb336e0..88a9b0f5 100644 --- a/services/vault-unit/actor/account.go +++ b/services/vault-unit/actor/account.go @@ -30,14 +30,14 @@ func NilAccount(s *ActorSystem) func(interface{}, system.Context) { return func(t_state interface{}, context system.Context) { state := t_state.(model.Account) - snapshotHydration := persistence.LoadAccount(s.Storage, state.AccountName) + snapshotHydration := persistence.LoadAccount(s.Storage, state.Name) if snapshotHydration == nil { context.Self.Become(state, NonExistAccount(s)) - log.Debugf("%s ~ Nil -> NonExist", state.AccountName) + log.Debugf("%s ~ Nil -> NonExist", state.Name) } else { context.Self.Become(*snapshotHydration, ExistAccount(s)) - log.Debugf("%s ~ Nil -> Exist", state.AccountName) + log.Debugf("%s ~ Nil -> Exist", state.Name) } context.Self.Receive(context) @@ -54,12 +54,13 @@ func NonExistAccount(s *ActorSystem) func(interface{}, system.Context) { case model.CreateAccount: currency := strings.ToUpper(msg.Currency) isBalanceCheck := msg.IsBalanceCheck + format := strings.ToUpper(msg.Format) - snaphostResult := persistence.CreateAccount(s.Storage, state.AccountName, currency, isBalanceCheck) + snaphostResult := persistence.CreateAccount(s.Storage, state.Name, format, currency, isBalanceCheck) if snaphostResult == nil { s.SendRemote(FatalErrorMessage(context)) - log.Debugf("%s ~ (NonExist CreateAccount) Error", state.AccountName) + log.Debugf("%s ~ (NonExist CreateAccount) Error", state.Name) return } @@ -68,20 +69,20 @@ func NonExistAccount(s *ActorSystem) func(interface{}, system.Context) { s.SendRemote(AccountCreatedMessage(context)) context.Self.Become(*snaphostResult, ExistAccount(s)) - log.Infof("New Account %s Created", state.AccountName) - log.Debugf("%s ~ (NonExist CreateAccount) OK", state.AccountName) + log.Infof("New Account %s Created", state.Name) + log.Debugf("%s ~ (NonExist CreateAccount) OK", state.Name) case model.Rollback: s.SendRemote(RollbackAcceptedMessage(context)) - log.Debugf("%s ~ (NonExist Rollback) OK", state.AccountName) + log.Debugf("%s ~ (NonExist Rollback) OK", state.Name) case model.GetAccountState: s.SendRemote(AccountMissingMessage(context)) - log.Debugf("%s ~ (NonExist GetAccountState) Error", state.AccountName) + log.Debugf("%s ~ (NonExist GetAccountState) Error", state.Name) default: s.SendRemote(FatalErrorMessage(context)) - log.Debugf("%s ~ (NonExist Unknown Message) Error", state.AccountName) + log.Debugf("%s ~ (NonExist Unknown Message) Error", state.Name) } return @@ -96,32 +97,32 @@ func ExistAccount(s *ActorSystem) func(interface{}, system.Context) { switch msg := context.Data.(type) { case model.GetAccountState: - s.SendRemote(AccountStateMessage(context, state.Currency, state.Balance.String(), state.Promised.String(), state.IsBalanceCheck)) - log.Debugf("%s ~ (Exist GetAccountState) OK", state.AccountName) + s.SendRemote(AccountStateMessage(context, state)) + log.Debugf("%s ~ (Exist GetAccountState) OK", state.Name) case model.CreateAccount: s.SendRemote(FatalErrorMessage(context)) - log.Debugf("%s ~ (Exist CreateAccount) Error", state.AccountName) + log.Debugf("%s ~ (Exist CreateAccount) Error", state.Name) case model.Promise: if state.PromiseBuffer.Contains(msg.Transaction) { s.SendRemote(PromiseAcceptedMessage(context)) - log.Debugf("%s ~ (Exist Promise) OK Already Accepted", state.AccountName) + log.Debugf("%s ~ (Exist Promise) OK Already Accepted", state.Name) return } if state.Currency != msg.Currency { s.SendRemote(PromiseRejectedMessage(context, "CURRENCY_MISMATCH")) - log.Warnf("%s ~ (Exist Promise) Error Currency Mismatch", state.AccountName) + log.Warnf("%s ~ (Exist Promise) Error Currency Mismatch", state.Name) return } nextPromised := new(money.Dec).Add(state.Promised, msg.Amount) if !state.IsBalanceCheck || new(money.Dec).Add(state.Balance, nextPromised).Sign() >= 0 { - if err := persistence.PersistPromise(s.Storage, state.AccountName, state.Version, msg.Amount, msg.Transaction); err != nil { + if err := persistence.PersistPromise(s.Storage, state.Name, state.Version, msg.Amount, msg.Transaction); err != nil { s.SendRemote(PromiseRejectedMessage(context, "STORAGE_ERROR")) - log.Warnf("%s ~ (Exist Promise) Error Could not Persist %+v", state.AccountName, err) + log.Warnf("%s ~ (Exist Promise) Error Could not Persist %+v", state.Name, err) return } @@ -134,33 +135,33 @@ func ExistAccount(s *ActorSystem) func(interface{}, system.Context) { s.SendRemote(PromiseAcceptedMessage(context)) context.Self.Become(next, ExistAccount(s)) - log.Infof("Account %s Promised %s %s", state.AccountName, msg.Amount.String(), state.Currency) - log.Debugf("%s ~ (Exist Promise) OK", state.AccountName) + log.Infof("Account %s Promised %s %s", state.Name, msg.Amount.String(), state.Currency) + log.Debugf("%s ~ (Exist Promise) OK", state.Name) return } if new(money.Dec).Sub(state.Balance, msg.Amount).Sign() < 0 { s.SendRemote(PromiseRejectedMessage(context, "INSUFFICIESNT_FUNDS")) - log.Debugf("%s ~ (Exist Promise) Error Insufficient Funds", state.AccountName) + log.Debugf("%s ~ (Exist Promise) Error Insufficient Funds", state.Name) return } // FIXME boucing not handled s.SendRemote(FatalErrorMessage(context)) - log.Warnf("%s ~ (Exist Promise) Error ... (Bounce?)", state.AccountName) + log.Warnf("%s ~ (Exist Promise) Error ... (Bounce?)", state.Name) return case model.Commit: if !state.PromiseBuffer.Contains(msg.Transaction) { s.SendRemote(CommitAcceptedMessage(context)) - log.Debugf("%s ~ (Exist Commit) OK Already Accepted", state.AccountName) + log.Debugf("%s ~ (Exist Commit) OK Already Accepted", state.Name) return } - if err := persistence.PersistCommit(s.Storage, state.AccountName, state.Version, msg.Amount, msg.Transaction); err != nil { + if err := persistence.PersistCommit(s.Storage, state.Name, state.Version, msg.Amount, msg.Transaction); err != nil { s.SendRemote(CommitRejectedMessage(context, "STORAGE_ERROR")) - log.Warnf("%s ~ (Exist Commit) Error Could not Persist %+v", state.AccountName, err) + log.Warnf("%s ~ (Exist Commit) Error Could not Persist %+v", state.Name, err) return } @@ -174,19 +175,19 @@ func ExistAccount(s *ActorSystem) func(interface{}, system.Context) { s.SendRemote(CommitAcceptedMessage(context)) context.Self.Become(next, ExistAccount(s)) - log.Debugf("%s ~ (Exist Commit) OK", state.AccountName) + log.Debugf("%s ~ (Exist Commit) OK", state.Name) return case model.Rollback: if !state.PromiseBuffer.Contains(msg.Transaction) { s.SendRemote(RollbackAcceptedMessage(context)) - log.Debugf("%s ~ (Exist Rollback) OK Already Accepted", state.AccountName) + log.Debugf("%s ~ (Exist Rollback) OK Already Accepted", state.Name) return } - if err := persistence.PersistRollback(s.Storage, state.AccountName, state.Version, msg.Amount, msg.Transaction); err != nil { + if err := persistence.PersistRollback(s.Storage, state.Name, state.Version, msg.Amount, msg.Transaction); err != nil { s.SendRemote(RollbackRejectedMessage(context, "STORAGE_ERROR")) - log.Warnf("%s ~ (Exist Rollback) Error Could not Persist %+v", state.AccountName, err) + log.Warnf("%s ~ (Exist Rollback) Error Could not Persist %+v", state.Name, err) return } @@ -199,35 +200,35 @@ func ExistAccount(s *ActorSystem) func(interface{}, system.Context) { s.SendRemote(RollbackAcceptedMessage(context)) context.Self.Become(next, ExistAccount(s)) - log.Infof("Account %s Rejected %s %s", state.AccountName, msg.Amount.String(), state.Currency) - log.Debugf("%s ~ (Exist Rollback) OK", state.AccountName) + log.Infof("Account %s Rejected %s %s", state.Name, msg.Amount.String(), state.Currency) + log.Debugf("%s ~ (Exist Rollback) OK", state.Name) return case model.Update: if msg.Version != state.Version { - log.Debugf("%s ~ (Exist Update) Error Already Updated", state.AccountName) + log.Debugf("%s ~ (Exist Update) Error Already Updated", state.Name) return } - result := persistence.LoadAccount(s.Storage, state.AccountName) + result := persistence.LoadAccount(s.Storage, state.Name) if result == nil { - log.Warnf("%s ~ (Exist Update) Error no existing snapshot", state.AccountName) + log.Warnf("%s ~ (Exist Update) Error no existing snapshot", state.Name) return } - next := persistence.UpdateAccount(s.Storage, state.AccountName, result) + next := persistence.UpdateAccount(s.Storage, state.Name, result) if next == nil { - log.Warnf("%s ~ (Exist Update) Error unable to update", state.AccountName) + log.Warnf("%s ~ (Exist Update) Error unable to update", state.Name) return } context.Self.Become(*next, ExistAccount(s)) - log.Infof("Account %s Updated Snapshot to %d", state.AccountName, next.Version) - log.Debugf("%s ~ (Exist Update) OK", state.AccountName) + log.Infof("Account %s Updated Snapshot to %d", state.Name, next.Version) + log.Debugf("%s ~ (Exist Update) OK", state.Name) default: s.SendRemote(FatalErrorMessage(context)) - log.Warnf("%s ~ (Exist Unknown Message) Error", state.AccountName) + log.Warnf("%s ~ (Exist Unknown Message) Error", state.Name) } diff --git a/services/vault-unit/actor/common.go b/services/vault-unit/actor/common.go index 7fc8b584..da70e47d 100644 --- a/services/vault-unit/actor/common.go +++ b/services/vault-unit/actor/common.go @@ -122,11 +122,12 @@ func ProcessRemoteMessage(s *ActorSystem) system.ProcessRemoteMessage { message = model.GetAccountState{} case ReqCreateAccount: - if len(parts) == 7 { + if len(parts) == 8 { message = model.CreateAccount{ - AccountName: to.Name, - Currency: parts[5], - IsBalanceCheck: parts[6] != "f", + Name: to.Name, + Format: parts[5], + Currency: parts[6], + IsBalanceCheck: parts[7] != "f", } } diff --git a/services/vault-unit/actor/messages.go b/services/vault-unit/actor/messages.go index ec0ff3b9..9ca3fb17 100644 --- a/services/vault-unit/actor/messages.go +++ b/services/vault-unit/actor/messages.go @@ -15,6 +15,8 @@ package actor import ( + "github.com/jancajthaml-openbank/vault-unit/model" + system "github.com/jancajthaml-openbank/actor-system" ) @@ -104,12 +106,12 @@ func RollbackRejectedMessage(context system.Context, reason string) string { } // AccountStateMessage is reply message carrying account state -func AccountStateMessage(context system.Context, currency string, balance string, promised string, isBalanceCheck bool) string { - if isBalanceCheck { - return context.Sender.Region + " " + context.Receiver.Region + " " + context.Sender.Name + " " + context.Receiver.Name + " " + RespAccountState + " " + currency + " t " + balance + " " + promised +func AccountStateMessage(context system.Context, state model.Account) string { + if state.IsBalanceCheck { + return context.Sender.Region + " " + context.Receiver.Region + " " + context.Sender.Name + " " + context.Receiver.Name + " " + RespAccountState + " " + state.Format + " " + state.Currency + " t " + state.Balance.String() + " " + state.Promised.String() } - return context.Sender.Region + " " + context.Receiver.Region + " " + context.Sender.Name + " " + context.Receiver.Name + " " + RespAccountState + " " + currency + " f " + balance + " " + promised + return context.Sender.Region + " " + context.Receiver.Region + " " + context.Sender.Name + " " + context.Receiver.Name + " " + RespAccountState + " " + state.Format + " " + state.Currency + " f " + state.Balance.String() + " " + state.Promised.String() } // AccountMissingMessage is reply message informing that account does not exist diff --git a/services/vault-unit/actor/messages_test.go b/services/vault-unit/actor/messages_test.go index c28cdb8a..3c62bbb6 100644 --- a/services/vault-unit/actor/messages_test.go +++ b/services/vault-unit/actor/messages_test.go @@ -3,7 +3,10 @@ package actor import ( "testing" + "github.com/jancajthaml-openbank/vault-unit/model" + system "github.com/jancajthaml-openbank/actor-system" + money "gopkg.in/inf.v0" "github.com/stretchr/testify/assert" ) @@ -62,7 +65,27 @@ func TestMessagesIntegrity(t *testing.T) { t.Log("AccountStateMessage") { - assert.Equal(t, "FROM_REGION TO_REGION FROM_NAME TO_NAME S0 CURRENCY t BALANCE PROMISED", AccountStateMessage(context, "CURRENCY", "BALANCE", "PROMISED", true)) - assert.Equal(t, "FROM_REGION TO_REGION FROM_NAME TO_NAME S0 CURRENCY f BALANCE PROMISED", AccountStateMessage(context, "CURRENCY", "BALANCE", "PROMISED", false)) + balance, _ := new(money.Dec).SetString("1.0") + promised, _ := new(money.Dec).SetString("2.0") + + a := model.Account{ + Format: "FORMAT", + Currency: "CURRENCY", + IsBalanceCheck: true, + Balance: balance, + Promised: promised, + } + + assert.Equal(t, "FROM_REGION TO_REGION FROM_NAME TO_NAME S0 FORMAT CURRENCY t 1.0 2.0", AccountStateMessage(context, a)) + + b := model.Account{ + Format: "FORMAT", + Currency: "CURRENCY", + IsBalanceCheck: false, + Balance: balance, + Promised: promised, + } + + assert.Equal(t, "FROM_REGION TO_REGION FROM_NAME TO_NAME S0 FORMAT CURRENCY f 1.0 2.0", AccountStateMessage(context, b)) } } diff --git a/services/vault-unit/model/model.go b/services/vault-unit/model/model.go index 0c073eeb..7ed21cfa 100644 --- a/services/vault-unit/model/model.go +++ b/services/vault-unit/model/model.go @@ -22,7 +22,8 @@ import ( // Account represents metadata of account entity type Account struct { - AccountName string `json:"name"` + Name string `json:"name"` + Format string `json:"format"` Currency string `json:"currency"` IsBalanceCheck bool `json:"isBalanceCheck"` Balance *money.Dec @@ -34,7 +35,8 @@ type Account struct { // Copy returns copy of Account func (entity Account) Copy() Account { return Account{ - AccountName: entity.AccountName, + Name: entity.Name, + Format: entity.Format, Currency: entity.Currency, IsBalanceCheck: entity.IsBalanceCheck, Balance: new(money.Dec).Set(entity.Balance), @@ -46,7 +48,8 @@ func (entity Account) Copy() Account { // CreateAccount is inbound request for creation of new account type CreateAccount struct { - AccountName string + Name string + Format string Currency string IsBalanceCheck bool } @@ -98,7 +101,8 @@ type Rollbacked struct { // NewAccount returns new Account func NewAccount(name string) Account { return Account{ - AccountName: name, + Name: name, + Format: "???", Currency: "???", IsBalanceCheck: true, Balance: new(money.Dec), @@ -112,23 +116,32 @@ func NewAccount(name string) Account { func (entity Account) Serialise() []byte { var buffer bytes.Buffer + // [CURRENCY FORMAT_IS-CHECK] + // [BALANCE] + // [PROMISED] + // [...PROMISE-BUFFER] + + buffer.WriteString("???"[0 : 3-len(entity.Currency)]) + buffer.WriteString(entity.Currency) + buffer.WriteString(" ") + + buffer.WriteString(entity.Format) if entity.IsBalanceCheck { - buffer.WriteString("T") + buffer.WriteString("_T") } else { - buffer.WriteString("F") + buffer.WriteString("_F") } - buffer.WriteString("???"[0 : 3-len(entity.Currency)]) - buffer.WriteString(entity.Currency) buffer.WriteString("\n") if entity.Balance == nil { - buffer.WriteString("0.0\n") + buffer.WriteString("0.0") } else { buffer.WriteString(entity.Balance.String()) - buffer.WriteString("\n") } + buffer.WriteString("\n") + if entity.Promised == nil { buffer.WriteString("0.0") } else { @@ -150,21 +163,35 @@ func (entity *Account) Deserialise(data []byte) { } entity.PromiseBuffer = NewTransactionSet() - entity.IsBalanceCheck = string(data[0]) != "F" - entity.Currency = string(data[1:4]) + entity.Currency = string(data[0:3]) var ( - i = 5 - j = bytes.IndexByte(data[5:], '\n') + 5 + i = 4 + j = bytes.IndexByte(data[4:], '\n') + 4 ) - entity.Balance, _ = new(money.Dec).SetString(string(data[i:j])) + format := string(data[i:j]) + + entity.IsBalanceCheck = (format[len(format)-1:] != "F") + entity.Format = format[:len(format)-2] i = j + 1 + + j = bytes.IndexByte(data[i:], '\n') + if j < 0 { + if len(data) > 0 { + entity.Balance, _ = new(money.Dec).SetString(string(data[i])) + } + return + } + j += i + entity.Balance, _ = new(money.Dec).SetString(string(data[i:j])) + i = j + 1 + j = bytes.IndexByte(data[i:], '\n') if j < 0 { if len(data) > 0 { - entity.Promised, _ = new(money.Dec).SetString(string(data[i:])) + entity.Promised, _ = new(money.Dec).SetString(string(data[i])) } return } diff --git a/services/vault-unit/model/model_test.go b/services/vault-unit/model/model_test.go index 3ed5c3c6..9f1ae3b1 100644 --- a/services/vault-unit/model/model_test.go +++ b/services/vault-unit/model/model_test.go @@ -14,7 +14,8 @@ func TestAccount_Serialise(t *testing.T) { { entity := new(Account) - entity.AccountName = "accountName" + entity.Name = "accountName" + entity.Format = "FOR" entity.Currency = "CUR" entity.IsBalanceCheck = false entity.Version = 3 @@ -33,10 +34,10 @@ func TestAccount_Serialise(t *testing.T) { data := entity.Serialise() require.NotNil(t, data) - assert.Equal(t, "FCUR\n1.0\n2.0\nA\nB\nC\nD\nE\nF\nG\nH", string(data)) + assert.Equal(t, "CUR FOR_F\n1.0\n2.0\nA\nB\nC\nD\nE\nF\nG\nH", string(data)) hydrated := new(Account) - hydrated.AccountName = entity.AccountName + hydrated.Name = entity.Name hydrated.Version = entity.Version hydrated.Deserialise(data) @@ -48,30 +49,32 @@ func TestAccount_Serialise(t *testing.T) { { entity := new(Account) entity.IsBalanceCheck = true + entity.Format = "FOR" entity.Currency = "CUR" data := entity.Serialise() require.NotNil(t, data) - assert.Equal(t, data, []byte("TCUR\n0.0\n0.0")) + assert.Equal(t, data, []byte("CUR FOR_T\n0.0\n0.0")) } t.Log("serialized isBalanceCheck") { yes := new(Account) yes.IsBalanceCheck = true - assert.Equal(t, yes.Serialise(), []byte("T???\n0.0\n0.0")) + assert.Equal(t, yes.Serialise(), []byte("??? _T\n0.0\n0.0")) no := new(Account) no.IsBalanceCheck = false - assert.Equal(t, no.Serialise(), []byte("F???\n0.0\n0.0")) + assert.Equal(t, no.Serialise(), []byte("??? _F\n0.0\n0.0")) } } func BenchmarkAccount_Serialise(b *testing.B) { entity := new(Account) - entity.AccountName = "accountName" + entity.Name = "accountName" + entity.Format = "accountFormat" entity.Currency = "CUR" entity.IsBalanceCheck = false entity.Balance = new(money.Dec) @@ -90,7 +93,8 @@ func BenchmarkAccount_Serialise(b *testing.B) { func BenchmarkAccount_Deserialise(b *testing.B) { entity := new(Account) - entity.AccountName = "accountName" + entity.Name = "accountName" + entity.Format = "accountFormat" entity.Currency = "CUR" entity.IsBalanceCheck = false diff --git a/services/vault-unit/persistence/journal.go b/services/vault-unit/persistence/journal.go index 15c28df9..6db87d28 100644 --- a/services/vault-unit/persistence/journal.go +++ b/services/vault-unit/persistence/journal.go @@ -48,7 +48,7 @@ func LoadAccount(storage *localfs.Storage, name string) *model.Account { } result.Version = version - result.AccountName = name + result.Name = name result.Deserialise(data) events, err := storage.ListDirectory(utils.EventPath(name, result.Version), false) @@ -82,13 +82,14 @@ func LoadAccount(storage *localfs.Storage, name string) *model.Account { } // CreateAccount persist account entity state to storage -func CreateAccount(storage *localfs.Storage, name, currency string, isBalanceCheck bool) *model.Account { +func CreateAccount(storage *localfs.Storage, name, format, currency string, isBalanceCheck bool) *model.Account { return PersistAccount(storage, name, &model.Account{ Balance: new(money.Dec), Promised: new(money.Dec), PromiseBuffer: model.NewTransactionSet(), Version: 0, - AccountName: name, + Name: name, + Format: format, Currency: currency, IsBalanceCheck: isBalanceCheck, }) @@ -106,7 +107,8 @@ func UpdateAccount(storage *localfs.Storage, name string, entity *model.Account) PromiseBuffer: entity.PromiseBuffer, Version: entity.Version + 1, Currency: entity.Currency, - AccountName: entity.AccountName, + Name: entity.Name, + Format: entity.Format, IsBalanceCheck: entity.IsBalanceCheck, }) } diff --git a/services/vault-unit/persistence/journal_test.go b/services/vault-unit/persistence/journal_test.go index bd1679bd..5c35d92d 100644 --- a/services/vault-unit/persistence/journal_test.go +++ b/services/vault-unit/persistence/journal_test.go @@ -23,11 +23,12 @@ func TestSnapshot_Update(t *testing.T) { storage := localfs.NewStorage(tmpdir) - name := "account_2" + name := "account_name" + format := "account_format" currency := "XRP" isBalanceCheck := false - snapshotInitial := CreateAccount(&storage, name, currency, isBalanceCheck) + snapshotInitial := CreateAccount(&storage, name, format, currency, isBalanceCheck) require.NotNil(t, snapshotInitial) loadedInitial := LoadAccount(&storage, name) @@ -70,6 +71,7 @@ func TestSnapshot_RefuseOverflow(t *testing.T) { storage := localfs.NewStorage(tmpdir) name := "xxx" + format := "format" currency := "XxX" isBalanceCheck := true @@ -78,7 +80,8 @@ func TestSnapshot_RefuseOverflow(t *testing.T) { Promised: new(money.Dec), PromiseBuffer: model.NewTransactionSet(), Version: int(math.MaxInt32), - AccountName: name, + Name: name, + Format: format, Currency: currency, IsBalanceCheck: isBalanceCheck, } @@ -96,6 +99,7 @@ func TestSnapshot_PromiseBuffer(t *testing.T) { storage := localfs.NewStorage(tmpdir) name := "yyy" + format := "format" currency := "yYy" isBalanceCheck := false @@ -106,7 +110,8 @@ func TestSnapshot_PromiseBuffer(t *testing.T) { Promised: new(money.Dec), PromiseBuffer: model.NewTransactionSet(), Version: 0, - AccountName: name, + Name: name, + Format: format, Currency: currency, IsBalanceCheck: isBalanceCheck, } @@ -125,7 +130,8 @@ func TestSnapshot_PromiseBuffer(t *testing.T) { assert.Equal(t, snapshot.Balance, loaded.Balance) assert.Equal(t, snapshot.Promised, loaded.Promised) assert.Equal(t, snapshot.Version, loaded.Version) - assert.Equal(t, snapshot.AccountName, loaded.AccountName) + assert.Equal(t, snapshot.Name, loaded.Name) + assert.Equal(t, snapshot.Format, loaded.Format) assert.Equal(t, snapshot.Currency, loaded.Currency) assert.Equal(t, snapshot.IsBalanceCheck, loaded.IsBalanceCheck) @@ -142,7 +148,7 @@ func BenchmarkAccountLoad(b *testing.B) { storage := localfs.NewStorage(tmpdir) - account := CreateAccount(&storage, "bench", "BNC", false) + account := CreateAccount(&storage, "bench", "format", "BNC", false) require.NotNil(b, account) b.ReportAllocs() diff --git a/services/vault-unit/persistence/snapshot_updater_test.go b/services/vault-unit/persistence/snapshot_updater_test.go index af970392..38fb0b01 100644 --- a/services/vault-unit/persistence/snapshot_updater_test.go +++ b/services/vault-unit/persistence/snapshot_updater_test.go @@ -47,7 +47,7 @@ func TestSnapshotUpdater(t *testing.T) { metrics := metrics.NewMetrics(ctx, "", time.Hour) su := NewSnapshotUpdater(ctx, 1, time.Hour, &metrics, &storage, callback) - s := CreateAccount(&storage, "account_1", "EUR", true) + s := CreateAccount(&storage, "account_1", "format", "EUR", true) require.NotNil(t, s) require.Nil(t, PersistPromise(&storage, "account_1", 0, new(money.Dec), "transaction_1")) s = UpdateAccount(&storage, "account_1", s) @@ -55,7 +55,7 @@ func TestSnapshotUpdater(t *testing.T) { require.Nil(t, PersistCommit(&storage, "account_1", 1, new(money.Dec), "transaction_2")) require.NotNil(t, s) - require.NotNil(t, CreateAccount(&storage, "account_2", "EUR", true)) + require.NotNil(t, CreateAccount(&storage, "account_2", "format", "EUR", true)) t.Log("return valid accounts") {