client: Move test setup into unit_test.go
Copy weldrcheck's utils.go into client, switch to using TestState struct to hold global test data. Only build unit_test.go if integration has not been selected. This is in preparation to moving weldrcheck code into client *_test.go files so that the test code can be shared and run against a mock server during unit testing, or against a running WELDR API server during integration testing.
This commit is contained in:
parent
dd3fc6bd7c
commit
b5b5bd96df
3 changed files with 141 additions and 76 deletions
|
|
@ -3,33 +3,16 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/osbuild/osbuild-composer/internal/distro/fedoratest"
|
|
||||||
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
|
|
||||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
|
||||||
"github.com/osbuild/osbuild-composer/internal/weldr"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// socketPath is the path to the temporary unix domain socket
|
|
||||||
var socketPath string
|
|
||||||
|
|
||||||
// socket is the client connection to the server on socketPath
|
|
||||||
var socket *http.Client
|
|
||||||
|
|
||||||
func TestRequest(t *testing.T) {
|
func TestRequest(t *testing.T) {
|
||||||
// Make a request to the status route
|
// Make a request to the status route
|
||||||
resp, err := Request(socket, "GET", "/api/status", "", map[string]string{})
|
resp, err := Request(testState.socket, "GET", "/api/status", "", map[string]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Request good route failed: %v", err)
|
t.Fatalf("Request good route failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +21,7 @@ func TestRequest(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a request to a bad route
|
// Make a request to a bad route
|
||||||
resp, err = Request(socket, "GET", "/invalidroute", "", map[string]string{})
|
resp, err = Request(testState.socket, "GET", "/invalidroute", "", map[string]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Request bad route failed: %v", err)
|
t.Fatalf("Request bad route failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +36,7 @@ func TestRequest(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a request with a bad offset to trigger a JSON response with Status set to 400
|
// Make a request with a bad offset to trigger a JSON response with Status set to 400
|
||||||
resp, err = Request(socket, "GET", "/api/v0/blueprints/list?offset=bad", "", map[string]string{})
|
resp, err = Request(testState.socket, "GET", "/api/v0/blueprints/list?offset=bad", "", map[string]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Request bad offset failed: %v", err)
|
t.Fatalf("Request bad offset failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +73,7 @@ func TestAPIResponse(t *testing.T) {
|
||||||
|
|
||||||
func TestGetRaw(t *testing.T) {
|
func TestGetRaw(t *testing.T) {
|
||||||
// Get raw data
|
// Get raw data
|
||||||
b, resp, err := GetRaw(socket, "GET", "/api/status")
|
b, resp, err := GetRaw(testState.socket, "GET", "/api/status")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GetRaw failed with a client error: %v", err)
|
t.Fatalf("GetRaw failed with a client error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +84,7 @@ func TestGetRaw(t *testing.T) {
|
||||||
t.Fatal("GetRaw returned an empty string")
|
t.Fatal("GetRaw returned an empty string")
|
||||||
}
|
}
|
||||||
// Get an API error
|
// Get an API error
|
||||||
b, resp, err = GetRaw(socket, "GET", "/api/v0/blueprints/list?offset=bad")
|
b, resp, err = GetRaw(testState.socket, "GET", "/api/v0/blueprints/list?offset=bad")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GetRaw bad request failed with a client error: %v", err)
|
t.Fatalf("GetRaw bad request failed with a client error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +103,7 @@ func TestGetRaw(t *testing.T) {
|
||||||
|
|
||||||
func TestGetJSONAll(t *testing.T) {
|
func TestGetJSONAll(t *testing.T) {
|
||||||
// Get all the projects
|
// Get all the projects
|
||||||
b, resp, err := GetJSONAll(socket, "/api/v0/projects/list")
|
b, resp, err := GetJSONAll(testState.socket, "/api/v0/projects/list")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GetJSONAll failed with a client error: %v", err)
|
t.Fatalf("GetJSONAll failed with a client error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +115,7 @@ func TestGetJSONAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run it on a route that doesn't support offset/limit
|
// Run it on a route that doesn't support offset/limit
|
||||||
b, resp, err = GetJSONAll(socket, "/api/status")
|
b, resp, err = GetJSONAll(testState.socket, "/api/status")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("GetJSONAll bad route failed: %v", b)
|
t.Fatalf("GetJSONAll bad route failed: %v", b)
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +126,7 @@ func TestGetJSONAll(t *testing.T) {
|
||||||
|
|
||||||
func TestPostRaw(t *testing.T) {
|
func TestPostRaw(t *testing.T) {
|
||||||
// There are no routes that accept raw POST w/o Content-Type so this ends up testing the error path
|
// There are no routes that accept raw POST w/o Content-Type so this ends up testing the error path
|
||||||
b, resp, err := PostRaw(socket, "/api/v0/blueprints/new", "nobody", nil)
|
b, resp, err := PostRaw(testState.socket, "/api/v0/blueprints/new", "nobody", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("PostRaw bad request failed with a client error: %v", err)
|
t.Fatalf("PostRaw bad request failed with a client error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -164,7 +147,7 @@ func TestPostTOML(t *testing.T) {
|
||||||
blueprint := `name = "test-blueprint"
|
blueprint := `name = "test-blueprint"
|
||||||
description = "TOML test blueprint"
|
description = "TOML test blueprint"
|
||||||
version = "0.0.1"`
|
version = "0.0.1"`
|
||||||
b, resp, err := PostTOML(socket, "/api/v0/blueprints/new", blueprint)
|
b, resp, err := PostTOML(testState.socket, "/api/v0/blueprints/new", blueprint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("PostTOML client failed: %v", err)
|
t.Fatalf("PostTOML client failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +163,7 @@ func TestPostJSON(t *testing.T) {
|
||||||
blueprint := `{"name": "test-blueprint",
|
blueprint := `{"name": "test-blueprint",
|
||||||
"description": "JSON test blueprint",
|
"description": "JSON test blueprint",
|
||||||
"version": "0.0.1"}`
|
"version": "0.0.1"}`
|
||||||
b, resp, err := PostJSON(socket, "/api/v0/blueprints/new", blueprint)
|
b, resp, err := PostJSON(testState.socket, "/api/v0/blueprints/new", blueprint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("PostJSON client failed: %v", err)
|
t.Fatalf("PostJSON client failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -191,52 +174,3 @@ func TestPostJSON(t *testing.T) {
|
||||||
t.Fatalf("PostJSON failed: %#v", string(b))
|
t.Fatalf("PostJSON failed: %#v", string(b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
// Setup the mocked server running on a temporary domain socket
|
|
||||||
tmpdir, err := ioutil.TempDir("", "client_test-")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
|
|
||||||
socketPath = tmpdir + "/client_test.socket"
|
|
||||||
ln, err := net.Listen("unix", socketPath)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a mock API server listening on the temporary socket
|
|
||||||
fixture := rpmmd_mock.BaseFixture()
|
|
||||||
rpm := rpmmd_mock.NewRPMMDMock(fixture)
|
|
||||||
distro := fedoratest.New()
|
|
||||||
arch, err := distro.GetArch("x86_64")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
repos := []rpmmd.RepoConfig{{Id: "test-id", BaseURL: "http://example.com/test/os/test_arch"}}
|
|
||||||
logger := log.New(os.Stdout, "", 0)
|
|
||||||
api := weldr.New(rpm, arch, distro, repos, logger, fixture.Store)
|
|
||||||
server := http.Server{Handler: api}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := server.Serve(ln)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Setup client connection to socketPath
|
|
||||||
socket = &http.Client{
|
|
||||||
Timeout: 60 * time.Second,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
|
||||||
return net.Dial("unix", socketPath)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the tests
|
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
68
internal/client/unit_test.go
Normal file
68
internal/client/unit_test.go
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Package client contains functions for communicating with the API server
|
||||||
|
// Copyright (C) 2020 by Red Hat, Inc.
|
||||||
|
|
||||||
|
// +build !integration
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/osbuild/osbuild-composer/internal/distro/fedoratest"
|
||||||
|
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
|
||||||
|
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||||
|
"github.com/osbuild/osbuild-composer/internal/weldr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hold test state to share between tests
|
||||||
|
var testState *TestState
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
// Setup the mocked server running on a temporary domain socket
|
||||||
|
tmpdir, err := ioutil.TempDir("", "client_test-")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
socketPath := tmpdir + "/client_test.socket"
|
||||||
|
ln, err := net.Listen("unix", socketPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a mock API server listening on the temporary socket
|
||||||
|
fixture := rpmmd_mock.BaseFixture()
|
||||||
|
rpm := rpmmd_mock.NewRPMMDMock(fixture)
|
||||||
|
distro := fedoratest.New()
|
||||||
|
arch, err := distro.GetArch("x86_64")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
repos := []rpmmd.RepoConfig{{Id: "test-id", BaseURL: "http://example.com/test/os/test_arch"}}
|
||||||
|
logger := log.New(os.Stdout, "", 0)
|
||||||
|
api := weldr.New(rpm, arch, distro, repos, logger, fixture.Store)
|
||||||
|
server := http.Server{Handler: api}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := server.Serve(ln)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
testState, err = setUpTestState(socketPath, 60*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: Test setup failed: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the tests
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
63
internal/client/utils.go
Normal file
63
internal/client/utils.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Package weldrcheck contains functions used to run integration tests on a running API server
|
||||||
|
// Copyright (C) 2020 by Red Hat, Inc.
|
||||||
|
|
||||||
|
// nolint: deadcode // These functions are used by the *_test.go code
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestState struct {
|
||||||
|
socket *http.Client
|
||||||
|
apiVersion int
|
||||||
|
repoDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// isStringInSlice returns true if the string is present, false if not
|
||||||
|
// slice must be sorted
|
||||||
|
// TODO decide if this belongs in a more widely useful package location
|
||||||
|
func isStringInSlice(slice []string, s string) bool {
|
||||||
|
i := sort.SearchStrings(slice, s)
|
||||||
|
if i < len(slice) && slice[i] == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUpTestState(socketPath string, timeout time.Duration) (*TestState, error) {
|
||||||
|
var state TestState
|
||||||
|
state.socket = &http.Client{
|
||||||
|
// TODO This may be too short/simple for downloading images
|
||||||
|
Timeout: timeout,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", socketPath)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the server is running
|
||||||
|
status, resp, err := GetStatusV0(state.socket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("status request failed with client error: %s", err)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
return nil, fmt.Errorf("status request failed: %v\n", resp)
|
||||||
|
}
|
||||||
|
apiVersion, e := strconv.Atoi(status.API)
|
||||||
|
if e != nil {
|
||||||
|
state.apiVersion = 0
|
||||||
|
} else {
|
||||||
|
state.apiVersion = apiVersion
|
||||||
|
}
|
||||||
|
fmt.Printf("Running tests against %s %s server using V%d API\n\n", status.Backend, status.Build, state.apiVersion)
|
||||||
|
|
||||||
|
return &state, nil
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue