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
This commit is contained in:
Pavel 2020-10-20 15:19:36 +03:00 committed by GitHub
parent 1de2e5626f
commit ac9e7e768e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 33 deletions

View File

@ -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:

View File

@ -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

View File

@ -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")

View File

@ -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)
}

View File

@ -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"