From 858eea32b53258cdf99a582e00e83771f94f840c Mon Sep 17 00:00:00 2001 From: Ani Channarasappa Date: Sun, 19 Dec 2021 13:43:41 -0500 Subject: [PATCH] fix: currency conversion for groups --- go.mod | 6 +-- go.sum | 13 +++++ internal/asset/asset.go | 26 ---------- internal/asset/asset_test.go | 37 -------------- internal/cli/cli.go | 11 ++--- internal/cli/cli_test.go | 96 +++++++++++++++++++++++++++++++++--- internal/quote/quote.go | 57 +++++++++++++++++++++ internal/quote/quote_test.go | 85 +++++++++++++++++++++++++++++++ 8 files changed, 250 insertions(+), 81 deletions(-) diff --git a/go.mod b/go.mod index 75d79aa..ea41c09 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.9.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.10.5 + github.com/onsi/gomega v1.17.0 github.com/spf13/afero v1.5.1 github.com/spf13/cobra v1.1.1 github.com/spf13/viper v1.7.0 @@ -42,11 +42,11 @@ require ( github.com/subosito/gotenv v1.2.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect - google.golang.org/protobuf v1.25.0 // indirect + google.golang.org/protobuf v1.26.0 // indirect gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) diff --git a/go.sum b/go.sum index 25adfc0..3b04b13 100644 --- a/go.sum +++ b/go.sum @@ -97,6 +97,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -104,6 +107,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -210,12 +214,15 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -340,8 +347,11 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -457,6 +467,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= diff --git a/internal/asset/asset.go b/internal/asset/asset.go index 213b933..aff7361 100644 --- a/internal/asset/asset.go +++ b/internal/asset/asset.go @@ -131,32 +131,6 @@ func getHoldingFromAssetQuote(assetQuote c.AssetQuote, lotsBySymbol map[string]A } -// GetSymbols retrieves a unique slice of symbols from the watchlist and lots sections of the config -func GetSymbols(config c.Config) []string { - - symbols := make(map[string]bool) - symbolsUnique := make([]string, 0) - - for _, symbol := range config.Watchlist { - if !symbols[symbol] { - symbols[symbol] = true - symbolsUnique = append(symbolsUnique, symbol) - } - } - - if config.ShowHoldings { - for _, lot := range config.Lots { - if !symbols[lot.Symbol] { - symbols[lot.Symbol] = true - symbolsUnique = append(symbolsUnique, lot.Symbol) - } - } - } - - return symbolsUnique - -} - func getLots(lots []c.Lot) map[string]AggregatedLot { if lots == nil { diff --git a/internal/asset/asset_test.go b/internal/asset/asset_test.go index 18e8cb1..c4dec6c 100644 --- a/internal/asset/asset_test.go +++ b/internal/asset/asset_test.go @@ -192,41 +192,4 @@ var _ = Describe("Asset", func() { }) }) - - Describe("GetSymbols", func() { - - It("should return a slice of symbols", func() { - - inputConfig := c.Config{ - Watchlist: []string{"GOOG", "ARKW"}, - Lots: []c.Lot{ - {Symbol: "ABNB", UnitCost: 100, Quantity: 10}, - {Symbol: "ARKW", UnitCost: 200, Quantity: 10}, - }, - ShowHoldings: true, - } - output := GetSymbols(inputConfig) - expected := []string{ - "ABNB", - "ARKW", - "GOOG", - } - Expect(output).To(ContainElements(expected)) - }) - - When("holdings are hidden", func() { - It("should not show symbols for holdings", func() { - inputConfig := c.Config{ - Watchlist: []string{"GOOG", "ARKW"}, - ShowHoldings: false, - } - output := GetSymbols(inputConfig) - expected := []string{ - "ARKW", - "GOOG", - } - Expect(output).To(ContainElements(expected)) - }) - }) - }) }) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 8fe2b33..dc4ee07 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -5,9 +5,8 @@ import ( "fmt" "strings" - "github.com/achannarasappa/ticker/internal/asset" c "github.com/achannarasappa/ticker/internal/common" - quoteYahoo "github.com/achannarasappa/ticker/internal/quote/yahoo" + "github.com/achannarasappa/ticker/internal/quote" "github.com/achannarasappa/ticker/internal/ui/util" "github.com/adrg/xdg" @@ -74,8 +73,8 @@ func GetContext(d c.Dependencies, options Options, configPath string) (c.Context } config = getConfig(config, options, *d.HttpClient) - reference, err = getReference(config, *d.HttpClient) groups := getGroups(config) + reference, err = getReference(config, groups, *d.HttpClient) if err != nil { return c.Context{}, err @@ -113,11 +112,9 @@ func readConfig(fs afero.Fs, configPathOption string) (c.Config, error) { return config, nil } -func getReference(config c.Config, client resty.Client) (c.Reference, error) { +func getReference(config c.Config, assetGroups []c.AssetGroup, client resty.Client) (c.Reference, error) { - symbols := asset.GetSymbols(config) - - currencyRates, err := quoteYahoo.GetCurrencyRates(client, symbols, config.Currency) + currencyRates, err := quote.GetAssetGroupsCurrencyRates(client, assetGroups, config.Currency) styles := util.GetColorScheme(config.ColorScheme) return c.Reference{ diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index eeeb8be..f36aee3 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -4,6 +4,7 @@ import ( "errors" "io/ioutil" "os" + "strings" "github.com/mitchellh/go-homedir" . "github.com/onsi/ginkgo" @@ -131,8 +132,13 @@ var _ = Describe("Cli", func() { InputConfigFileContents: "watchlist:\n - GME\n - BB", AssertionErr: BeNil(), AssertionCtx: g.MatchFields(g.IgnoreExtras, g.Fields{ - "Config": g.MatchFields(g.IgnoreExtras, g.Fields{ - "Watchlist": Equal([]string{"GME", "BB"}), + "Groups": g.MatchAllElementsWithIndex(g.IndexIdentity, g.Elements{ + "0": g.MatchFields(g.IgnoreExtras, g.Fields{ + "ConfigAssetGroup": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Name": Equal("default"), + "Watchlist": Equal([]string{"GME", "BB"}), + }), + }), }), }), }), @@ -142,8 +148,13 @@ var _ = Describe("Cli", func() { InputConfigFileContents: "", AssertionErr: BeNil(), AssertionCtx: g.MatchFields(g.IgnoreExtras, g.Fields{ - "Config": g.MatchFields(g.IgnoreExtras, g.Fields{ - "Watchlist": Equal([]string{"BIO", "BB"}), + "Groups": g.MatchAllElementsWithIndex(g.IndexIdentity, g.Elements{ + "0": g.MatchFields(g.IgnoreExtras, g.Fields{ + "ConfigAssetGroup": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Name": Equal("default"), + "Watchlist": Equal([]string{"BIO", "BB"}), + }), + }), }), }), }), @@ -153,8 +164,77 @@ var _ = Describe("Cli", func() { InputConfigFileContents: "watchlist:\n - GME", AssertionErr: BeNil(), AssertionCtx: g.MatchFields(g.IgnoreExtras, g.Fields{ - "Config": g.MatchFields(g.IgnoreExtras, g.Fields{ - "Watchlist": Equal([]string{"BB"}), + "Groups": g.MatchAllElementsWithIndex(g.IndexIdentity, g.Elements{ + "0": g.MatchFields(g.IgnoreExtras, g.Fields{ + "ConfigAssetGroup": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Name": Equal("default"), + "Watchlist": Equal([]string{"BB"}), + }), + }), + }), + }), + }), + + // groups + Entry("when groups are defined", Case{ + InputOptions: cli.Options{}, + InputConfigFileContents: strings.Join([]string{ + "groups:", + " - name: crypto", + " watchlist:", + " - SHIB-USD", + " - BTC-USD", + " holdings:", + " - symbol: SOL1-USD", + " quantity: 17", + " unit_cost: 159.10", + }, "\n"), + AssertionErr: BeNil(), + AssertionCtx: g.MatchFields(g.IgnoreExtras, g.Fields{ + "Groups": g.MatchAllElementsWithIndex(g.IndexIdentity, g.Elements{ + "0": g.MatchFields(g.IgnoreExtras, g.Fields{ + "ConfigAssetGroup": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Name": Equal("crypto"), + "Watchlist": Equal([]string{"SHIB-USD", "BTC-USD"}), + "Holdings": g.MatchAllElementsWithIndex(g.IndexIdentity, g.Elements{ + "0": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Symbol": Equal("SOL1-USD"), + "Quantity": Equal(17.0), + "UnitCost": Equal(159.10), + }), + }), + }), + }), + }), + }), + }), + + Entry("when groups and watchlist are defined", Case{ + InputOptions: cli.Options{}, + InputConfigFileContents: strings.Join([]string{ + "watchlist:", + " - TSLA", + "groups:", + " - name: crypto", + " watchlist:", + " - SOL1-USD", + " - BTC-USD", + }, "\n"), + AssertionErr: BeNil(), + AssertionCtx: g.MatchFields(g.IgnoreExtras, g.Fields{ + "Groups": g.MatchAllElementsWithIndex(g.IndexIdentity, g.Elements{ + "0": g.MatchFields(g.IgnoreExtras, g.Fields{ + "ConfigAssetGroup": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Name": Equal("default"), + "Watchlist": Equal([]string{"TSLA"}), + }), + }), + "1": g.MatchFields(g.IgnoreExtras, g.Fields{ + "ConfigAssetGroup": g.MatchFields(g.IgnoreExtras, g.Fields{ + "Name": Equal("crypto"), + "Watchlist": Equal([]string{"SOL1-USD", "BTC-USD"}), + }), + }), }), }), }), @@ -251,12 +331,12 @@ var _ = Describe("Cli", func() { }), Entry("when show-separator is set in options", Case{ - InputOptions: cli.Options{Separate: false}, + InputOptions: cli.Options{Separate: true}, InputConfigFileContents: "", AssertionErr: BeNil(), AssertionCtx: g.MatchFields(g.IgnoreExtras, g.Fields{ "Config": g.MatchFields(g.IgnoreExtras, g.Fields{ - "Separate": Equal(false), + "Separate": Equal(true), }), }), }), diff --git a/internal/quote/quote.go b/internal/quote/quote.go index 72096aa..a1affc1 100644 --- a/internal/quote/quote.go +++ b/internal/quote/quote.go @@ -3,6 +3,7 @@ package quote import ( c "github.com/achannarasappa/ticker/internal/common" quoteYahoo "github.com/achannarasappa/ticker/internal/quote/yahoo" + "github.com/go-resty/resty/v2" ) func getQuoteBySource(dep c.Dependencies, symbolBySource c.AssetGroupSymbolsBySource) []c.AssetQuote { @@ -34,3 +35,59 @@ func GetAssetGroupQuote(dep c.Dependencies) func(c.AssetGroup) c.AssetGroupQuote } } } + +func getUniqueSymbolsBySource(assetGroups []c.AssetGroup) []c.AssetGroupSymbolsBySource { + + symbols := make(map[c.QuoteSource]map[string]bool) + symbolsUnique := make(map[c.QuoteSource][]string) + var assetGroupSymbolsBySource []c.AssetGroupSymbolsBySource + for _, assetGroup := range assetGroups { + + for _, symbolGroup := range assetGroup.SymbolsBySource { + + for _, symbol := range symbolGroup.Symbols { + + source := symbolGroup.Source + + if symbols[source] == nil { + symbols[source] = map[string]bool{} + } + + if !symbols[source][symbol] { + symbols[source][symbol] = true + symbolsUnique[source] = append(symbolsUnique[source], symbol) + } + } + + } + + } + + for source, symbols := range symbolsUnique { + assetGroupSymbolsBySource = append(assetGroupSymbolsBySource, c.AssetGroupSymbolsBySource{ + Source: source, + Symbols: symbols, + }) + } + + return assetGroupSymbolsBySource + +} + +// GetAssetGroupsCurrencyRates gets the currency rates by source across all asset groups +func GetAssetGroupsCurrencyRates(client resty.Client, assetGroups []c.AssetGroup, targetCurrency string) (c.CurrencyRates, error) { + + var err error + var currencyRates c.CurrencyRates + uniqueSymbolsBySource := getUniqueSymbolsBySource(assetGroups) + + for _, source := range uniqueSymbolsBySource { + + if source.Source == c.QuoteSourceYahoo && err == nil { + currencyRates, err = quoteYahoo.GetCurrencyRates(client, source.Symbols, targetCurrency) + } + + } + + return currencyRates, err +} diff --git a/internal/quote/quote_test.go b/internal/quote/quote_test.go index ae377fb..1ba0aa4 100644 --- a/internal/quote/quote_test.go +++ b/internal/quote/quote_test.go @@ -1,13 +1,67 @@ package quote_test import ( + "net/http" + c "github.com/achannarasappa/ticker/internal/common" . "github.com/achannarasappa/ticker/internal/quote" . "github.com/achannarasappa/ticker/test/http" + "github.com/jarcoal/httpmock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + g "github.com/onsi/gomega/gstruct" ) +func mockResponseCurrencyGOOG() { + response := `{ + "quoteResponse": { + "result": [ + { + "currency": "USD", + "symbol": "GOOG" + } + ], + "error": null + } + } + ` + responseURL := `https://query1.finance.yahoo.com/v7/finance/quote?lang=en-US®ion=US&corsDomain=finance.yahoo.com&fields=regularMarketPrice,currency&symbols=GOOG` + httpmock.RegisterResponder("GET", responseURL, func(req *http.Request) (*http.Response, error) { + resp := httpmock.NewStringResponse(200, response) + resp.Header.Set("Content-Type", "application/json") + return resp, nil + }) +} + +func mockResponseCurrencyEURUSD() { + response := `{ + "quoteResponse": { + "result": [ + { + "quoteType": "CURRENCY", + "quoteSourceName": "Delayed Quote", + "currency": "EUR", + "regularMarketPrice": 0.8891, + "sourceInterval": 15, + "exchangeDataDelayedBy": 0, + "exchange": "CCY", + "fullExchangeName": "CCY", + "symbol": "USDEUR=X" + } + ], + "error": null + } + } + + ` + responseURL := `https://query1.finance.yahoo.com/v7/finance/quote?lang=en-US®ion=US&corsDomain=finance.yahoo.com&fields=regularMarketPrice,currency&symbols=USDEUR=X` + httpmock.RegisterResponder("GET", responseURL, func(req *http.Request) (*http.Response, error) { + resp := httpmock.NewStringResponse(200, response) + resp.Header.Set("Content-Type", "application/json") + return resp, nil + }) +} + var _ = Describe("Quote", func() { var ( @@ -51,4 +105,35 @@ var _ = Describe("Quote", func() { }) + Describe("GetAssetGroupsCurrencyRates", func() { + + It("should get currency conversion rates for each type of data source", func() { + + mockResponseCurrencyEURUSD() + mockResponseCurrencyGOOG() + input := []c.AssetGroup{ + { + SymbolsBySource: []c.AssetGroupSymbolsBySource{ + { + Source: c.QuoteSourceYahoo, + Symbols: []string{ + "GOOG", + }, + }, + }, + }, + } + output, _ := GetAssetGroupsCurrencyRates(*client, input, "EUR") + Expect(output).To(g.MatchAllKeys(g.Keys{ + "USD": g.MatchFields(g.IgnoreExtras, g.Fields{ + "FromCurrency": Equal("USD"), + "ToCurrency": Equal("EUR"), + "Rate": Equal(0.8891), + }), + })) + + }) + + }) + })