feat: add accounts MCP methods
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
decimal "github.com/alpacahq/alpacadecimal"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"s1d3sw1ped/robinhood-agentic-mcp/accounts"
|
||||
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||
"s1d3sw1ped/robinhood-agentic-mcp/internal/rhntest"
|
||||
)
|
||||
|
||||
func TestAccounts_toolNames(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
call func(*accounts.Client) error
|
||||
wantName string
|
||||
wantArgs map[string]any
|
||||
}{
|
||||
{
|
||||
name: "Accounts",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.Accounts(context.Background(), accounts.AccountsRequest{})
|
||||
return err
|
||||
},
|
||||
wantName: "get_accounts",
|
||||
wantArgs: map[string]any{},
|
||||
},
|
||||
{
|
||||
name: "Portfolio",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.Portfolio(context.Background(), accounts.PortfolioRequest{AccountNumber: "acct-1"})
|
||||
return err
|
||||
},
|
||||
wantName: "get_portfolio",
|
||||
wantArgs: map[string]any{"account_number": "acct-1"},
|
||||
},
|
||||
{
|
||||
name: "RealizedPnL",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.RealizedPnL(context.Background(), accounts.RealizedPnLRequest{
|
||||
AccountNumber: "acct-1",
|
||||
Span: "month",
|
||||
StartDate: "2026-01-01",
|
||||
EndDate: "2026-01-31",
|
||||
AssetClasses: []string{"equity"},
|
||||
DisplayCurrency: "USD",
|
||||
Timezone: "America/New_York",
|
||||
})
|
||||
return err
|
||||
},
|
||||
wantName: "get_realized_pnl",
|
||||
wantArgs: map[string]any{
|
||||
"account_number": "acct-1",
|
||||
"span": "month",
|
||||
"start_date": "2026-01-01",
|
||||
"end_date": "2026-01-31",
|
||||
"asset_classes": []string{"equity"},
|
||||
"display_currency": "USD",
|
||||
"timezone": "America/New_York",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PnLTradeHistory",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.PnLTradeHistory(context.Background(), accounts.PnLTradeHistoryRequest{
|
||||
AccountNumber: "acct-1",
|
||||
Span: "week",
|
||||
Symbol: "MU",
|
||||
Cursor: "c1",
|
||||
})
|
||||
return err
|
||||
},
|
||||
wantName: "get_pnl_trade_history",
|
||||
wantArgs: map[string]any{
|
||||
"account_number": "acct-1",
|
||||
"span": "week",
|
||||
"symbol": "MU",
|
||||
"cursor": "c1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LimitedMarginUpgradeInfo",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.LimitedMarginUpgradeInfo(context.Background(), accounts.AccountNumberRequest{AccountNumber: "acct-1"})
|
||||
return err
|
||||
},
|
||||
wantName: "get_limited_margin_upgrade_info",
|
||||
wantArgs: map[string]any{"account_number": "acct-1"},
|
||||
},
|
||||
{
|
||||
name: "OptionLevelUpgradeInfo",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.OptionLevelUpgradeInfo(context.Background(), accounts.AccountNumberRequest{AccountNumber: "acct-1"})
|
||||
return err
|
||||
},
|
||||
wantName: "get_option_level_upgrade_info",
|
||||
wantArgs: map[string]any{"account_number": "acct-1"},
|
||||
},
|
||||
{
|
||||
name: "CryptoOnboardingInfo",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.CryptoOnboardingInfo(context.Background(), struct{}{})
|
||||
return err
|
||||
},
|
||||
wantName: "get_crypto_account_onboarding_info",
|
||||
wantArgs: map[string]any{},
|
||||
},
|
||||
{
|
||||
name: "Search",
|
||||
call: func(c *accounts.Client) error {
|
||||
_, err := c.Search(context.Background(), accounts.SearchRequest{
|
||||
Query: "apple",
|
||||
AssetType: "instrument",
|
||||
Limit: 5,
|
||||
})
|
||||
return err
|
||||
},
|
||||
wantName: "search",
|
||||
wantArgs: map[string]any{
|
||||
"query": "apple",
|
||||
"asset_type": "instrument",
|
||||
"limit": 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var gotName string
|
||||
var gotArgs map[string]any
|
||||
c := accounts.New(client.Func(func(ctx context.Context, name string, args map[string]any) (json.RawMessage, error) {
|
||||
gotName, gotArgs = name, args
|
||||
return json.RawMessage(`{}`), nil
|
||||
}))
|
||||
if err := tc.call(c); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if gotName != tc.wantName {
|
||||
t.Fatalf("%s %+v", gotName, gotArgs)
|
||||
}
|
||||
if diff := cmp.Diff(tc.wantArgs, gotArgs); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTools(t *testing.T) {
|
||||
t.Parallel()
|
||||
want := []string{
|
||||
"get_accounts",
|
||||
"get_crypto_account_onboarding_info",
|
||||
"get_limited_margin_upgrade_info",
|
||||
"get_option_level_upgrade_info",
|
||||
"get_pnl_trade_history",
|
||||
"get_portfolio",
|
||||
"get_realized_pnl",
|
||||
"search",
|
||||
}
|
||||
got := append([]string(nil), accounts.Tools()...)
|
||||
sort.Strings(got)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccounts_rhntest(t *testing.T) {
|
||||
t.Parallel()
|
||||
raw, err := os.ReadFile("testdata/accounts.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := rhntest.New(t)
|
||||
s.Set("get_accounts", raw)
|
||||
c := accounts.New(&client.Client{URL: s.URL})
|
||||
got, err := c.Accounts(context.Background(), accounts.AccountsRequest{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got.Accounts) != 1 {
|
||||
t.Fatalf("%+v", got)
|
||||
}
|
||||
a := got.Accounts[0]
|
||||
if a.ID != "acct-1" || a.Type != "cash" || !a.AgenticAllowed || !a.Cash {
|
||||
t.Fatalf("%+v", a)
|
||||
}
|
||||
if a.BuyingPower == nil || !a.BuyingPower.Equal(decimal.RequireFromString("1000")) {
|
||||
t.Fatalf("buying power %v", a.BuyingPower)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPortfolio_rhntest(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := rhntest.New(t)
|
||||
s.Set("get_portfolio", json.RawMessage(`{"data":{"buying_power":{"buying_power":"1000.0"}}}`))
|
||||
c := accounts.New(&client.Client{URL: s.URL})
|
||||
got, err := c.Portfolio(context.Background(), accounts.PortfolioRequest{AccountNumber: "acct-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !got.BuyingPower.Equal(decimal.RequireFromString("1000.0")) {
|
||||
t.Fatalf("buying power %s", got.BuyingPower)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user