-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathharness.go
More file actions
108 lines (95 loc) · 3.24 KB
/
Copy pathharness.go
File metadata and controls
108 lines (95 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package strut
import (
"fmt"
"reflect"
"github.com/disgoorg/disgo/discord"
)
// Simulation drives a registered handler with caller-supplied seams, so a
// command can be exercised without a gateway or a REST client. The struttest
// package builds on these; a bot never calls them.
//
// Each returns the failure the framework reported, which is nil on success
// and has already been routed through the error handlers.
// SimulateCommand runs the command at route against the given interaction
// data.
func (f *Framework[D]) SimulateCommand(route string, data discord.SlashCommandInteractionData, e *Event[D]) (*Error[D], error) {
inv, ok := f.invs[route]
if !ok {
return nil, fmt.Errorf("strut: no command registered at %q", route)
}
e.meta = inv.meta
if err := f.run(inv, e, data, func(*Event[D], reflect.Value) error { return nil }); err != nil {
f.report(inv, err)
return err, nil
}
return nil, nil
}
// SimulateText runs a prefix invocation. content is the whole message,
// including the prefix.
func (f *Framework[D]) SimulateText(content string, e *Event[D]) (*Error[D], error) {
if f.opts.Prefix == nil {
return nil, fmt.Errorf("strut: prefix commands are not enabled")
}
node, lex, ok := f.resolveText(content)
if !ok {
return nil, fmt.Errorf("strut: %q names no command", content)
}
return f.finishPrefix(node, e, lex), nil
}
// SimulateComponent runs the handler registered for the given state value.
func (f *Framework[D]) SimulateComponent(state any, e *Event[D]) (*Error[D], error) {
c, err := codecFor(reflect.TypeOf(state))
if err != nil {
return nil, err
}
h, ok := f.components[c.route]
if !ok {
return nil, fmt.Errorf("strut: no component handler for %s", c.route)
}
return h(e, reflect.ValueOf(state)), nil
}
// SimulateModal runs the handler registered for modals of the given type.
func (f *Framework[D]) SimulateModal(modal reflect.Type, data discord.ModalSubmitInteractionData, e *Event[D]) (*Error[D], error) {
h, ok := f.modals[modalRoute(modal)]
if !ok {
return nil, fmt.Errorf("strut: no modal handler for %s", modal)
}
c, err := modalCodecFor(modal)
if err != nil {
return nil, err
}
v, err := c.decode(data)
if err != nil {
return nil, err
}
return h(e, v), nil
}
// SimulateAutocomplete asks a command's autocomplete method for suggestions.
func (f *Framework[D]) SimulateAutocomplete(route, option string, p Partial, e *Event[D]) ([]discord.AutocompleteChoice, error) {
inv, ok := f.invs[route]
if !ok {
return nil, fmt.Errorf("strut: no command registered at %q", route)
}
c, ok := inv.completers[option]
if !ok {
return nil, fmt.Errorf("strut: option %q on %q is not autocompleted", option, route)
}
e.meta = inv.meta
// Same chain the live path runs, so a harness sees what production does.
var out []discord.AutocompleteChoice
err := f.chain(func(ev *Event[D]) error {
var err error
out, err = c.call(reflect.ValueOf(ev), p)
return err
})(e)
return out, err
}
// OptionTypes reports the wire type of each option of a command, so a harness
// can build interaction data without restating them.
func (f *Framework[D]) OptionTypes(route string) (map[string]discord.ApplicationCommandOptionType, bool) {
inv, ok := f.invs[route]
if !ok {
return nil, false
}
return inv.dec.types, true
}