debian-forge-composer/internal/test/helpers.go
Brian C. Lane 3af34fba3f test: Move temporary repo setup into test/helpers.go
This allows it to be shared between the dnf-json tests and weldrcheck.
2020-03-23 21:08:01 +01:00

203 lines
5 KiB
Go

package test
import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
)
type API interface {
ServeHTTP(writer http.ResponseWriter, request *http.Request)
}
func externalRequest(method, path, body string) *http.Response {
client := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", "/run/weldr/api.socket")
},
},
}
req, err := http.NewRequest(method, "http://localhost"+path, bytes.NewReader([]byte(body)))
if err != nil {
panic(err)
}
if method == "POST" {
req.Header.Set("Content-Type", "application/json")
}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
return resp
}
func internalRequest(api API, method, path, body string) *http.Response {
req := httptest.NewRequest(method, path, bytes.NewReader([]byte(body)))
req.Header.Set("Content-Type", "application/json")
resp := httptest.NewRecorder()
api.ServeHTTP(resp, req)
return resp.Result()
}
func SendHTTP(api API, external bool, method, path, body string) *http.Response {
if len(os.Getenv("OSBUILD_COMPOSER_TEST_EXTERNAL")) > 0 {
if !external {
return nil
}
return externalRequest(method, path, body)
} else {
return internalRequest(api, method, path, body)
}
}
// this function serves to drop fields that shouldn't be tested from the unmarshalled json objects
func dropFields(obj interface{}, fields ...string) {
switch v := obj.(type) {
// if the interface type is a map attempt to delete the fields
case map[string]interface{}:
for i, field := range fields {
if _, ok := v[field]; ok {
delete(v, field)
// if the field is found remove it from the fields slice
if len(fields) < i-1 {
fields = append(fields[:i], fields[i+1:]...)
} else {
fields = fields[:i]
}
}
}
// call dropFields on the remaining elements since they may contain a map containing the field
for _, val := range v {
dropFields(val, fields...)
}
// if the type is a list of interfaces call dropFields on each interface
case []interface{}:
for _, element := range v {
dropFields(element, fields...)
}
default:
return
}
}
func TestRoute(t *testing.T, api API, external bool, method, path, body string, expectedStatus int, expectedJSON string, ignoreFields ...string) {
resp := SendHTTP(api, external, method, path, body)
if resp == nil {
t.Skip("This test is for internal testing only")
}
if resp.StatusCode != expectedStatus {
t.Errorf("%s: expected status %v, but got %v", path, expectedStatus, resp.StatusCode)
}
replyJSON, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("%s: could not read response body: %v", path, err)
return
}
if expectedJSON == "" {
if len(replyJSON) != 0 {
t.Errorf("%s: expected no response body, but got:\n%s", path, replyJSON)
}
return
}
var reply, expected interface{}
err = json.Unmarshal(replyJSON, &reply)
if err != nil {
t.Errorf("%s: %v\n%s", path, err, string(replyJSON))
return
}
if expectedJSON == "*" {
return
}
err = json.Unmarshal([]byte(expectedJSON), &expected)
if err != nil {
t.Errorf("%s: expected JSON is invalid: %v", path, err)
return
}
dropFields(reply, ignoreFields...)
dropFields(expected, ignoreFields...)
if diff := cmp.Diff(expected, reply); diff != "" {
t.Errorf("%s: reply != expected:\n reply: %s\nexpected: %s\ndiff: %s", path, strings.TrimSpace(string(replyJSON)), expectedJSON, diff)
return
}
}
func TestNonJsonRoute(t *testing.T, api API, external bool, method, path, body string, expectedStatus int, expectedResponse string) {
response := SendHTTP(api, external, method, path, body)
if response.StatusCode != expectedStatus {
t.Errorf("%s: expected status %v, but got %v", path, expectedStatus, response.StatusCode)
}
responseBodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
t.Errorf("%s: could not read response body: %v", path, err)
}
responseBody := string(responseBodyBytes)
if responseBody != expectedResponse {
t.Errorf("%s: expected response \"%s\", but got \"%s\"", path, expectedResponse, responseBody)
}
}
func IgnoreDates() cmp.Option {
return cmp.Comparer(func(a, b time.Time) bool { return true })
}
func IgnoreUuids() cmp.Option {
return cmp.Comparer(func(a, b uuid.UUID) bool { return true })
}
func Ignore(what string) cmp.Option {
return cmp.FilterPath(func(p cmp.Path) bool { return p.String() == what }, cmp.Ignore())
}
// Create a temporary repository
func SetUpTemporaryRepository() (string, error) {
dir, err := ioutil.TempDir("/tmp", "osbuild-composer-test-")
if err != nil {
return "", err
}
cmd := exec.Command("createrepo_c", path.Join(dir))
err = cmd.Start()
if err != nil {
return "", err
}
err = cmd.Wait()
if err != nil {
return "", err
}
return dir, nil
}
// Remove the temporary repository
func TearDownTemporaryRepository(dir string) error {
return os.RemoveAll(dir)
}