2025-01-29 10:42:21 +00:00
|
|
|
package testserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2025-02-03 18:24:41 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2025-01-31 13:31:23 +00:00
|
|
|
"io"
|
2025-01-29 10:42:21 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2025-02-03 18:24:41 +00:00
|
|
|
"slices"
|
2025-01-29 10:42:21 +00:00
|
|
|
|
2025-01-31 13:31:23 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
2025-01-29 10:42:21 +00:00
|
|
|
"github.com/databricks/cli/internal/testutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Server struct {
|
|
|
|
*httptest.Server
|
|
|
|
Mux *http.ServeMux
|
|
|
|
|
|
|
|
t testutil.TestingT
|
2025-01-31 13:31:23 +00:00
|
|
|
|
2025-02-03 18:24:41 +00:00
|
|
|
RecordRequests bool
|
|
|
|
IncludeReqHeaders []string
|
2025-01-31 13:31:23 +00:00
|
|
|
|
|
|
|
Requests []Request
|
|
|
|
}
|
|
|
|
|
|
|
|
type Request struct {
|
2025-02-03 18:24:41 +00:00
|
|
|
Method string `json:"method"`
|
|
|
|
Path string `json:"path"`
|
|
|
|
Headers map[string]string `json:"headers,omitempty"`
|
|
|
|
Body any `json:"body,omitempty"`
|
2025-01-29 10:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func New(t testutil.TestingT) *Server {
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
server := httptest.NewServer(mux)
|
2025-01-29 15:54:33 +00:00
|
|
|
t.Cleanup(server.Close)
|
2025-01-29 10:42:21 +00:00
|
|
|
|
|
|
|
return &Server{
|
|
|
|
Server: server,
|
|
|
|
Mux: mux,
|
|
|
|
t: t,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-03 18:24:41 +00:00
|
|
|
func (s *Server) HandleUnknown() {
|
|
|
|
s.Handle("/", func(req *http.Request) (any, error) {
|
|
|
|
msg := fmt.Sprintf(`
|
|
|
|
unknown API request received. Please add a handler for this request in
|
|
|
|
your test. You can copy the following snippet in your test.toml file:
|
|
|
|
|
|
|
|
[[Server]]
|
|
|
|
Pattern = %s %s
|
|
|
|
Response = '''
|
|
|
|
<response here>
|
|
|
|
'''`, req.Method, req.URL.Path)
|
|
|
|
|
|
|
|
s.t.Fatalf(msg)
|
|
|
|
return nil, errors.New("unknown API request")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2025-01-29 10:42:21 +00:00
|
|
|
type HandlerFunc func(req *http.Request) (resp any, err error)
|
|
|
|
|
|
|
|
func (s *Server) Handle(pattern string, handler HandlerFunc) {
|
|
|
|
s.Mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
resp, err := handler(r)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2025-01-31 13:31:23 +00:00
|
|
|
if s.RecordRequests {
|
|
|
|
body, err := io.ReadAll(r.Body)
|
|
|
|
assert.NoError(s.t, err)
|
|
|
|
|
2025-02-03 18:24:41 +00:00
|
|
|
headers := make(map[string]string)
|
|
|
|
for k, v := range r.Header {
|
|
|
|
if !slices.Contains(s.IncludeReqHeaders, k) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(v) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
headers[k] = v[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
var reqBody any
|
|
|
|
if len(body) > 0 && body[0] == '{' {
|
|
|
|
reqBody = json.RawMessage(body)
|
|
|
|
} else {
|
|
|
|
reqBody = string(body)
|
|
|
|
}
|
|
|
|
|
2025-01-31 13:31:23 +00:00
|
|
|
s.Requests = append(s.Requests, Request{
|
2025-02-03 18:24:41 +00:00
|
|
|
Method: r.Method,
|
|
|
|
Path: r.URL.Path,
|
|
|
|
Headers: headers,
|
|
|
|
Body: reqBody,
|
2025-01-31 13:31:23 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2025-01-29 10:42:21 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
var respBytes []byte
|
|
|
|
|
|
|
|
respString, ok := resp.(string)
|
|
|
|
if ok {
|
|
|
|
respBytes = []byte(respString)
|
|
|
|
} else {
|
|
|
|
respBytes, err = json.MarshalIndent(resp, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := w.Write(respBytes); err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|