@@ -15,8 +15,11 @@ import (
1515 "time"
1616
1717 api "github.com/bootdotdev/bootdev/client"
18+ "github.com/bootdotdev/bootdev/messages"
19+ tea "github.com/charmbracelet/bubbletea"
1820 "github.com/itchyny/gojq"
1921 "github.com/spf13/cobra"
22+ "github.com/spf13/viper"
2023)
2124
2225func runCLICommand (command api.CLIStepCLICommand , variables map [string ]string ) (result api.CLICommandResult ) {
@@ -124,7 +127,7 @@ func runHTTPRequest(
124127 return result
125128}
126129
127- func CLIChecks (cliData api.CLIData , overrideBaseURL string ) (results []api.CLIStepResult ) {
130+ func CLIChecks (cliData api.CLIData , overrideBaseURL string , ch chan tea. Msg ) (results []api.CLIStepResult ) {
128131 client := & http.Client {}
129132 variables := make (map [string ]string )
130133 results = make ([]api.CLIStepResult , len (cliData .Steps ))
@@ -140,23 +143,155 @@ func CLIChecks(cliData api.CLIData, overrideBaseURL string) (results []api.CLISt
140143 }
141144
142145 for i , step := range cliData .Steps {
146+ // This is the magic of the initial message sent before executing the test
147+ if step .CLICommand != nil {
148+ ch <- messages.StartStepMsg {CMD : step .CLICommand .Command }
149+ } else if step .HTTPRequest != nil {
150+ finalBaseURL := baseURL
151+ overrideURL := viper .GetString ("override_base_url" )
152+ if overrideURL != "" {
153+ finalBaseURL = overrideURL
154+ }
155+ fullURL := strings .Replace (step .HTTPRequest .Request .FullURL , api .BaseURLPlaceholder , finalBaseURL , 1 )
156+ interpolatedURL := InterpolateVariables (fullURL , variables )
157+
158+ ch <- messages.StartStepMsg {
159+ URL : interpolatedURL ,
160+ Method : step .HTTPRequest .Request .Method ,
161+ ResponseVariables : step .HTTPRequest .ResponseVariables ,
162+ }
163+ }
164+
143165 switch {
144166 case step .CLICommand != nil :
145167 result := runCLICommand (* step .CLICommand , variables )
146168 results [i ].CLICommandResult = & result
169+
170+ sendCLICommandResults (ch , * step .CLICommand , result , i )
171+
147172 case step .HTTPRequest != nil :
148173 result := runHTTPRequest (client , baseURL , variables , * step .HTTPRequest )
149174 results [i ].HTTPRequestResult = & result
150175 if result .Variables != nil {
151176 variables = result .Variables
152177 }
178+
179+ sendHTTPRequestResults (ch , * step .HTTPRequest , result , i )
180+
153181 default :
154182 cobra .CheckErr ("unable to run lesson: missing step" )
155183 }
156184 }
157185 return results
158186}
159187
188+ func sendCLICommandResults (ch chan tea.Msg , cmd api.CLIStepCLICommand , result api.CLICommandResult , index int ) {
189+ for _ , test := range cmd .Tests {
190+ ch <- messages.StartTestMsg {Text : prettyPrintCLICommand (test , result .Variables )}
191+ }
192+
193+ for j := range cmd .Tests {
194+ ch <- messages.ResolveTestMsg {Index : j }
195+ }
196+
197+ ch <- messages.ResolveStepMsg {
198+ Index : index ,
199+ Result : & api.CLIStepResult {
200+ CLICommandResult : & result ,
201+ },
202+ }
203+ }
204+
205+ func sendHTTPRequestResults (ch chan tea.Msg , req api.CLIStepHTTPRequest , result api.HTTPRequestResult , index int ) {
206+ for _ , test := range req .Tests {
207+ ch <- messages.StartTestMsg {Text : prettyPrintHTTPTest (test , result .Variables )}
208+ }
209+
210+ for j := range req .Tests {
211+ ch <- messages.ResolveTestMsg {Index : j }
212+ }
213+
214+ ch <- messages.ResolveStepMsg {
215+ Index : index ,
216+ Result : & api.CLIStepResult {
217+ HTTPRequestResult : & result ,
218+ },
219+ }
220+ }
221+
222+ func prettyPrintCLICommand (test api.CLICommandTest , variables map [string ]string ) string {
223+ if test .ExitCode != nil {
224+ return fmt .Sprintf ("Expect exit code %d" , * test .ExitCode )
225+ }
226+ if test .StdoutLinesGt != nil {
227+ return fmt .Sprintf ("Expect > %d lines on stdout" , * test .StdoutLinesGt )
228+ }
229+ if test .StdoutContainsAll != nil {
230+ str := "Expect stdout to contain all of:"
231+ for _ , contains := range test .StdoutContainsAll {
232+ interpolatedContains := InterpolateVariables (contains , variables )
233+ str += fmt .Sprintf ("\n - '%s'" , interpolatedContains )
234+ }
235+ return str
236+ }
237+ if test .StdoutContainsNone != nil {
238+ str := "Expect stdout to contain none of:"
239+ for _ , containsNone := range test .StdoutContainsNone {
240+ interpolatedContainsNone := InterpolateVariables (containsNone , variables )
241+ str += fmt .Sprintf ("\n - '%s'" , interpolatedContainsNone )
242+ }
243+ return str
244+ }
245+ return ""
246+ }
247+
248+ func prettyPrintHTTPTest (test api.HTTPRequestTest , variables map [string ]string ) string {
249+ if test .StatusCode != nil {
250+ return fmt .Sprintf ("Expecting status code: %d" , * test .StatusCode )
251+ }
252+ if test .BodyContains != nil {
253+ interpolated := InterpolateVariables (* test .BodyContains , variables )
254+ return fmt .Sprintf ("Expecting body to contain: %s" , interpolated )
255+ }
256+ if test .BodyContainsNone != nil {
257+ interpolated := InterpolateVariables (* test .BodyContainsNone , variables )
258+ return fmt .Sprintf ("Expecting JSON body to not contain: %s" , interpolated )
259+ }
260+ if test .HeadersContain != nil {
261+ interpolatedKey := InterpolateVariables (test .HeadersContain .Key , variables )
262+ interpolatedValue := InterpolateVariables (test .HeadersContain .Value , variables )
263+ return fmt .Sprintf ("Expecting headers to contain: '%s: %v'" , interpolatedKey , interpolatedValue )
264+ }
265+ if test .TrailersContain != nil {
266+ interpolatedKey := InterpolateVariables (test .TrailersContain .Key , variables )
267+ interpolatedValue := InterpolateVariables (test .TrailersContain .Value , variables )
268+ return fmt .Sprintf ("Expecting trailers to contain: '%s: %v'" , interpolatedKey , interpolatedValue )
269+ }
270+ if test .JSONValue != nil {
271+ var val any
272+ var op any
273+ if test .JSONValue .IntValue != nil {
274+ val = * test .JSONValue .IntValue
275+ } else if test .JSONValue .StringValue != nil {
276+ val = * test .JSONValue .StringValue
277+ } else if test .JSONValue .BoolValue != nil {
278+ val = * test .JSONValue .BoolValue
279+ }
280+ if test .JSONValue .Operator == api .OpEquals {
281+ op = "to be equal to"
282+ } else if test .JSONValue .Operator == api .OpGreaterThan {
283+ op = "to be greater than"
284+ } else if test .JSONValue .Operator == api .OpContains {
285+ op = "contains"
286+ } else if test .JSONValue .Operator == api .OpNotContains {
287+ op = "to not contain"
288+ }
289+ expecting := fmt .Sprintf ("Expecting JSON at %v %s %v" , test .JSONValue .Path , op , val )
290+ return InterpolateVariables (expecting , variables )
291+ }
292+ return ""
293+ }
294+
160295// truncateAndStringifyBody
161296// in some lessons we yeet the entire body up to the server, but we really shouldn't ever care
162297// about more than 100,000 stringified characters of it, so this protects against giant bodies
0 commit comments