2022-05-09 11:23:02 -05:00
|
|
|
package test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2022-05-17 07:23:35 -05:00
|
|
|
"context"
|
2022-05-09 11:23:02 -05:00
|
|
|
"encoding/json"
|
2022-05-17 07:23:35 -05:00
|
|
|
"fmt"
|
2022-05-09 11:23:02 -05:00
|
|
|
"io"
|
2022-05-17 07:23:35 -05:00
|
|
|
"net"
|
2022-05-09 11:23:02 -05:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2022-05-17 07:23:35 -05:00
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
2022-05-09 11:23:02 -05:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
type HTTPRequestOpt func(req *http.Request)
|
|
|
|
|
|
|
|
func WithJSONBody(t *testing.T, body interface{}) HTTPRequestOpt {
|
|
|
|
t.Helper()
|
|
|
|
b, err := json.Marshal(body)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("WithJSONBody: %s", err)
|
|
|
|
}
|
|
|
|
return func(req *http.Request) {
|
|
|
|
req.Body = io.NopCloser(bytes.NewBuffer(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithQueryParams(qps map[string]string) HTTPRequestOpt {
|
|
|
|
var vals url.Values = map[string][]string{}
|
|
|
|
for k, v := range qps {
|
|
|
|
vals.Set(k, v)
|
|
|
|
}
|
|
|
|
return func(req *http.Request) {
|
|
|
|
req.URL.RawQuery = vals.Encode()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRequest(t *testing.T, method, path string, opts ...HTTPRequestOpt) *http.Request {
|
|
|
|
t.Helper()
|
|
|
|
req, err := http.NewRequest(method, "http://localhost"+path, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to make new HTTP request %v %v : %v", method, path, err)
|
|
|
|
}
|
|
|
|
for _, o := range opts {
|
|
|
|
o(req)
|
|
|
|
}
|
|
|
|
return req
|
|
|
|
}
|
2022-05-17 07:23:35 -05:00
|
|
|
|
2023-05-04 07:11:23 -05:00
|
|
|
// it's a testing.T but implementable for things which don't have one e.g furl
|
|
|
|
type testInterface interface {
|
|
|
|
Logf(format string, args ...any)
|
|
|
|
Errorf(format string, args ...any)
|
|
|
|
Fatalf(format string, args ...any)
|
|
|
|
TempDir() string
|
|
|
|
}
|
|
|
|
|
2022-05-17 07:23:35 -05:00
|
|
|
// ListenAndServe will listen on a random high-numbered port and attach the given router.
|
|
|
|
// Returns the base URL to send requests to. Call `cancel` to shutdown the server, which will block until it has closed.
|
2023-05-04 07:11:23 -05:00
|
|
|
func ListenAndServe(t testInterface, router http.Handler, withTLS bool) (apiURL string, cancel func()) {
|
2023-05-02 09:03:16 -05:00
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
2022-05-17 07:23:35 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to listen: %s", err)
|
|
|
|
}
|
|
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
|
|
srv := http.Server{}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
srv.Handler = router
|
|
|
|
var err error
|
|
|
|
if withTLS {
|
|
|
|
certFile := filepath.Join(t.TempDir(), "dendrite.cert")
|
|
|
|
keyFile := filepath.Join(t.TempDir(), "dendrite.key")
|
2022-08-23 06:10:29 -05:00
|
|
|
err = NewTLSKey(keyFile, certFile, 1024)
|
2022-05-17 07:23:35 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("failed to make TLS key: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = srv.ServeTLS(listener, certFile, keyFile)
|
|
|
|
} else {
|
|
|
|
err = srv.Serve(listener)
|
|
|
|
}
|
|
|
|
if err != nil && err != http.ErrServerClosed {
|
|
|
|
t.Logf("Listen failed: %s", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
s := ""
|
|
|
|
if withTLS {
|
|
|
|
s = "s"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("http%s://localhost:%d", s, port), func() {
|
|
|
|
_ = srv.Shutdown(context.Background())
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
}
|