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.Kind 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) Kind() internal.Kind { 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") } }) }