This is needed for unit tests, because it wasn't possible to mock the rpmmd module before. This also requires that the checksum is moved to the compose request and evaluated in the endpoint handler instead of push compose. I think it makes sense to have the checksum in the compose request directly. Also a "module platform ID" is required now, but we don't have the "global" distribution any more, so this patch introduces mapping from a distribution to the module platform ID.
133 lines
4.6 KiB
Go
133 lines
4.6 KiB
Go
package rcm_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"github.com/google/uuid"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"regexp"
|
|
"testing"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/distro/fedoratest"
|
|
distro_mock "github.com/osbuild/osbuild-composer/internal/mocks/distro"
|
|
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
|
|
"github.com/osbuild/osbuild-composer/internal/rcm"
|
|
"github.com/osbuild/osbuild-composer/internal/store"
|
|
)
|
|
|
|
type API interface {
|
|
ServeHTTP(writer http.ResponseWriter, request *http.Request)
|
|
}
|
|
|
|
func internalRequest(api API, method, path, body, contentType string) *http.Response {
|
|
req := httptest.NewRequest(method, path, bytes.NewReader([]byte(body)))
|
|
req.Header.Set("Content-Type", contentType)
|
|
resp := httptest.NewRecorder()
|
|
api.ServeHTTP(resp, req)
|
|
|
|
return resp.Result()
|
|
}
|
|
|
|
func TestBasicRcmAPI(t *testing.T) {
|
|
// Test the HTTP API responses
|
|
// This test mainly focuses on HTTP status codes and JSON structures, not necessarily on their content
|
|
|
|
var cases = []struct {
|
|
Method string
|
|
Path string
|
|
Body string
|
|
ContentType string
|
|
ExpectedStatus int
|
|
ExpectedBodyRegex string
|
|
}{
|
|
{"GET", "/v1/compose", ``, "", http.StatusMethodNotAllowed, ``},
|
|
{"POST", "/v1/compose", `{"status":"RUNNING"}`, "application/json", http.StatusBadRequest, ``},
|
|
{"POST", "/v1/compose", `{"status":"RUNNING"}`, "text/plain", http.StatusBadRequest, ``},
|
|
{"POST", "/v1/compose", `{"distribution": "fedora-30", "image_types": ["qcow2"], "architectures":["x86_64"], "repositories": []}`, "application/json", http.StatusBadRequest, ""},
|
|
{"POST", "/v1/compose/111-222-333", `{"status":"RUNNING"}`, "application/json", http.StatusMethodNotAllowed, ``},
|
|
{"GET", "/v1/compose/7802c476-9cd1-41b7-ba81-43c1906bce73", `{"status":"RUNNING"}`, "application/json", http.StatusBadRequest, `{"error_reason":"Compose UUID does not exist"}`},
|
|
}
|
|
|
|
registry := distro_mock.NewRegistry()
|
|
distroStruct := fedoratest.New()
|
|
api := rcm.New(nil, store.New(nil, distroStruct, *registry), rpmmd_mock.NewRPMMDMock(rpmmd_mock.BaseFixture()))
|
|
|
|
for _, c := range cases {
|
|
resp := internalRequest(api, c.Method, c.Path, c.Body, c.ContentType)
|
|
buf := new(bytes.Buffer)
|
|
_, _ = buf.ReadFrom(resp.Body)
|
|
response_body := buf.String()
|
|
if resp.StatusCode != c.ExpectedStatus {
|
|
t.Errorf("%s request to %s should return status code %d but returns %d, response: %s", c.Method, c.Path, c.ExpectedStatus, resp.StatusCode, response_body)
|
|
}
|
|
matched, err := regexp.Match(c.ExpectedBodyRegex, []byte(response_body))
|
|
if err != nil {
|
|
t.Fatalf("Failed to match regex, correct the test definition!")
|
|
}
|
|
if !matched {
|
|
t.Errorf("The response to %s request to %s should match this regex %s but returns %s", c.Method, c.Path, c.ExpectedBodyRegex, response_body)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSubmitCompose(t *testing.T) {
|
|
// Test the most basic use case: Submit a new job and get its status.
|
|
distroStruct := fedoratest.New()
|
|
registry := distro_mock.NewRegistry()
|
|
api := rcm.New(nil, store.New(nil, distroStruct, *registry), rpmmd_mock.NewRPMMDMock(rpmmd_mock.BaseFixture()))
|
|
|
|
var submit_reply struct {
|
|
UUID uuid.UUID `json:"compose_id"`
|
|
}
|
|
var status_reply struct {
|
|
Status string `json:"status,omitempty"`
|
|
ErrorReason string `json:"error_reason,omitempty"`
|
|
}
|
|
|
|
var cases = []struct {
|
|
Method string
|
|
Path string
|
|
Body string
|
|
ContentType string
|
|
}{
|
|
{
|
|
"POST",
|
|
"/v1/compose",
|
|
`{"distribution": "fedora-30",
|
|
"image_types": ["qcow2"],
|
|
"architectures":["x86_64"],
|
|
"repositories": [{
|
|
"url": "http://download.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/"
|
|
}]}`,
|
|
"application/json",
|
|
},
|
|
}
|
|
|
|
for n, c := range cases {
|
|
// Submit job
|
|
t.Logf("RCM API submit compose, case %d\n", n)
|
|
resp := internalRequest(api, c.Method, c.Path, c.Body, c.ContentType)
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatal("Failed to call /v1/compose, HTTP status code:", resp.StatusCode)
|
|
}
|
|
decoder := json.NewDecoder(resp.Body)
|
|
decoder.DisallowUnknownFields()
|
|
err := decoder.Decode(&submit_reply)
|
|
if err != nil {
|
|
t.Fatal("Failed to decode response to /v1/compose:", err)
|
|
}
|
|
// Get the status
|
|
t.Log("RCM API get status")
|
|
resp = internalRequest(api, "GET", "/v1/compose/"+submit_reply.UUID.String(), "", "")
|
|
decoder = json.NewDecoder(resp.Body)
|
|
decoder.DisallowUnknownFields()
|
|
err = decoder.Decode(&status_reply)
|
|
if err != nil {
|
|
t.Fatal("Failed to decode response to /v1/compose/UUID:", err)
|
|
}
|
|
if status_reply.ErrorReason != "" {
|
|
t.Error("Failed to get compose status, reason:", status_reply.ErrorReason)
|
|
}
|
|
}
|
|
}
|