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
23 changes: 11 additions & 12 deletions checks/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import (
)

type HttpTestResult struct {
Err string `json:"-"`
RequestHeaders http.Header `json:"-"`
StatusCode int
Headers map[string]string
BodyString string
Variables map[string]string
Err string `json:"-"`
StatusCode int
ResponseHeaders map[string]string
BodyString string
Variables map[string]string
Request api.LessonDataHTTPTestsRequest
}

func HttpTest(
Expand Down Expand Up @@ -101,11 +101,11 @@ func HttpTest(
}
parseVariables(body, request.ResponseVariables, variables)
responses[i] = HttpTestResult{
RequestHeaders: r.Header,
StatusCode: resp.StatusCode,
Headers: headers,
BodyString: truncateAndStringifyBody(body),
Variables: variables,
StatusCode: resp.StatusCode,
ResponseHeaders: headers,
BodyString: truncateAndStringifyBody(body),
Variables: variables,
Request: request,
}
}
return responses, finalBaseURL
Expand All @@ -118,7 +118,6 @@ func truncateAndStringifyBody(body []byte) string {
bodyString := string(body)
const maxBodyLength = 100000
if len(bodyString) > maxBodyLength {
fmt.Printf("truncating from %v to %v", len(bodyString), maxBodyLength)
bodyString = bodyString[:maxBodyLength]
}
return bodyString
Expand Down
36 changes: 19 additions & 17 deletions client/lessons.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,25 @@ type LessonDataHTTPTests struct {
HttpTests struct {
BaseURL *string
ContainsCompleteDir bool
Requests []struct {
ResponseVariables []ResponseVariable
Tests []HTTPTest
Request struct {
FullURL string // overrides BaseURL and Path if set
Path string
BasicAuth *struct {
Username string
Password string
}
Headers map[string]string
BodyJSON map[string]interface{}
Method string
Actions struct {
DelayRequestByMs *int32
}
}
Requests []LessonDataHTTPTestsRequest
}
}

type LessonDataHTTPTestsRequest struct {
ResponseVariables []ResponseVariable
Tests []HTTPTest
Request struct {
FullURL string // overrides BaseURL and Path if set
Path string
BasicAuth *struct {
Username string
Password string
}
Headers map[string]string
BodyJSON map[string]interface{}
Method string
Actions struct {
DelayRequestByMs *int32
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion render/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

api "github.com/bootdotdev/bootdev/client"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/lipgloss"
)
Expand Down Expand Up @@ -36,6 +37,20 @@ func renderTestHeader(header string, spinner spinner.Model, isFinished bool, isS
return strings.Join(sliced, "\n") + "\n"
}

func renderTestResponseVars(respVars []api.ResponseVariable) string {
var str string
for _, respVar := range respVars {
varStr := gray.Render(fmt.Sprintf(" * Saving `%s` from `%s`", respVar.Name, respVar.Path))
edges := " ├─"
for i := 0; i < lipgloss.Height(varStr)-1; i++ {
edges += "\n │ "
}
str += lipgloss.JoinHorizontal(lipgloss.Top, edges, varStr)
str += "\n"
}
return str
}

func renderTests(tests []testModel, spinner string) string {
var str string
for _, test := range tests {
Expand All @@ -49,7 +64,6 @@ func renderTests(tests []testModel, spinner string) string {
str += lipgloss.JoinHorizontal(lipgloss.Top, edges, testStr)
str += "\n"
}
str += "\n"
return str
}

Expand Down
98 changes: 67 additions & 31 deletions render/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ type doneHttpMsg struct {
}

type startHttpMsg struct {
url string
method string
url string
method string
responseVariables []api.ResponseVariable
}

type resolveHttpMsg struct {
Expand All @@ -33,11 +34,12 @@ type resolveHttpMsg struct {
results *checks.HttpTestResult
}
type httpReqModel struct {
request string
passed *bool
results *checks.HttpTestResult
finished bool
tests []testModel
responseVariables []api.ResponseVariable
request string
passed *bool
results *checks.HttpTestResult
finished bool
tests []testModel
}

type httpRootModel struct {
Expand Down Expand Up @@ -78,7 +80,11 @@ func (m httpRootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Quit

case startHttpMsg:
m.reqs = append(m.reqs, httpReqModel{request: fmt.Sprintf("%s %s", msg.method, msg.url), tests: []testModel{}})
m.reqs = append(m.reqs, httpReqModel{
request: fmt.Sprintf("%s %s", msg.method, msg.url),
tests: []testModel{},
responseVariables: msg.responseVariables,
})
return m, nil

case resolveHttpMsg:
Expand Down Expand Up @@ -115,6 +121,7 @@ func (m httpRootModel) View() string {
for _, req := range m.reqs {
str += renderTestHeader(req.request, m.spinner, req.finished, m.isSubmit, req.passed)
str += renderTests(req.tests, s)
str += renderTestResponseVars(req.responseVariables)
if req.results != nil && m.finalized {
str += printHTTPResult(*req.results)
}
Expand All @@ -129,37 +136,63 @@ func (m httpRootModel) View() string {
}

func printHTTPResult(result checks.HttpTestResult) string {
str := ""
if result.Err != "" {
str += fmt.Sprintf(" Err: %v\n", result.Err)
} else {
if len(result.RequestHeaders) > 0 {
str += " Request Headers: \n"
for k, v := range result.RequestHeaders {
str += fmt.Sprintf(" - %v: %v\n", k, v[0])
return fmt.Sprintf(" Err: %v\n\n", result.Err)
}

str := ""

str += fmt.Sprintf(" Response Status Code: %v\n", result.StatusCode)

filteredHeaders := make(map[string]string)
for respK, respV := range result.ResponseHeaders {
for reqK := range result.Request.Request.Headers {
if strings.ToLower(respK) == strings.ToLower(reqK) {
filteredHeaders[respK] = respV
}
}
str += fmt.Sprintf(" Response Status Code: %v\n", result.StatusCode)
str += " Response Body: \n"
unmarshalled := map[string]interface{}{}
bytes := []byte(result.BodyString)
}

if len(filteredHeaders) > 0 {
str += " Response Headers: \n"
for k, v := range filteredHeaders {
str += fmt.Sprintf(" - %v: %v\n", k, v)
}
}

contentType := http.DetectContentType(bytes)
if contentType == "application/json" || strings.HasPrefix(contentType, "text/") {
err := json.Unmarshal([]byte(result.BodyString), &unmarshalled)
str += " Response Body: \n"
bytes := []byte(result.BodyString)
contentType := http.DetectContentType(bytes)
if contentType == "application/json" || strings.HasPrefix(contentType, "text/") {
var unmarshalled interface{}
err := json.Unmarshal([]byte(result.BodyString), &unmarshalled)
if err == nil {
pretty, err := json.MarshalIndent(unmarshalled, "", " ")
if err == nil {
pretty, err := json.MarshalIndent(unmarshalled, "", " ")
if err == nil {
str += string(pretty)
}
str += string(pretty)
} else {
str += result.BodyString
}
} else {
str += fmt.Sprintf("Binary %s file", contentType)
str += result.BodyString
}
} else {
str += fmt.Sprintf("Binary %s file", contentType)
}
str += "\n"

if len(result.Variables) > 0 {
str += " Variables available: \n"
for k, v := range result.Variables {
if v != "" {
str += fmt.Sprintf(" - %v: %v\n", k, v)
} else {
str += fmt.Sprintf(" - %v: [not found]\n", k)
}
}
}
str += "\n"

return str
}

Expand Down Expand Up @@ -215,11 +248,14 @@ func httpRenderer(
url = req.Request.FullURL
}
ch <- startHttpMsg{
url: checks.InterpolateVariables(url, results[i].Variables),
method: req.Request.Method,
url: checks.InterpolateVariables(url, results[i].Variables),
method: req.Request.Method,
responseVariables: req.ResponseVariables,
}
for _, test := range req.Tests {
ch <- startTestMsg{text: prettyPrintHTTPTest(test, results[i].Variables)}
ch <- startTestMsg{
text: prettyPrintHTTPTest(test, results[i].Variables),
}
}
time.Sleep(500 * time.Millisecond)
for j := range req.Tests {
Expand Down Expand Up @@ -267,7 +303,7 @@ func prettyPrintHTTPTest(test api.HTTPTest, variables map[string]string) string
if test.HeadersContain != nil {
interpolatedKey := checks.InterpolateVariables(test.HeadersContain.Key, variables)
interpolatedValue := checks.InterpolateVariables(test.HeadersContain.Value, variables)
return fmt.Sprintf("Expecting header to contain: '%s: %v'", interpolatedKey, interpolatedValue)
return fmt.Sprintf("Expecting headers to contain: '%s: %v'", interpolatedKey, interpolatedValue)
}
if test.JSONValue != nil {
var val any
Expand Down