435 lines
9.8 KiB
Go
435 lines
9.8 KiB
Go
package internal_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nmoniz/any2anexoj/internal"
|
|
"github.com/shopspring/decimal"
|
|
)
|
|
|
|
type testRecord struct {
|
|
symbol string
|
|
nature internal.Nature
|
|
brokerCountry int64
|
|
assetCountry int64
|
|
side internal.Side
|
|
price decimal.Decimal
|
|
quantity decimal.Decimal
|
|
timestamp time.Time
|
|
fees decimal.Decimal
|
|
taxes decimal.Decimal
|
|
}
|
|
|
|
func (m testRecord) Symbol() string { return m.symbol }
|
|
func (m testRecord) Nature() internal.Nature { return m.nature }
|
|
func (m testRecord) BrokerCountry() int64 { return m.brokerCountry }
|
|
func (m testRecord) AssetCountry() int64 { return m.assetCountry }
|
|
func (m testRecord) Side() internal.Side { return m.side }
|
|
func (m testRecord) Price() decimal.Decimal { return m.price }
|
|
func (m testRecord) Quantity() decimal.Decimal { return m.quantity }
|
|
func (m testRecord) Timestamp() time.Time { return m.timestamp }
|
|
func (m testRecord) Fees() decimal.Decimal { return m.fees }
|
|
func (m testRecord) Taxes() decimal.Decimal { return m.taxes }
|
|
|
|
func TestAny(t *testing.T) {
|
|
selector := internal.Any()
|
|
|
|
tests := []struct {
|
|
name string
|
|
record internal.Record
|
|
want bool
|
|
}{
|
|
{
|
|
name: "returns true for any record",
|
|
record: testRecord{
|
|
symbol: "AAPL",
|
|
nature: internal.NatureG01,
|
|
assetCountry: 1,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "returns true for record with unknown nature",
|
|
record: testRecord{
|
|
symbol: "MSFT",
|
|
nature: internal.NatureUnknown,
|
|
assetCountry: 2,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "returns true for empty record",
|
|
record: testRecord{},
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := selector(tt.record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnlyNature(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nature internal.Nature
|
|
record internal.Record
|
|
want bool
|
|
}{
|
|
{
|
|
name: "matches G01 nature",
|
|
nature: internal.NatureG01,
|
|
record: testRecord{
|
|
symbol: "AAPL",
|
|
nature: internal.NatureG01,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "matches G20 nature",
|
|
nature: internal.NatureG20,
|
|
record: testRecord{
|
|
symbol: "ETF",
|
|
nature: internal.NatureG20,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "does not match different nature",
|
|
nature: internal.NatureG01,
|
|
record: testRecord{
|
|
symbol: "AAPL",
|
|
nature: internal.NatureG20,
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "does not match unknown nature",
|
|
nature: internal.NatureG01,
|
|
record: testRecord{
|
|
symbol: "AAPL",
|
|
nature: internal.NatureUnknown,
|
|
},
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
selector := internal.OnlyNature(tt.nature)
|
|
got := selector(tt.record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOnlyAssetCountry(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
country int64
|
|
record internal.Record
|
|
want bool
|
|
}{
|
|
{
|
|
name: "matches asset country",
|
|
country: 840, // USA
|
|
record: testRecord{
|
|
symbol: "AAPL",
|
|
assetCountry: 840,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "matches different country",
|
|
country: 826, // UK
|
|
record: testRecord{
|
|
symbol: "BP",
|
|
assetCountry: 826,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "does not match different asset country",
|
|
country: 840,
|
|
record: testRecord{
|
|
symbol: "BP",
|
|
assetCountry: 826,
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "does not match zero country",
|
|
country: 0,
|
|
record: testRecord{
|
|
symbol: "AAPL",
|
|
assetCountry: 840,
|
|
},
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
selector := internal.OnlyAssetCountry(tt.country)
|
|
got := selector(tt.record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAnd(t *testing.T) {
|
|
record := testRecord{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
selectorA internal.Selector
|
|
selectorB internal.Selector
|
|
want bool
|
|
}{
|
|
{
|
|
name: "both selectors return true",
|
|
selectorA: func(internal.Record) bool { return true },
|
|
selectorB: func(internal.Record) bool { return true },
|
|
want: true,
|
|
},
|
|
{
|
|
name: "first selector returns true, second returns false",
|
|
selectorA: func(internal.Record) bool { return true },
|
|
selectorB: func(internal.Record) bool { return false },
|
|
want: false,
|
|
},
|
|
{
|
|
name: "first selector returns false, second returns true",
|
|
selectorA: func(internal.Record) bool { return false },
|
|
selectorB: func(internal.Record) bool { return true },
|
|
want: false,
|
|
},
|
|
{
|
|
name: "both selectors return false",
|
|
selectorA: func(internal.Record) bool { return false },
|
|
selectorB: func(internal.Record) bool { return false },
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
selector := internal.And(tt.selectorA, tt.selectorB)
|
|
got := selector(record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSelectors_Empty(t *testing.T) {
|
|
selector, err := internal.ParseSelectors([]string{})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error for empty selectors: %v", err)
|
|
}
|
|
record := testRecord{
|
|
nature: internal.NatureG01,
|
|
assetCountry: 840,
|
|
}
|
|
|
|
if !selector(record) {
|
|
t.Fatalf("empty selectors should match all records")
|
|
}
|
|
}
|
|
|
|
func TestParseSelectors_OnlyNature(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
selector string
|
|
nature internal.Nature
|
|
want bool
|
|
}{
|
|
{
|
|
name: "matches G01",
|
|
selector: "code:G01",
|
|
nature: internal.NatureG01,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "matches G20",
|
|
selector: "code:G20",
|
|
nature: internal.NatureG20,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "does not match different nature",
|
|
selector: "code:G01",
|
|
nature: internal.NatureG20,
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
selector, err := internal.ParseSelectors([]string{tt.selector})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
record := testRecord{
|
|
nature: tt.nature,
|
|
assetCountry: 840,
|
|
}
|
|
|
|
got := selector(record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSelectors_OnlyAssetCountry(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
selector string
|
|
assetCountry int64
|
|
want bool
|
|
}{
|
|
{
|
|
name: "matches USA",
|
|
selector: "assetCountry:840",
|
|
assetCountry: 840,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "matches UK",
|
|
selector: "assetCountry:826",
|
|
assetCountry: 826,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "does not match different country",
|
|
selector: "assetCountry:840",
|
|
assetCountry: 826,
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
selector, err := internal.ParseSelectors([]string{tt.selector})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
record := testRecord{
|
|
nature: internal.NatureG01,
|
|
assetCountry: tt.assetCountry,
|
|
}
|
|
|
|
got := selector(record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSelectors_Multiple(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
selectors []string
|
|
nature internal.Nature
|
|
assetCountry int64
|
|
want bool
|
|
}{
|
|
{
|
|
name: "both selectors match",
|
|
selectors: []string{"code:G01", "assetCountry:840"},
|
|
nature: internal.NatureG01,
|
|
assetCountry: 840,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "first selector matches, second does not",
|
|
selectors: []string{"code:G01", "assetCountry:826"},
|
|
nature: internal.NatureG01,
|
|
assetCountry: 840,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "first selector does not match, second does",
|
|
selectors: []string{"code:G20", "assetCountry:840"},
|
|
nature: internal.NatureG01,
|
|
assetCountry: 840,
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
selector, err := internal.ParseSelectors(tt.selectors)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
record := testRecord{
|
|
nature: tt.nature,
|
|
assetCountry: tt.assetCountry,
|
|
}
|
|
|
|
got := selector(record)
|
|
if got != tt.want {
|
|
t.Fatalf("want %v but got %v", tt.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSelectors_InvalidAssetCountry(t *testing.T) {
|
|
t.Run("returns error for non-numeric asset country", func(t *testing.T) {
|
|
_, err := internal.ParseSelectors([]string{"assetCountry:notanumber"})
|
|
if err == nil {
|
|
t.Fatalf("expected error for non-numeric asset country")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestParseSelectors_UnknownSelector(t *testing.T) {
|
|
t.Run("returns error for unknown selector type", func(t *testing.T) {
|
|
_, err := internal.ParseSelectors([]string{"unknown:value"})
|
|
if err == nil {
|
|
t.Fatalf("expected error for unknown selector type")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestParseSelectors_EmptyValue(t *testing.T) {
|
|
t.Run("rejects code with empty value", func(t *testing.T) {
|
|
_, err := internal.ParseSelectors([]string{"code:"})
|
|
if err == nil {
|
|
t.Fatalf("expected error for empty code value")
|
|
}
|
|
})
|
|
|
|
t.Run("rejects assetCountry with empty value", func(t *testing.T) {
|
|
_, err := internal.ParseSelectors([]string{"assetCountry:"})
|
|
if err == nil {
|
|
t.Fatalf("expected error for empty assetCountry value")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestParseSelectors_MissingColon(t *testing.T) {
|
|
t.Run("rejects input without colon", func(t *testing.T) {
|
|
_, err := internal.ParseSelectors([]string{"code"})
|
|
if err == nil {
|
|
t.Fatalf("expected error for input without colon")
|
|
}
|
|
})
|
|
}
|