feat: add scanner MCP methods
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
package scanner
|
||||||
|
|
||||||
|
import "s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
|
||||||
|
// Client wraps Robinhood scanner MCP tools.
|
||||||
|
type Client struct {
|
||||||
|
c client.Caller
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a scanner client that invokes tools through c.
|
||||||
|
func New(c client.Caller) *Client {
|
||||||
|
return &Client{c: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tools returns the MCP names this package implements.
|
||||||
|
func Tools() []string {
|
||||||
|
return []string{
|
||||||
|
toolFilterSpecs,
|
||||||
|
toolDatapoints,
|
||||||
|
toolScans,
|
||||||
|
toolCreate,
|
||||||
|
toolPreview,
|
||||||
|
toolRun,
|
||||||
|
toolUpdateFilters,
|
||||||
|
toolUpdateConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
package scanner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
toolFilterSpecs = "get_scanner_filter_specs"
|
||||||
|
toolDatapoints = "get_scanner_datapoints"
|
||||||
|
toolScans = "get_scans"
|
||||||
|
toolCreate = "create_scan"
|
||||||
|
toolPreview = "preview_scan"
|
||||||
|
toolRun = "run_scan"
|
||||||
|
toolUpdateFilters = "update_scan_filters"
|
||||||
|
toolUpdateConfig = "update_scan_config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter is one scanner filter (enum-based or expression-based).
|
||||||
|
type Filter struct {
|
||||||
|
FilterType string
|
||||||
|
Predicate string
|
||||||
|
Values []string
|
||||||
|
Interval string
|
||||||
|
Length int
|
||||||
|
Plot string
|
||||||
|
Expression string
|
||||||
|
DisplayTitle string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column is one extra result column on a scan.
|
||||||
|
type Column struct {
|
||||||
|
DisplayName string
|
||||||
|
Expression string
|
||||||
|
Visible *bool
|
||||||
|
Order *int
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterSpecsRequest is the argument set for get_scanner_filter_specs (none).
|
||||||
|
type FilterSpecsRequest struct{}
|
||||||
|
|
||||||
|
// FilterSpecsResult is the parsed get_scanner_filter_specs payload.
|
||||||
|
type FilterSpecsResult struct{}
|
||||||
|
|
||||||
|
// DatapointsRequest is the argument set for get_scanner_datapoints (none).
|
||||||
|
type DatapointsRequest struct{}
|
||||||
|
|
||||||
|
// DatapointsResult is the parsed get_scanner_datapoints payload.
|
||||||
|
type DatapointsResult struct{}
|
||||||
|
|
||||||
|
// ScansRequest is the argument set for get_scans (none).
|
||||||
|
type ScansRequest struct{}
|
||||||
|
|
||||||
|
// Scan is one saved scanner from get_scans.
|
||||||
|
type Scan struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScansResult is the parsed get_scans payload.
|
||||||
|
type ScansResult struct {
|
||||||
|
Scans []Scan `json:"scans"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRequest is the argument set for create_scan.
|
||||||
|
type CreateRequest struct {
|
||||||
|
ScanID string
|
||||||
|
Preset string
|
||||||
|
Filters []Filter
|
||||||
|
Columns []Column
|
||||||
|
Title string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the parsed create_scan payload.
|
||||||
|
type CreateResult struct{}
|
||||||
|
|
||||||
|
// PreviewRequest is the argument set for preview_scan.
|
||||||
|
type PreviewRequest struct {
|
||||||
|
Filters []Filter
|
||||||
|
Columns []Column
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreviewResult is the parsed preview_scan payload.
|
||||||
|
type PreviewResult struct{}
|
||||||
|
|
||||||
|
// RunRequest is the argument set for run_scan.
|
||||||
|
type RunRequest struct {
|
||||||
|
ScanID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunResult is the parsed run_scan payload.
|
||||||
|
type RunResult struct{}
|
||||||
|
|
||||||
|
// UpdateFiltersRequest is the argument set for update_scan_filters.
|
||||||
|
type UpdateFiltersRequest struct {
|
||||||
|
ScanID string
|
||||||
|
Filters []Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFiltersResult is the parsed update_scan_filters payload.
|
||||||
|
type UpdateFiltersResult struct{}
|
||||||
|
|
||||||
|
// UpdateConfigRequest is the argument set for update_scan_config.
|
||||||
|
type UpdateConfigRequest struct {
|
||||||
|
ScanID string
|
||||||
|
SortingColumn string
|
||||||
|
SortingDirection string
|
||||||
|
Columns []Column
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfigResult is the parsed update_scan_config payload.
|
||||||
|
type UpdateConfigResult struct{}
|
||||||
|
|
||||||
|
// FilterSpecs calls get_scanner_filter_specs.
|
||||||
|
func (c *Client) FilterSpecs(ctx context.Context, req FilterSpecsRequest) (FilterSpecsResult, error) {
|
||||||
|
var out FilterSpecsResult
|
||||||
|
if err := c.parse(ctx, toolFilterSpecs, map[string]any{}, &out); err != nil {
|
||||||
|
return FilterSpecsResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datapoints calls get_scanner_datapoints.
|
||||||
|
func (c *Client) Datapoints(ctx context.Context, req DatapointsRequest) (DatapointsResult, error) {
|
||||||
|
var out DatapointsResult
|
||||||
|
if err := c.parse(ctx, toolDatapoints, map[string]any{}, &out); err != nil {
|
||||||
|
return DatapointsResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scans calls get_scans.
|
||||||
|
func (c *Client) Scans(ctx context.Context, req ScansRequest) (ScansResult, error) {
|
||||||
|
var out ScansResult
|
||||||
|
if err := c.parse(ctx, toolScans, map[string]any{}, &out); err != nil {
|
||||||
|
return ScansResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create calls create_scan.
|
||||||
|
func (c *Client) Create(ctx context.Context, req CreateRequest) (CreateResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.ScanID != "" {
|
||||||
|
args["scan_id"] = req.ScanID
|
||||||
|
}
|
||||||
|
if req.Preset != "" {
|
||||||
|
args["preset"] = req.Preset
|
||||||
|
}
|
||||||
|
if len(req.Filters) > 0 {
|
||||||
|
args["filters"] = encodeFilters(req.Filters)
|
||||||
|
}
|
||||||
|
if len(req.Columns) > 0 {
|
||||||
|
args["columns"] = encodeColumns(req.Columns)
|
||||||
|
}
|
||||||
|
if req.Title != "" {
|
||||||
|
args["title"] = req.Title
|
||||||
|
}
|
||||||
|
var out CreateResult
|
||||||
|
if err := c.parse(ctx, toolCreate, args, &out); err != nil {
|
||||||
|
return CreateResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview calls preview_scan.
|
||||||
|
func (c *Client) Preview(ctx context.Context, req PreviewRequest) (PreviewResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if len(req.Filters) > 0 {
|
||||||
|
args["filters"] = encodeFilters(req.Filters)
|
||||||
|
}
|
||||||
|
if len(req.Columns) > 0 {
|
||||||
|
args["columns"] = encodeColumns(req.Columns)
|
||||||
|
}
|
||||||
|
var out PreviewResult
|
||||||
|
if err := c.parse(ctx, toolPreview, args, &out); err != nil {
|
||||||
|
return PreviewResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run calls run_scan. scan_id is always sent (required).
|
||||||
|
func (c *Client) Run(ctx context.Context, req RunRequest) (RunResult, error) {
|
||||||
|
var out RunResult
|
||||||
|
if err := c.parse(ctx, toolRun, map[string]any{"scan_id": req.ScanID}, &out); err != nil {
|
||||||
|
return RunResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFilters calls update_scan_filters. scan_id and filters are always sent (required; empty filters clears).
|
||||||
|
func (c *Client) UpdateFilters(ctx context.Context, req UpdateFiltersRequest) (UpdateFiltersResult, error) {
|
||||||
|
filters := req.Filters
|
||||||
|
if filters == nil {
|
||||||
|
filters = []Filter{}
|
||||||
|
}
|
||||||
|
args := map[string]any{
|
||||||
|
"scan_id": req.ScanID,
|
||||||
|
"filters": encodeFilters(filters),
|
||||||
|
}
|
||||||
|
var out UpdateFiltersResult
|
||||||
|
if err := c.parse(ctx, toolUpdateFilters, args, &out); err != nil {
|
||||||
|
return UpdateFiltersResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig calls update_scan_config. scan_id is always sent (required).
|
||||||
|
func (c *Client) UpdateConfig(ctx context.Context, req UpdateConfigRequest) (UpdateConfigResult, error) {
|
||||||
|
args := map[string]any{"scan_id": req.ScanID}
|
||||||
|
if req.SortingColumn != "" {
|
||||||
|
args["sorting_column"] = req.SortingColumn
|
||||||
|
}
|
||||||
|
if req.SortingDirection != "" {
|
||||||
|
args["sorting_direction"] = req.SortingDirection
|
||||||
|
}
|
||||||
|
if len(req.Columns) > 0 {
|
||||||
|
args["columns"] = encodeColumns(req.Columns)
|
||||||
|
}
|
||||||
|
var out UpdateConfigResult
|
||||||
|
if err := c.parse(ctx, toolUpdateConfig, args, &out); err != nil {
|
||||||
|
return UpdateConfigResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFilters(filters []Filter) []map[string]any {
|
||||||
|
out := make([]map[string]any, len(filters))
|
||||||
|
for i, f := range filters {
|
||||||
|
m := map[string]any{}
|
||||||
|
if f.FilterType != "" {
|
||||||
|
m["filter_type"] = f.FilterType
|
||||||
|
}
|
||||||
|
if f.Predicate != "" {
|
||||||
|
m["predicate"] = f.Predicate
|
||||||
|
}
|
||||||
|
if len(f.Values) > 0 {
|
||||||
|
m["values"] = f.Values
|
||||||
|
}
|
||||||
|
if f.Interval != "" {
|
||||||
|
m["interval"] = f.Interval
|
||||||
|
}
|
||||||
|
if f.Length != 0 {
|
||||||
|
m["length"] = f.Length
|
||||||
|
}
|
||||||
|
if f.Plot != "" {
|
||||||
|
m["plot"] = f.Plot
|
||||||
|
}
|
||||||
|
if f.Expression != "" {
|
||||||
|
m["expression"] = f.Expression
|
||||||
|
}
|
||||||
|
if f.DisplayTitle != "" {
|
||||||
|
m["display_title"] = f.DisplayTitle
|
||||||
|
}
|
||||||
|
out[i] = m
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeColumns(cols []Column) []map[string]any {
|
||||||
|
out := make([]map[string]any, len(cols))
|
||||||
|
for i, col := range cols {
|
||||||
|
m := map[string]any{}
|
||||||
|
if col.DisplayName != "" {
|
||||||
|
m["display_name"] = col.DisplayName
|
||||||
|
}
|
||||||
|
if col.Expression != "" {
|
||||||
|
m["expression"] = col.Expression
|
||||||
|
}
|
||||||
|
if col.Visible != nil {
|
||||||
|
m["visible"] = *col.Visible
|
||||||
|
}
|
||||||
|
if col.Order != nil {
|
||||||
|
m["order"] = *col.Order
|
||||||
|
}
|
||||||
|
out[i] = m
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) parse(ctx context.Context, tool string, args map[string]any, dest any) error {
|
||||||
|
raw, err := c.c.Call(ctx, tool, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(wire.Unwrap(raw), dest); err != nil {
|
||||||
|
return client.ToolErrorf(tool, "parse: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
package scanner_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/rhntest"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/scanner"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScanner_toolNames(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
visible := true
|
||||||
|
order := 1
|
||||||
|
enumFilter := scanner.Filter{
|
||||||
|
FilterType: "FILTER_TYPE_RSI",
|
||||||
|
Predicate: "PREDICATE_GREATER_THAN",
|
||||||
|
Values: []string{"70"},
|
||||||
|
Interval: "1d",
|
||||||
|
Length: 14,
|
||||||
|
Plot: "close",
|
||||||
|
}
|
||||||
|
enumFilterWire := []map[string]any{
|
||||||
|
{
|
||||||
|
"filter_type": "FILTER_TYPE_RSI",
|
||||||
|
"predicate": "PREDICATE_GREATER_THAN",
|
||||||
|
"values": []string{"70"},
|
||||||
|
"interval": "1d",
|
||||||
|
"length": 14,
|
||||||
|
"plot": "close",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exprFilter := scanner.Filter{
|
||||||
|
Predicate: "=",
|
||||||
|
Values: []string{"True"},
|
||||||
|
Expression: `dayVolume / volumeAvg(candleCount=30, candlePeriod="1d", session="all")`,
|
||||||
|
DisplayTitle: "Relative volume (30D)",
|
||||||
|
}
|
||||||
|
exprFilterWire := []map[string]any{
|
||||||
|
{
|
||||||
|
"predicate": "=",
|
||||||
|
"values": []string{"True"},
|
||||||
|
"expression": `dayVolume / volumeAvg(candleCount=30, candlePeriod="1d", session="all")`,
|
||||||
|
"display_title": "Relative volume (30D)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
col := scanner.Column{
|
||||||
|
DisplayName: "Put/Call volume",
|
||||||
|
Expression: "optionsPutDayVolume / optionsCallDayVolume",
|
||||||
|
Visible: &visible,
|
||||||
|
Order: &order,
|
||||||
|
}
|
||||||
|
colWire := []map[string]any{
|
||||||
|
{
|
||||||
|
"display_name": "Put/Call volume",
|
||||||
|
"expression": "optionsPutDayVolume / optionsCallDayVolume",
|
||||||
|
"visible": true,
|
||||||
|
"order": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
call func(*scanner.Client) error
|
||||||
|
wantName string
|
||||||
|
wantArgs map[string]any
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "FilterSpecs",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.FilterSpecs(context.Background(), scanner.FilterSpecsRequest{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_scanner_filter_specs",
|
||||||
|
wantArgs: map[string]any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Datapoints",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.Datapoints(context.Background(), scanner.DatapointsRequest{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_scanner_datapoints",
|
||||||
|
wantArgs: map[string]any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Scans",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.Scans(context.Background(), scanner.ScansRequest{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_scans",
|
||||||
|
wantArgs: map[string]any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Create",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.Create(context.Background(), scanner.CreateRequest{
|
||||||
|
ScanID: "scan-1",
|
||||||
|
Preset: "INITIAL",
|
||||||
|
Filters: []scanner.Filter{enumFilter},
|
||||||
|
Columns: []scanner.Column{col},
|
||||||
|
Title: "RSI overbought",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "create_scan",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"scan_id": "scan-1",
|
||||||
|
"preset": "INITIAL",
|
||||||
|
"filters": enumFilterWire,
|
||||||
|
"columns": colWire,
|
||||||
|
"title": "RSI overbought",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Preview",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.Preview(context.Background(), scanner.PreviewRequest{
|
||||||
|
Filters: []scanner.Filter{exprFilter},
|
||||||
|
Columns: []scanner.Column{col},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "preview_scan",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"filters": exprFilterWire,
|
||||||
|
"columns": colWire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Run",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.Run(context.Background(), scanner.RunRequest{ScanID: "scan-1"})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "run_scan",
|
||||||
|
wantArgs: map[string]any{"scan_id": "scan-1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UpdateFilters",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.UpdateFilters(context.Background(), scanner.UpdateFiltersRequest{
|
||||||
|
ScanID: "scan-1",
|
||||||
|
Filters: []scanner.Filter{enumFilter},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "update_scan_filters",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"scan_id": "scan-1",
|
||||||
|
"filters": enumFilterWire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UpdateConfig",
|
||||||
|
call: func(c *scanner.Client) error {
|
||||||
|
_, err := c.UpdateConfig(context.Background(), scanner.UpdateConfigRequest{
|
||||||
|
ScanID: "scan-1",
|
||||||
|
SortingColumn: "Volume",
|
||||||
|
SortingDirection: "desc",
|
||||||
|
Columns: []scanner.Column{col},
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "update_scan_config",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"scan_id": "scan-1",
|
||||||
|
"sorting_column": "Volume",
|
||||||
|
"sorting_direction": "desc",
|
||||||
|
"columns": colWire,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var gotName string
|
||||||
|
var gotArgs map[string]any
|
||||||
|
c := scanner.New(client.Func(func(ctx context.Context, name string, args map[string]any) (json.RawMessage, error) {
|
||||||
|
gotName, gotArgs = name, args
|
||||||
|
return json.RawMessage(`{"scans":[]}`), 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)
|
||||||
|
}
|
||||||
|
if tc.name == "Run" {
|
||||||
|
if gotArgs["scan_id"] != "scan-1" {
|
||||||
|
t.Fatalf("scan_id %v", gotArgs["scan_id"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
want := []string{
|
||||||
|
"create_scan",
|
||||||
|
"get_scanner_datapoints",
|
||||||
|
"get_scanner_filter_specs",
|
||||||
|
"get_scans",
|
||||||
|
"preview_scan",
|
||||||
|
"run_scan",
|
||||||
|
"update_scan_config",
|
||||||
|
"update_scan_filters",
|
||||||
|
}
|
||||||
|
got := append([]string(nil), scanner.Tools()...)
|
||||||
|
sort.Strings(got)
|
||||||
|
if diff := cmp.Diff(want, got); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScans_rhntest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
s := rhntest.New(t)
|
||||||
|
s.Set("get_scans", json.RawMessage(`{"scans":[]}`))
|
||||||
|
c := scanner.New(&client.Client{URL: s.URL})
|
||||||
|
got, err := c.Scans(context.Background(), scanner.ScansRequest{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(got.Scans) != 0 {
|
||||||
|
t.Fatalf("%+v", got)
|
||||||
|
}
|
||||||
|
if s.LastName() != "get_scans" {
|
||||||
|
t.Fatalf("%s", s.LastName())
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(map[string]any{}, s.LastArgs()); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user