|
Packit |
63bb0d |
package test
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"bytes"
|
|
Packit |
63bb0d |
"context"
|
|
Packit |
63bb0d |
"encoding/json"
|
|
Packit Service |
509fd4 |
"fmt"
|
|
Packit |
63bb0d |
"io/ioutil"
|
|
Packit |
63bb0d |
"net"
|
|
Packit |
63bb0d |
"net/http"
|
|
Packit |
63bb0d |
"net/http/httptest"
|
|
Packit |
63bb0d |
"os"
|
|
Packit |
63bb0d |
"os/exec"
|
|
Packit |
63bb0d |
"path"
|
|
Packit |
63bb0d |
"testing"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/BurntSushi/toml"
|
|
Packit |
63bb0d |
"github.com/google/go-cmp/cmp"
|
|
Packit |
63bb0d |
"github.com/google/uuid"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/common"
|
|
Packit |
63bb0d |
"github.com/osbuild/osbuild-composer/internal/distro"
|
|
Packit |
63bb0d |
"github.com/stretchr/testify/assert"
|
|
Packit |
63bb0d |
"github.com/stretchr/testify/require"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func externalRequest(method, path, body string) *http.Response {
|
|
Packit |
63bb0d |
client := http.Client{
|
|
Packit |
63bb0d |
Transport: &http.Transport{
|
|
Packit |
63bb0d |
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
|
Packit |
63bb0d |
return net.Dial("unix", "/run/weldr/api.socket")
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
req, err := http.NewRequest(method, "http://localhost"+path, bytes.NewReader([]byte(body)))
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
panic(err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if method == "POST" {
|
|
Packit |
63bb0d |
req.Header.Set("Content-Type", "application/json")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
resp, err := client.Do(req)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
panic(err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return resp
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func internalRequest(api http.Handler, method, path, body string) *http.Response {
|
|
Packit |
63bb0d |
req := httptest.NewRequest(method, path, bytes.NewReader([]byte(body)))
|
|
Packit |
63bb0d |
req.Header.Set("Content-Type", "application/json")
|
|
Packit |
63bb0d |
resp := httptest.NewRecorder()
|
|
Packit |
63bb0d |
api.ServeHTTP(resp, req)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return resp.Result()
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func SendHTTP(api http.Handler, external bool, method, path, body string) *http.Response {
|
|
Packit |
63bb0d |
if len(os.Getenv("OSBUILD_COMPOSER_TEST_EXTERNAL")) > 0 {
|
|
Packit |
63bb0d |
if !external {
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return externalRequest(method, path, body)
|
|
Packit |
63bb0d |
} else {
|
|
Packit |
63bb0d |
return internalRequest(api, method, path, body)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// this function serves to drop fields that shouldn't be tested from the unmarshalled json objects
|
|
Packit |
63bb0d |
func dropFields(obj interface{}, fields ...string) {
|
|
Packit |
63bb0d |
switch v := obj.(type) {
|
|
Packit |
63bb0d |
// if the interface type is a map attempt to delete the fields
|
|
Packit |
63bb0d |
case map[string]interface{}:
|
|
Packit |
63bb0d |
for i, field := range fields {
|
|
Packit |
63bb0d |
if _, ok := v[field]; ok {
|
|
Packit |
63bb0d |
delete(v, field)
|
|
Packit |
63bb0d |
// if the field is found remove it from the fields slice
|
|
Packit |
63bb0d |
if len(fields) < i-1 {
|
|
Packit |
63bb0d |
fields = append(fields[:i], fields[i+1:]...)
|
|
Packit |
63bb0d |
} else {
|
|
Packit |
63bb0d |
fields = fields[:i]
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
// call dropFields on the remaining elements since they may contain a map containing the field
|
|
Packit |
63bb0d |
for _, val := range v {
|
|
Packit |
63bb0d |
dropFields(val, fields...)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
// if the type is a list of interfaces call dropFields on each interface
|
|
Packit |
63bb0d |
case []interface{}:
|
|
Packit |
63bb0d |
for _, element := range v {
|
|
Packit |
63bb0d |
dropFields(element, fields...)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
default:
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func TestRoute(t *testing.T, api http.Handler, external bool, method, path, body string, expectedStatus int, expectedJSON string, ignoreFields ...string) {
|
|
Packit |
63bb0d |
t.Helper()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
resp := SendHTTP(api, external, method, path, body)
|
|
Packit |
63bb0d |
if resp == nil {
|
|
Packit |
63bb0d |
t.Skip("This test is for internal testing only")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
replyJSON, err := ioutil.ReadAll(resp.Body)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: could not read response body", path)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
assert.Equalf(t, expectedStatus, resp.StatusCode, "SendHTTP failed for path %s: %v", path, string(replyJSON))
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if expectedJSON == "" {
|
|
Packit |
63bb0d |
require.Lenf(t, replyJSON, 0, "%s: expected no response body, but got:\n%s", path, replyJSON)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
if expectedJSON == "?" {
|
|
Packit Service |
509fd4 |
return
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
var reply, expected interface{}
|
|
Packit |
63bb0d |
err = json.Unmarshal(replyJSON, &reply)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: json.Unmarshal failed for\n%s", path, string(replyJSON))
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if expectedJSON == "*" {
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
err = json.Unmarshal([]byte(expectedJSON), &expected)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: expected JSON is invalid", path)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
dropFields(reply, ignoreFields...)
|
|
Packit |
63bb0d |
dropFields(expected, ignoreFields...)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
require.Equal(t, expected, reply)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func TestTOMLRoute(t *testing.T, api http.Handler, external bool, method, path, body string, expectedStatus int, expectedTOML string, ignoreFields ...string) {
|
|
Packit |
63bb0d |
t.Helper()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
resp := SendHTTP(api, external, method, path, body)
|
|
Packit |
63bb0d |
if resp == nil {
|
|
Packit |
63bb0d |
t.Skip("This test is for internal testing only")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
replyTOML, err := ioutil.ReadAll(resp.Body)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: could not read response body", path)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
assert.Equalf(t, expectedStatus, resp.StatusCode, "SendHTTP failed for path %s: %v", path, string(replyTOML))
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if expectedTOML == "" {
|
|
Packit |
63bb0d |
require.Lenf(t, replyTOML, 0, "%s: expected no response body, but got:\n%s", path, replyTOML)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var reply, expected interface{}
|
|
Packit |
63bb0d |
err = toml.Unmarshal(replyTOML, &reply)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: json.Unmarshal failed for\n%s", path, string(replyTOML))
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if expectedTOML == "*" {
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
err = toml.Unmarshal([]byte(expectedTOML), &expected)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: expected TOML is invalid", path)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
dropFields(reply, ignoreFields...)
|
|
Packit |
63bb0d |
dropFields(expected, ignoreFields...)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
require.Equal(t, expected, reply)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func TestNonJsonRoute(t *testing.T, api http.Handler, external bool, method, path, body string, expectedStatus int, expectedResponse string) {
|
|
Packit |
63bb0d |
response := SendHTTP(api, external, method, path, body)
|
|
Packit |
63bb0d |
assert.Equalf(t, expectedStatus, response.StatusCode, "%s: status mismatch", path)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
responseBodyBytes, err := ioutil.ReadAll(response.Body)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: could not read response body", path)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
responseBody := string(responseBodyBytes)
|
|
Packit |
63bb0d |
require.Equalf(t, expectedResponse, responseBody, "%s: body mismatch", path)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func IgnoreDates() cmp.Option {
|
|
Packit |
63bb0d |
return cmp.Comparer(func(a, b time.Time) bool { return true })
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func IgnoreUuids() cmp.Option {
|
|
Packit |
63bb0d |
return cmp.Comparer(func(a, b uuid.UUID) bool { return true })
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func Ignore(what string) cmp.Option {
|
|
Packit |
63bb0d |
return cmp.FilterPath(func(p cmp.Path) bool { return p.String() == what }, cmp.Ignore())
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// CompareImageType considers two image type objects equal if and only if the names of their distro/arch/imagetype
|
|
Packit |
63bb0d |
// are. The thinking is that the objects are static, and resolving by these three keys should always give equivalent
|
|
Packit |
63bb0d |
// objects. Whether we actually have object equality, is an implementation detail, so we don't want to rely on that.
|
|
Packit |
63bb0d |
func CompareImageTypes() cmp.Option {
|
|
Packit |
63bb0d |
return cmp.Comparer(func(x, y distro.ImageType) bool {
|
|
Packit |
63bb0d |
return x.Name() == y.Name() &&
|
|
Packit |
63bb0d |
x.Arch().Name() == y.Arch().Name() &&
|
|
Packit |
63bb0d |
x.Arch().Distro().Name() == y.Arch().Distro().Name()
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Create a temporary repository
|
|
Packit |
63bb0d |
func SetUpTemporaryRepository() (string, error) {
|
|
Packit |
63bb0d |
dir, err := ioutil.TempDir("/tmp", "osbuild-composer-test-")
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return "", err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
cmd := exec.Command("createrepo_c", path.Join(dir))
|
|
Packit |
63bb0d |
err = cmd.Start()
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return "", err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
err = cmd.Wait()
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return "", err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return dir, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Remove the temporary repository
|
|
Packit |
63bb0d |
func TearDownTemporaryRepository(dir string) error {
|
|
Packit |
63bb0d |
return os.RemoveAll(dir)
|
|
Packit |
63bb0d |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// GenerateCIArtifactName generates a new identifier for CI artifacts which is based
|
|
Packit Service |
509fd4 |
// on environment variables specified by Jenkins
|
|
Packit Service |
509fd4 |
// note: in case of migration to sth else like Github Actions, change it to whatever variables GH Action provides
|
|
Packit Service |
509fd4 |
func GenerateCIArtifactName(prefix string) (string, error) {
|
|
Packit Service |
509fd4 |
distroCode := os.Getenv("DISTRO_CODE")
|
|
Packit Service |
509fd4 |
branchName := os.Getenv("BRANCH_NAME")
|
|
Packit Service |
509fd4 |
buildId := os.Getenv("BUILD_ID")
|
|
Packit Service |
509fd4 |
if branchName == "" || buildId == "" || distroCode == "" {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("The environment variables must specify BRANCH_NAME, BUILD_ID, and DISTRO_CODE")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
arch := common.CurrentArch()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return fmt.Sprintf("%s%s-%s-%s-%s", prefix, distroCode, arch, branchName, buildId), nil
|
|
Packit Service |
509fd4 |
}
|