From 495f5b558bd7223c385c5d64702a6bfd3efad7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Tue, 12 Nov 2019 20:14:32 +0100 Subject: [PATCH] Add support for mocking rpmmd We want to test API methods which calls dnf. Unfortunately, calling dnf is expensive operation - it requires network access and downloading a lot of (meta)data. This commit changes the rpmmd implementation so that it can be mocked. --- cmd/osbuild-composer/main.go | 6 +++-- internal/mocks/rpmmd/fixtures.go | 19 +++++++++++++++ internal/mocks/rpmmd/rpmmd_mock.go | 33 +++++++++++++++++++++++++ internal/rpmmd/repository.go | 15 ++++++++++-- internal/weldr/api.go | 8 +++--- internal/weldr/api_test.go | 39 ++++++++++++++++++------------ 6 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 internal/mocks/rpmmd/fixtures.go create mode 100644 internal/mocks/rpmmd/rpmmd_mock.go diff --git a/cmd/osbuild-composer/main.go b/cmd/osbuild-composer/main.go index 504d52741..0088a8cf7 100644 --- a/cmd/osbuild-composer/main.go +++ b/cmd/osbuild-composer/main.go @@ -38,7 +38,9 @@ func main() { Metalink: "https://mirrors.fedoraproject.org/metalink?repo=fedora-30&arch=x86_64", } - packages, err := rpmmd.FetchPackageList([]rpmmd.RepoConfig{repo}) + rpm := rpmmd.NewRPMMD() + + packages, err := rpm.FetchPackageList([]rpmmd.RepoConfig{repo}) if err != nil { panic(err) } @@ -56,7 +58,7 @@ func main() { store := store.New(&stateFile) jobAPI := jobqueue.New(logger, store) - weldrAPI := weldr.New(repo, packages, logger, store) + weldrAPI := weldr.New(rpm, repo, packages, logger, store) go jobAPI.Serve(jobListener) weldrAPI.Serve(weldrListener) diff --git a/internal/mocks/rpmmd/fixtures.go b/internal/mocks/rpmmd/fixtures.go new file mode 100644 index 000000000..3378da9f6 --- /dev/null +++ b/internal/mocks/rpmmd/fixtures.go @@ -0,0 +1,19 @@ +package rpmmd_mock + +import ( + "github.com/osbuild/osbuild-composer/internal/rpmmd" +) + +var BaseFixture = Fixture{ + fetchPackageList{ + rpmmd.PackageList{ + {Name: "package1"}, + {Name: "package2"}, + }, + nil, + }, + depsolve{ + nil, + nil, + }, +} diff --git a/internal/mocks/rpmmd/rpmmd_mock.go b/internal/mocks/rpmmd/rpmmd_mock.go new file mode 100644 index 000000000..a5b08e04a --- /dev/null +++ b/internal/mocks/rpmmd/rpmmd_mock.go @@ -0,0 +1,33 @@ +package rpmmd_mock + +import "github.com/osbuild/osbuild-composer/internal/rpmmd" + +type fetchPackageList struct { + ret rpmmd.PackageList + err error +} +type depsolve struct { + ret []rpmmd.PackageSpec + err error +} + +type Fixture struct { + fetchPackageList + depsolve +} + +type rpmmdMock struct { + Fixture Fixture +} + +func NewRPMMDMock(fixture Fixture) rpmmd.RPMMD { + return &rpmmdMock{Fixture: fixture} +} + +func (r *rpmmdMock) FetchPackageList(repos []rpmmd.RepoConfig) (rpmmd.PackageList, error) { + return r.Fixture.fetchPackageList.ret, r.Fixture.fetchPackageList.err +} + +func (r *rpmmdMock) Depsolve(specs []string, repos []rpmmd.RepoConfig) ([]rpmmd.PackageSpec, error) { + return r.Fixture.depsolve.ret, r.Fixture.depsolve.err +} diff --git a/internal/rpmmd/repository.go b/internal/rpmmd/repository.go index 5a5e958f6..5f5f3690d 100644 --- a/internal/rpmmd/repository.go +++ b/internal/rpmmd/repository.go @@ -39,6 +39,11 @@ type PackageSpec struct { Arch string `json:"arch,omitempty"` } +type RPMMD interface { + FetchPackageList(repos []RepoConfig) (PackageList, error) + Depsolve(specs []string, repos []RepoConfig) ([]PackageSpec, error) +} + func runDNF(command string, arguments interface{}, result interface{}) error { var call = struct { Command string `json:"command"` @@ -80,7 +85,13 @@ func runDNF(command string, arguments interface{}, result interface{}) error { return cmd.Wait() } -func FetchPackageList(repos []RepoConfig) (PackageList, error) { +type rpmmdImpl struct{} + +func NewRPMMD() RPMMD { + return &rpmmdImpl{} +} + +func (*rpmmdImpl) FetchPackageList(repos []RepoConfig) (PackageList, error) { var arguments = struct { Repos []RepoConfig `json:"repos"` }{repos} @@ -92,7 +103,7 @@ func FetchPackageList(repos []RepoConfig) (PackageList, error) { return packages, err } -func Depsolve(specs []string, repos []RepoConfig) ([]PackageSpec, error) { +func (*rpmmdImpl) Depsolve(specs []string, repos []RepoConfig) ([]PackageSpec, error) { var arguments = struct { PackageSpecs []string `json:"package-specs"` Repos []RepoConfig `json:"repos"` diff --git a/internal/weldr/api.go b/internal/weldr/api.go index 2b80d959c..9fa622053 100644 --- a/internal/weldr/api.go +++ b/internal/weldr/api.go @@ -24,6 +24,7 @@ import ( type API struct { store *store.Store + rpmmd rpmmd.RPMMD repo rpmmd.RepoConfig packages rpmmd.PackageList @@ -31,11 +32,12 @@ type API struct { router *httprouter.Router } -func New(repo rpmmd.RepoConfig, packages rpmmd.PackageList, logger *log.Logger, store *store.Store) *API { +func New(rpmmd rpmmd.RPMMD, repo rpmmd.RepoConfig, packages rpmmd.PackageList, logger *log.Logger, store *store.Store) *API { // This needs to be shared with the worker API so that they can communicate with each other // builds := make(chan queue.Build, 200) api := &API{ store: store, + rpmmd: rpmmd, repo: repo, packages: packages, logger: logger, @@ -467,7 +469,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req } if modulesRequested { - project.Dependencies, _ = rpmmd.Depsolve([]string{pkg.Name}, []rpmmd.RepoConfig{api.repo}) + project.Dependencies, _ = api.rpmmd.Depsolve([]string{pkg.Name}, []rpmmd.RepoConfig{api.repo}) } projects = append(projects, project) @@ -610,7 +612,7 @@ func (api *API) blueprintsDepsolveHandler(writer http.ResponseWriter, request *h specs[i] += "-*-*.*" } } - dependencies, _ := rpmmd.Depsolve(specs, []rpmmd.RepoConfig{api.repo}) + dependencies, _ := api.rpmmd.Depsolve(specs, []rpmmd.RepoConfig{api.repo}) blueprints = append(blueprints, entry{blueprint, dependencies}) } diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go index f1b32e3ee..3edf21e0b 100644 --- a/internal/weldr/api_test.go +++ b/internal/weldr/api_test.go @@ -16,6 +16,7 @@ import ( "testing" "time" + "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd" "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/store" "github.com/osbuild/osbuild-composer/internal/weldr" @@ -27,11 +28,6 @@ var repo = rpmmd.RepoConfig{ BaseURL: "http://example.com/test/os", } -var packages = rpmmd.PackageList{ - {Name: "package1"}, - {Name: "package2"}, -} - func externalRequest(method, path, body string) *http.Response { client := http.Client{ Transport: &http.Transport{ @@ -161,6 +157,18 @@ func testRoute(t *testing.T, api *weldr.API, external bool, method, path, body s } } +func createWeldrAPI(fixture rpmmd_mock.Fixture) (*weldr.API, *store.Store) { + s := store.New(nil) + rpm := rpmmd_mock.NewRPMMDMock(fixture) + packageList, err := rpm.FetchPackageList([]rpmmd.RepoConfig{repo}) + + if err != nil { + panic(err) + } + + return weldr.New(rpm, repo, packageList, nil, s), s +} + func TestBasic(t *testing.T) { var cases = []struct { Path string @@ -197,7 +205,7 @@ func TestBasic(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) testRoute(t, api, true, "GET", c.Path, ``, c.ExpectedStatus, c.ExpectedJSON) } } @@ -214,7 +222,7 @@ func TestBlueprintsNew(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) testRoute(t, api, true, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON) } } @@ -231,7 +239,7 @@ func TestBlueprintsWorkspace(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) testRoute(t, api, true, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON) } @@ -252,7 +260,7 @@ func TestBlueprintsInfo(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"test1","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) sendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"test2","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) sendHTTP(api, true, "POST", "/api/v0/blueprints/workspace", `{"name":"test2","description":"Test","packages":[{"name":"systemd","version":"123"}],"version":"0.0.0"}`) @@ -274,7 +282,7 @@ func TestBlueprintsDiff(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) sendHTTP(api, true, "POST", "/api/v0/blueprints/workspace", `{"name":"test","description":"Test","packages":[{"name":"systemd","version":"123"}],"version":"0.0.0"}`) testRoute(t, api, true, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON) @@ -294,7 +302,7 @@ func TestBlueprintsDelete(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) testRoute(t, api, true, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON) sendHTTP(api, true, "DELETE", "/api/v0/blueprints/delete/test", ``) @@ -331,7 +339,7 @@ func TestCompose(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, c.External, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) testRoute(t, api, c.External, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON, c.IgnoreFields...) sendHTTP(api, c.External, "DELETE", "/api/v0/blueprints/delete/test", ``) @@ -355,8 +363,7 @@ func TestComposeQueue(t *testing.T) { } for _, c := range cases { - s := store.New(nil) - api := weldr.New(repo, packages, nil, s) + api, s := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, false, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`) // create job and leave it waiting sendHTTP(api, false, "POST", "/api/v0/compose", `{"blueprint_name": "test","compose_type": "tar","branch": "master"}`) @@ -390,7 +397,7 @@ func TestSourcesNew(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) testRoute(t, api, true, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON) sendHTTP(api, true, "DELETE", "/api/v0/projects/source/delete/fish", ``) } @@ -409,7 +416,7 @@ func TestSourcesDelete(t *testing.T) { } for _, c := range cases { - api := weldr.New(repo, packages, nil, store.New(nil)) + api, _ := createWeldrAPI(rpmmd_mock.BaseFixture) sendHTTP(api, true, "POST", "/api/v0/projects/source/new", `{"name": "fish","url": "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","type": "yum-baseurl","check_ssl": false,"check_gpg": false}`) testRoute(t, api, true, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON) sendHTTP(api, true, "DELETE", "/api/v0/projects/source/delete/fish", ``)