master
go 216 lines 6.74 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package functions
4
5 import (
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10 )
11
12 const (
13 parserTestPermissions = "0xFFFF"
14 parserTestSource = "method=api,role=test"
15 testPayloadStartLine = `FUNCTION_PAYLOAD tx1 1 "fn1 arg1" ` + parserTestPermissions + ` "` + parserTestSource + `" application/json`
16 testFunctionLine = `FUNCTION tx2 1 "fn2 arg1" ` + parserTestPermissions + ` "` + parserTestSource + `"`
17 )
18
19 func TestInputParser_ParseEvent(t *testing.T) {
20 tests := map[string]struct {
21 lines []string
22 wantErr bool
23 assertEvent func(t *testing.T, events []inputEvent)
24 assertState func(t *testing.T, p *inputParser)
25 }{
26 "cancel in normal mode": {
27 lines: []string{"FUNCTION_CANCEL tx1"},
28 assertEvent: func(t *testing.T, events []inputEvent) {
29 require.Len(t, events, 1)
30 assert.Equal(t, inputEventCancel, events[0].kind)
31 assert.Equal(t, "tx1", events[0].uid)
32 assert.False(t, events[0].preAdmission)
33 },
34 },
35 "progress in normal mode": {
36 lines: []string{"FUNCTION_PROGRESS tx1 10 100"},
37 assertEvent: func(t *testing.T, events []inputEvent) {
38 require.Len(t, events, 1)
39 assert.Equal(t, inputEventProgress, events[0].kind)
40 assert.Equal(t, "tx1", events[0].uid)
41 },
42 },
43 "malformed cancel with extra token": {
44 lines: []string{"FUNCTION_CANCEL tx1 extra"},
45 wantErr: true,
46 },
47 "malformed cancel with missing uid": {
48 lines: []string{"FUNCTION_CANCEL"},
49 wantErr: true,
50 },
51 "payload cancel with different uid keeps payload": {
52 lines: []string{testPayloadStartLine, "line1", "FUNCTION_CANCEL tx-other", "line2", "FUNCTION_PAYLOAD_END"},
53 assertEvent: func(t *testing.T, events []inputEvent) {
54 require.Len(t, events, 2)
55 assert.Equal(t, inputEventCancel, events[0].kind)
56 assert.Equal(t, "tx-other", events[0].uid)
57 assert.False(t, events[0].preAdmission)
58
59 assert.Equal(t, inputEventCall, events[1].kind)
60 require.NotNil(t, events[1].fn)
61 assert.Equal(t, "tx1", events[1].fn.UID)
62 assert.Equal(t, []byte("line1\nline2"), events[1].fn.Payload)
63 },
64 assertState: func(t *testing.T, p *inputParser) {
65 assert.False(t, p.readingPayload)
66 assert.Nil(t, p.currentFn)
67 assert.Equal(t, 0, p.payloadBuf.Len())
68 },
69 },
70 "payload cancel with same uid is pre-admission": {
71 lines: []string{testPayloadStartLine, "line1", "FUNCTION_CANCEL tx1"},
72 assertEvent: func(t *testing.T, events []inputEvent) {
73 require.Len(t, events, 1)
74 assert.Equal(t, inputEventCancel, events[0].kind)
75 assert.Equal(t, "tx1", events[0].uid)
76 assert.True(t, events[0].preAdmission)
77 },
78 assertState: func(t *testing.T, p *inputParser) {
79 assert.False(t, p.readingPayload)
80 assert.Nil(t, p.currentFn)
81 assert.Equal(t, 0, p.payloadBuf.Len())
82 },
83 },
84 "malformed cancel during payload keeps parser state": {
85 lines: []string{testPayloadStartLine, "line1", "FUNCTION_CANCEL tx1 extra"},
86 wantErr: true,
87 assertState: func(t *testing.T, p *inputParser) {
88 assert.True(t, p.readingPayload)
89 require.NotNil(t, p.currentFn)
90 assert.Equal(t, "tx1", p.currentFn.UID)
91 assert.Equal(t, "line1", p.payloadBuf.String())
92 },
93 },
94 "progress during payload keeps payload": {
95 lines: []string{testPayloadStartLine, "line1", "FUNCTION_PROGRESS tx1 10 100", "line2", "FUNCTION_PAYLOAD_END"},
96 assertEvent: func(t *testing.T, events []inputEvent) {
97 require.Len(t, events, 2)
98 assert.Equal(t, inputEventProgress, events[0].kind)
99 assert.Equal(t, "tx1", events[0].uid)
100
101 assert.Equal(t, inputEventCall, events[1].kind)
102 require.NotNil(t, events[1].fn)
103 assert.Equal(t, []byte("line1\nline2"), events[1].fn.Payload)
104 },
105 },
106 "payload data line with FUNCTION prefix text is preserved": {
107 lines: []string{testPayloadStartLine, "line1", "FUNCTIONALITY=true", "line2", "FUNCTION_PAYLOAD_END"},
108 assertEvent: func(t *testing.T, events []inputEvent) {
109 require.Len(t, events, 1)
110 assert.Equal(t, inputEventCall, events[0].kind)
111 require.NotNil(t, events[0].fn)
112 assert.Equal(t, []byte("line1\nFUNCTIONALITY=true\nline2"), events[0].fn.Payload)
113 },
114 },
115 "unexpected control line during payload aborts partial payload": {
116 lines: []string{testPayloadStartLine, "line1", testFunctionLine},
117 assertEvent: func(t *testing.T, events []inputEvent) {
118 require.Len(t, events, 1)
119 assert.Equal(t, inputEventCall, events[0].kind)
120 require.NotNil(t, events[0].fn)
121 assert.Equal(t, "tx2", events[0].fn.UID)
122 assert.Nil(t, events[0].fn.Payload)
123 },
124 assertState: func(t *testing.T, p *inputParser) {
125 assert.False(t, p.readingPayload)
126 assert.Nil(t, p.currentFn)
127 },
128 },
129 "quit during payload aborts payload and emits quit": {
130 lines: []string{testPayloadStartLine, "line1", "QUIT"},
131 assertEvent: func(t *testing.T, events []inputEvent) {
132 require.Len(t, events, 1)
133 assert.Equal(t, inputEventQuit, events[0].kind)
134 },
135 assertState: func(t *testing.T, p *inputParser) {
136 assert.False(t, p.readingPayload)
137 assert.Nil(t, p.currentFn)
138 assert.Equal(t, 0, p.payloadBuf.Len())
139 },
140 },
141 "unknown FUNCTION_ control during payload aborts payload and errors": {
142 lines: []string{testPayloadStartLine, "line1", "FUNCTION_UNKNOWN tx1"},
143 wantErr: true,
144 assertState: func(t *testing.T, p *inputParser) {
145 assert.False(t, p.readingPayload)
146 assert.Nil(t, p.currentFn)
147 assert.Equal(t, 0, p.payloadBuf.Len())
148 },
149 },
150 }
151
152 for name, tc := range tests {
153 t.Run(name, func(t *testing.T) {
154 p := newInputParser()
155 events := make([]inputEvent, 0, len(tc.lines))
156 var parseErr error
157
158 for _, line := range tc.lines {
159 ev, err := p.parseEvent(line)
160 if err != nil {
161 parseErr = err
162 break
163 }
164 if ev.kind != inputEventNone {
165 events = append(events, ev)
166 }
167 }
168
169 if tc.wantErr {
170 require.Error(t, parseErr)
171 } else {
172 require.NoError(t, parseErr)
173 }
174 if !tc.wantErr && tc.assertEvent != nil {
175 tc.assertEvent(t, events)
176 }
177 if tc.assertState != nil {
178 tc.assertState(t, p)
179 }
180 })
181 }
182 }
183
184 func TestInputParser_Parse_Wrapper(t *testing.T) {
185 tests := map[string]struct {
186 line string
187 wantFn bool
188 wantID string
189 }{
190 "function line returns function": {
191 line: `FUNCTION tx1 1 "fn1 arg1" ` + parserTestPermissions + ` "` + parserTestSource + `"`,
192 wantFn: true,
193 wantID: "tx1",
194 },
195 "cancel line returns nil function": {
196 line: "FUNCTION_CANCEL tx1",
197 },
198 "progress line returns nil function": {
199 line: "FUNCTION_PROGRESS tx1 10 100",
200 },
201 }
202
203 for name, tc := range tests {
204 t.Run(name, func(t *testing.T) {
205 p := newInputParser()
206 fn, err := p.parse(tc.line)
207 require.NoError(t, err)
208 if tc.wantFn {
209 require.NotNil(t, fn)
210 assert.Equal(t, tc.wantID, fn.UID)
211 return
212 }
213 assert.Nil(t, fn)
214 })
215 }
216 }