1package fsm_test
2
3import (
4	"testing"
5
6	"github.com/jawher/mow.cli/internal/container"
7	"github.com/jawher/mow.cli/internal/fsm"
8	"github.com/jawher/mow.cli/internal/fsm/fsmtest"
9	"github.com/jawher/mow.cli/internal/matcher"
10	"github.com/jawher/mow.cli/internal/values"
11	"github.com/stretchr/testify/require"
12)
13
14func TestApplyTerminalStateNoArgs(t *testing.T) {
15	s := fsm.NewState()
16	s.Terminal = true
17
18	err := s.Parse(nil)
19
20	require.NoError(t, err)
21}
22
23func TestApply(t *testing.T) {
24	var (
25		testArgs = []string{"1", "2", "3"}
26		optStrs  []string
27		optCon   = &container.Container{
28			Value: values.NewStrings(&optStrs, nil),
29		}
30		argStrs []string
31		argCon  = &container.Container{
32			Value: values.NewStrings(&argStrs, nil),
33		}
34		calls []string
35	)
36	matchers := map[string]matcher.Matcher{
37		"a": fsmtest.TestMatcher{
38			TestPriority: 2,
39			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
40				require.Equal(t, testArgs, args)
41
42				calls = append(calls, "a")
43
44				c.Opts[optCon] = []string{"a.opt"}
45				c.Args[argCon] = []string{"a.arg"}
46				return true, args[1:]
47			},
48		},
49		"b": fsmtest.TestMatcher{
50			TestPriority: 1,
51			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
52				require.Equal(t, testArgs, args)
53
54				calls = append(calls, "b")
55
56				c.Opts[optCon] = []string{"b.opt"}
57				c.Args[argCon] = []string{"b.arg"}
58				return true, args[1:]
59			},
60		},
61		"c": fsmtest.TestMatcher{
62			TestPriority: 1,
63			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
64				require.Equal(t, testArgs[1:], args, "second stage matchers should be called with the rem args")
65
66				calls = append(calls, "c")
67
68				c.Opts[optCon] = []string{"c.opt"}
69				c.Args[argCon] = []string{"c.arg"}
70				return true, args[1:]
71			},
72		},
73		"d": fsmtest.TestMatcher{
74			TestPriority: 1,
75			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
76				require.Equal(t, testArgs[1:], args, "second stage matchers should be called with the rem args")
77
78				calls = append(calls, "d")
79
80				c.Opts[optCon] = []string{"d.opt"}
81				c.Args[argCon] = []string{"d.arg"}
82				return false, args[1:]
83			},
84		},
85		"e": fsmtest.TestMatcher{
86			TestPriority: 1,
87			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
88				require.Equal(t, testArgs[2:], args, "third stage matchers should be called with the rem args")
89
90				calls = append(calls, "e")
91
92				c.Opts[optCon] = []string{"e.opt"}
93				c.Args[argCon] = []string{"e.arg"}
94				return true, nil
95			},
96		},
97	}
98	s := fsmtest.NewFsm(`
99		S1 a S2
100		S1 b S3
101
102		S2 c S4
103		S3 d S4
104
105		S4 e (S5)
106	`, matchers)
107
108	s.Prepare()
109
110	err := s.Parse(testArgs)
111
112	require.Equal(t, []string{"b", "a", "d", "c", "e"}, calls)
113
114	require.NoError(t, err)
115
116	require.Equal(t, []string{"a.opt", "c.opt", "e.opt"}, optStrs)
117	require.Equal(t, []string{"a.arg", "c.arg", "e.arg"}, argStrs)
118}
119
120func TestApplyRejectOptions(t *testing.T) {
121	var (
122		testArgs = []string{"1", "--", "2"}
123		calls    []string
124	)
125	matchers := map[string]matcher.Matcher{
126		"a": fsmtest.TestMatcher{
127			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
128				require.Equal(t, testArgs, args)
129				require.False(t, c.RejectOptions)
130
131				calls = append(calls, "a")
132				return true, args[1:]
133			},
134		},
135		"b": fsmtest.TestMatcher{
136			MatchFunc: func(args []string, c *matcher.ParseContext) (bool, []string) {
137				require.Equal(t, []string{"2"}, args)
138				require.True(t, c.RejectOptions)
139
140				calls = append(calls, "b")
141				return true, args[1:]
142			},
143		},
144	}
145	s := fsmtest.NewFsm(`
146		S1 a S2
147		S2 b (S3)
148	`, matchers)
149
150	s.Prepare()
151
152	err := s.Parse(testArgs)
153
154	require.NoError(t, err)
155
156	require.Equal(t, []string{"a", "b"}, calls)
157
158}
159