From ac9e7e768e97ccff21e93abf06a4c21d6a453808 Mon Sep 17 00:00:00 2001 From: Neur0toxine Date: Tue, 20 Oct 2020 15:19:36 +0300 Subject: [PATCH] All retailCRM currencies support (#16) * all currencies supported in retailCRM" * function which allows to pick currency symbol without interacting with map * tweak for new function test * check for undefined currency code * increase timeout for failing test * refactored test to WaitGroup, which resulted in finding and fixing actual data race in the job object --- .travis.yml | 2 +- core/job_manager.go | 16 +++++++++ core/job_manager_test.go | 44 +++++++++++-------------- core/utils.go | 71 +++++++++++++++++++++++++++++++++++----- core/utils_test.go | 13 ++++++++ 5 files changed, 113 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c4f415..2a5133b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ go: before_install: - go mod tidy script: - - go test ./... -v -cpu 2 -timeout 2m -race -cover -coverprofile=coverage.txt -covermode=atomic + - go test ./... -v -cpu 2 -timeout 10s -race -cover -coverprofile=coverage.txt -covermode=atomic - go get -v -u github.com/axw/gocov/gocov - gocov convert ./coverage.txt | gocov report after_success: diff --git a/core/job_manager.go b/core/job_manager.go index a24540c..9965c29 100644 --- a/core/job_manager.go +++ b/core/job_manager.go @@ -27,6 +27,7 @@ type Job struct { ErrorHandler JobErrorHandler PanicHandler JobPanicHandler Interval time.Duration + writeLock sync.RWMutex Regular bool active bool stopChannel chan bool @@ -86,10 +87,18 @@ func (j *Job) getWrappedTimerFunc(name string, log JobLogFunc) func(chan bool) { // run job func (j *Job) run(name string, log JobLogFunc) *Job { + j.writeLock.RLock() + if j.Regular && j.Interval > 0 && !j.active { + j.writeLock.RUnlock() + defer j.writeLock.Unlock() + j.writeLock.Lock() + j.stopChannel = make(chan bool) go j.getWrappedTimerFunc(name, log)(j.stopChannel) j.active = true + } else { + j.writeLock.RUnlock() } return j @@ -97,11 +106,18 @@ func (j *Job) run(name string, log JobLogFunc) *Job { // stop running job func (j *Job) stop() *Job { + j.writeLock.RLock() + if j.active && j.stopChannel != nil { + j.writeLock.RUnlock() go func() { + defer j.writeLock.Unlock() + j.writeLock.Lock() j.stopChannel <- true j.active = false }() + } else { + j.writeLock.RUnlock() } return j diff --git a/core/job_manager_test.go b/core/job_manager_test.go index 92ff93b..6ae79bb 100644 --- a/core/job_manager_test.go +++ b/core/job_manager_test.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math/rand" + "sync" "testing" "time" @@ -28,7 +29,7 @@ type JobTest struct { type JobManagerTest struct { suite.Suite manager *JobManager - runnerFlag chan bool + runnerWG sync.WaitGroup syncRunnerFlag bool } @@ -308,15 +309,17 @@ func (t *JobManagerTest) SetupSuite() { t.manager = NewJobManager() } -func (t *JobManagerTest) ranFlag() bool { - if t.runnerFlag == nil { - return false - } +func (t *JobManagerTest) WaitForJob() bool { + c := make(chan bool) + go func() { + t.runnerWG.Wait() + c <- true + }() select { - case c := <-t.runnerFlag: - return c - case <-time.After(time.Millisecond): + case <-c: + return true + case <-time.After(time.Second): return false } } @@ -349,7 +352,7 @@ func (t *JobManagerTest) Test_RegisterJob() { require.NotNil(t.T(), t.manager.jobs) err := t.manager.RegisterJob("job", &Job{ Command: func(log JobLogFunc) error { - t.runnerFlag <- true + t.runnerWG.Done() return nil }, ErrorHandler: DefaultJobErrorHandler(), @@ -358,7 +361,7 @@ func (t *JobManagerTest) Test_RegisterJob() { assert.NoError(t.T(), err) err = t.manager.RegisterJob("job_regular", &Job{ Command: func(log JobLogFunc) error { - t.runnerFlag <- true + t.runnerWG.Done() return nil }, ErrorHandler: DefaultJobErrorHandler(), @@ -431,15 +434,17 @@ func (t *JobManagerTest) Test_RunJobDoesntExist() { assert.EqualError(t.T(), err, "cannot find job `doesn't exist`") } -func (t *JobManagerTest) Test_RunJob() { +func (t *JobManagerTest) Test_RunJob_RunJobOnce() { require.NotNil(t.T(), t.manager.jobs) - t.runnerFlag = make(chan bool) - err := t.manager.RunJob("job_regular") + err := t.manager.StopJob("job_regular") require.NoError(t.T(), err) - time.Sleep(time.Millisecond * 5) - assert.True(t.T(), <-t.runnerFlag) + t.runnerWG.Add(1) + err = t.manager.RunJobOnce("job_regular") + require.NoError(t.T(), err) + time.Sleep(time.Millisecond) err = t.manager.StopJob("job_regular") require.NoError(t.T(), err) + assert.True(t.T(), t.WaitForJob(), "Job was not executed in time") } func (t *JobManagerTest) Test_RunJobOnceDoesntExist() { @@ -448,15 +453,6 @@ func (t *JobManagerTest) Test_RunJobOnceDoesntExist() { assert.EqualError(t.T(), err, "cannot find job `doesn't exist`") } -func (t *JobManagerTest) Test_RunJobOnce() { - require.NotNil(t.T(), t.manager.jobs) - go func() { t.runnerFlag <- false }() - err := t.manager.RunJobOnce("job") - time.Sleep(300 * time.Millisecond) - require.NoError(t.T(), err) - assert.True(t.T(), t.ranFlag()) -} - func (t *JobManagerTest) Test_RunJobOnceSyncDoesntExist() { require.NotNil(t.T(), t.manager.jobs) err := t.manager.RunJobOnceSync("doesn't exist") diff --git a/core/utils.go b/core/utils.go index a963f1e..4251217 100644 --- a/core/utils.go +++ b/core/utils.go @@ -21,6 +21,58 @@ import ( v1 "github.com/retailcrm/mg-transport-api-client-go/v1" ) +var defaultCurrencies = map[string]string{ + "rub": "₽", + "uah": "₴", + "byn": "Br", + "kzt": "₸", + "usd": "$", + "eur": "€", + "prb": "PRB", + "mdl": "L", + "kgs": "с", + "pln": "zł", + "azn": "₼", + "amd": "֏", + "thb": "฿", + "aed": "AED", + "nok": "kr", + "cad": "C$", + "czk": "Kč", + "sek": "kr", + "dkk": "kr", + "ron": "lei", + "uzs": "So'm", + "aud": "$", + "chf": "₣", + "inr": "₹", + "bgn": "лв", + "ngn": "₦", + "huf": "ƒ", + "ils": "₪", + "try": "₺", + "stn": "₡", + "ars": "$", + "bob": "Bs", + "ves": "Bs", + "gtq": "Q", + "hnl": "L", + "dop": "RD$", + "cop": "COL$", + "crc": "₡", + "cup": "$MN", + "mxn": "NP$", + "nio": "C$", + "pab": "B/", + "pyg": "₲", + "pen": "S/", + "svc": "₡", + "uyu": "$U", + "clp": "Ch$", + "gel": "₾", + "gbp": "£", +} + // Utils service object type Utils struct { IsDebug bool @@ -201,12 +253,15 @@ func ReplaceMarkdownSymbols(s string) string { // DefaultCurrencies will return default currencies list for all bots func DefaultCurrencies() map[string]string { - return map[string]string{ - "rub": "₽", - "uah": "₴", - "byr": "Br", - "kzt": "₸", - "usd": "$", - "eur": "€", - } + return defaultCurrencies +} + +// GetCurrencySymbol returns currency symbol by it's ISO 4127 code. +// It returns provided currency code in uppercase if currency symbol cannot be found +func GetCurrencySymbol(code string) string { + if i, ok := DefaultCurrencies()[strings.ToLower(code)]; ok { + return i + } + + return strings.ToUpper(code) } diff --git a/core/utils_test.go b/core/utils_test.go index f9fe39b..eb7155f 100644 --- a/core/utils_test.go +++ b/core/utils_test.go @@ -3,6 +3,7 @@ package core import ( "encoding/json" "net/http" + "strings" "testing" "time" @@ -241,6 +242,18 @@ func TestUtils_GetEntitySHA1(t *testing.T) { assert.Equal(t, "751b56fb98c9fd803140e8287b4236675554a668", hash) } +func TestUtils_GetCurrencySymbol(t *testing.T) { + for code, _ := range DefaultCurrencies() { + if strings.ToUpper(code) == defaultCurrencies[code] { + continue + } + + assert.NotEqual(t, strings.ToUpper(code), GetCurrencySymbol(code)) + } + + assert.Equal(t, "XAG", GetCurrencySymbol("xag")) +} + func TestUtils_ReplaceMarkdownSymbols(t *testing.T) { test := "this *is* _test_ `string` [markdown" expected := "this \\*is\\* \\_test\\_ \\`string\\` \\[markdown"