Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions checks/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ func sendCLICommandResults(ch chan tea.Msg, cmd api.CLIStepCLICommand, result ap
}

for j := range cmd.Tests {
ch <- messages.ResolveTestMsg{Index: j}
ch <- messages.ResolveTestMsg{
StepIndex: index,
TestIndex: j,
}
}

ch <- messages.ResolveStepMsg{
Expand All @@ -205,7 +208,10 @@ func sendHTTPRequestResults(ch chan tea.Msg, req api.CLIStepHTTPRequest, result
}

for j := range req.Tests {
ch <- messages.ResolveTestMsg{Index: j}
ch <- messages.ResolveTestMsg{
StepIndex: index,
TestIndex: j,
}
}

ch <- messages.ResolveStepMsg{
Expand All @@ -216,6 +222,44 @@ func sendHTTPRequestResults(ch chan tea.Msg, req api.CLIStepHTTPRequest, result
}
}

func ApplySubmissionResults(cliData api.CLIData, failure *api.VerificationResultStructuredErrCLI, ch chan tea.Msg) {
for i, step := range cliData.Steps {
pass := true
if failure != nil {
pass = i < failure.FailedStepIndex
}

ch <- messages.ResolveStepMsg{
Index: i,
Passed: &pass,
}

if step.CLICommand != nil {
for j := range step.CLICommand.Tests {
ch <- messages.ResolveTestMsg{
StepIndex: i,
TestIndex: j,
Passed: &pass,
}
}
}
if step.HTTPRequest != nil {
for j := range step.HTTPRequest.Tests {
ch <- messages.ResolveTestMsg{
StepIndex: i,
TestIndex: j,
Passed: &pass,
}
}
}

if !pass {
break
}

}
}

func prettyPrintCLICommand(test api.CLICommandTest, variables map[string]string) string {
if test.ExitCode != nil {
return fmt.Sprintf("Expect exit code %d", *test.ExitCode)
Expand Down
2 changes: 2 additions & 0 deletions cmd/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ func submissionHandler(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
checks.ApplySubmissionResults(data, failure, ch)

finalise(failure)
} else {
finalise(nil)
Expand Down
5 changes: 3 additions & 2 deletions messages/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ type StartTestMsg struct {
}

type ResolveTestMsg struct {
Index int
Passed *bool
StepIndex int
TestIndex int
Passed *bool
}

type DoneStepMsg struct {
Expand Down
206 changes: 8 additions & 198 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ func (m rootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case messages.ResolveStepMsg:
m.steps[msg.Index].passed = msg.Passed
m.steps[msg.Index].finished = true
m.steps[msg.Index].result = msg.Result
if msg.Result != nil {
m.steps[msg.Index].result = msg.Result
}
return m, nil

case messages.StartTestMsg:
Expand All @@ -155,8 +157,8 @@ func (m rootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil

case messages.ResolveTestMsg:
m.steps[len(m.steps)-1].tests[msg.Index].passed = msg.Passed
m.steps[len(m.steps)-1].tests[msg.Index].finished = true
m.steps[msg.StepIndex].tests[msg.TestIndex].passed = msg.Passed
m.steps[msg.StepIndex].tests[msg.TestIndex].finished = true
return m, nil

default:
Expand Down Expand Up @@ -202,44 +204,16 @@ func (m rootModel) View() string {
}
}
if m.failure != nil {
str += red.Render("\n\nError: "+m.failure.ErrorMessage) + "\n\n"
str += "\n\n" + red.Render("Tests failed! ❌")
str += red.Render(fmt.Sprintf("\n\nFailed Step: %v", m.failure.FailedStepIndex+1))
str += red.Render("\nError: "+m.failure.ErrorMessage) + "\n\n"
} else if m.success {
str += "\n\n" + green.Render("All tests passed! 🎉") + "\n\n"
str += green.Render("Return to your browser to continue with the next lesson.") + "\n\n"
}
return str
}

func prettyPrintCLICommand(test api.CLICommandTest, variables map[string]string) string {
if test.ExitCode != nil {
return fmt.Sprintf("Expect exit code %d", *test.ExitCode)
}
if test.StdoutLinesGt != nil {
return fmt.Sprintf("Expect > %d lines on stdout", *test.StdoutLinesGt)
}
if test.StdoutContainsAll != nil {
str := "Expect stdout to contain all of:"
for _, contains := range test.StdoutContainsAll {
interpolatedContains := checks.InterpolateVariables(contains, variables)
str += fmt.Sprintf("\n - '%s'", interpolatedContains)
}
return str
}
if test.StdoutContainsNone != nil {
str := "Expect stdout to contain none of:"
for _, containsNone := range test.StdoutContainsNone {
interpolatedContainsNone := checks.InterpolateVariables(containsNone, variables)
str += fmt.Sprintf("\n - '%s'", interpolatedContainsNone)
}
return str
}
return ""
}

func pointerToBool(a bool) *bool {
return &a
}

func printHTTPRequestResult(result api.HTTPRequestResult) string {
if result.Err != "" {
return fmt.Sprintf(" Err: %v\n\n", result.Err)
Expand Down Expand Up @@ -358,167 +332,3 @@ func StartRenderer(data api.CLIData, isSubmit bool, ch chan tea.Msg) func(*api.V
wg.Wait()
}
}

func renderCLICommand(
cmd api.CLIStepCLICommand,
result api.CLICommandResult,
failure *api.VerificationResultStructuredErrCLI,
isSubmit bool,
ch chan tea.Msg,
index int,
) {
for _, test := range cmd.Tests {
ch <- messages.StartTestMsg{Text: prettyPrintCLICommand(test, result.Variables)}
}

earlierCmdFailed := false
if failure != nil {
earlierCmdFailed = failure.FailedStepIndex < index
}
for j := range cmd.Tests {
earlierTestFailed := false
if failure != nil {
if earlierCmdFailed {
earlierTestFailed = true
} else if failure.FailedStepIndex == index {
earlierTestFailed = failure.FailedTestIndex < j
}
}
if !isSubmit {
ch <- messages.ResolveTestMsg{Index: j}
} else if earlierTestFailed {
ch <- messages.ResolveTestMsg{Index: j}
} else {
passed := failure == nil || failure.FailedStepIndex != index || failure.FailedTestIndex != j
ch <- messages.ResolveTestMsg{
Index: j,
Passed: pointerToBool(passed),
}
}
}

if !isSubmit {
ch <- messages.ResolveStepMsg{
Index: index,
Result: &api.CLIStepResult{
CLICommandResult: &result,
},
}
} else if earlierCmdFailed {
ch <- messages.ResolveStepMsg{Index: index}
} else {
passed := failure == nil || failure.FailedStepIndex != index
if passed {
ch <- messages.ResolveStepMsg{
Index: index,
Passed: pointerToBool(passed),
}
} else {
ch <- messages.ResolveStepMsg{
Index: index,
Passed: pointerToBool(passed),
Result: &api.CLIStepResult{
CLICommandResult: &result,
},
}
}
}
}

func renderHTTPRequest(
req api.CLIStepHTTPRequest,
result api.HTTPRequestResult,
failure *api.VerificationResultStructuredErrCLI,
isSubmit bool,
baseURLDefault string,
ch chan tea.Msg,
index int,
) {
for _, test := range req.Tests {
ch <- messages.StartTestMsg{Text: prettyPrintHTTPTest(test, result.Variables)}
}

for j := range req.Tests {
if !isSubmit {
ch <- messages.ResolveTestMsg{Index: j}
} else if failure != nil && (failure.FailedStepIndex < index || (failure.FailedStepIndex == index && failure.FailedTestIndex < j)) {
ch <- messages.ResolveTestMsg{Index: j}
} else {
ch <- messages.ResolveTestMsg{Index: j, Passed: pointerToBool(failure == nil || !(failure.FailedStepIndex == index && failure.FailedTestIndex == j))}
}
}

if !isSubmit {
ch <- messages.ResolveStepMsg{
Index: index,
Result: &api.CLIStepResult{
HTTPRequestResult: &result,
},
}
} else if failure != nil && failure.FailedStepIndex < index {
ch <- messages.ResolveStepMsg{Index: index}
} else {
passed := failure == nil || failure.FailedStepIndex != index
if passed {
ch <- messages.ResolveStepMsg{
Index: index,
Passed: pointerToBool(passed),
}
} else {
ch <- messages.ResolveStepMsg{
Index: index,
Passed: pointerToBool(passed),
Result: &api.CLIStepResult{
HTTPRequestResult: &result,
},
}
}
}
}

func prettyPrintHTTPTest(test api.HTTPRequestTest, variables map[string]string) string {
if test.StatusCode != nil {
return fmt.Sprintf("Expecting status code: %d", *test.StatusCode)
}
if test.BodyContains != nil {
interpolated := checks.InterpolateVariables(*test.BodyContains, variables)
return fmt.Sprintf("Expecting body to contain: %s", interpolated)
}
if test.BodyContainsNone != nil {
interpolated := checks.InterpolateVariables(*test.BodyContainsNone, variables)
return fmt.Sprintf("Expecting JSON body to not contain: %s", interpolated)
}
if test.HeadersContain != nil {
interpolatedKey := checks.InterpolateVariables(test.HeadersContain.Key, variables)
interpolatedValue := checks.InterpolateVariables(test.HeadersContain.Value, variables)
return fmt.Sprintf("Expecting headers to contain: '%s: %v'", interpolatedKey, interpolatedValue)
}
if test.TrailersContain != nil {
interpolatedKey := checks.InterpolateVariables(test.TrailersContain.Key, variables)
interpolatedValue := checks.InterpolateVariables(test.TrailersContain.Value, variables)
return fmt.Sprintf("Expecting trailers to contain: '%s: %v'", interpolatedKey, interpolatedValue)
}
if test.JSONValue != nil {
var val any
var op any
if test.JSONValue.IntValue != nil {
val = *test.JSONValue.IntValue
} else if test.JSONValue.StringValue != nil {
val = *test.JSONValue.StringValue
} else if test.JSONValue.BoolValue != nil {
val = *test.JSONValue.BoolValue
}
if test.JSONValue.Operator == api.OpEquals {
op = "to be equal to"
} else if test.JSONValue.Operator == api.OpGreaterThan {
op = "to be greater than"
} else if test.JSONValue.Operator == api.OpContains {
op = "contains"
} else if test.JSONValue.Operator == api.OpNotContains {
op = "to not contain"
}
expecting := fmt.Sprintf("Expecting JSON at %v %s %v", test.JSONValue.Path, op, val)
return checks.InterpolateVariables(expecting, variables)
}
return ""
}
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.20.5
v1.20.6