From cdc4b1bd53320e5def6a112e2df6ecd547df412a Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 12 May 2020 12:52:09 +0200 Subject: [PATCH] store/json: add some basic tests In the process clarify the code a bit to make it easter to test/understand. Signed-off-by: Tom Gundersen --- internal/store/fixtures.go | 84 +++++++++++++++++++++ internal/store/json.go | 147 +++++++++++++++++++++--------------- internal/store/json_test.go | 134 +++++++++++++++++++++++++++++++- 3 files changed, 305 insertions(+), 60 deletions(-) diff --git a/internal/store/fixtures.go b/internal/store/fixtures.go index 9cb9e4179..8e3573a73 100644 --- a/internal/store/fixtures.go +++ b/internal/store/fixtures.go @@ -105,6 +105,90 @@ func FixtureBase() *Store { return s } +func FixtureFinished() *Store { + var bName = "test" + var b = blueprint.Blueprint{ + Name: bName, + Version: "0.0.0", + Packages: []blueprint.Package{}, + Modules: []blueprint.Package{}, + Groups: []blueprint.Group{}, + Customizations: nil, + } + + var date = time.Date(2019, 11, 27, 13, 19, 0, 0, time.FixedZone("UTC+1", 60*60)) + + var localTarget = &target.Target{ + Uuid: uuid.MustParse("20000000-0000-0000-0000-000000000000"), + Name: "org.osbuild.local", + ImageName: "localimage", + Created: date, + Status: common.IBWaiting, + Options: &target.LocalTargetOptions{}, + } + + var awsTarget = &target.Target{ + Uuid: uuid.MustParse("10000000-0000-0000-0000-000000000000"), + Name: "org.osbuild.aws", + ImageName: "awsimage", + Created: date, + Status: common.IBWaiting, + Options: &target.AWSTargetOptions{ + Region: "frankfurt", + AccessKeyID: "accesskey", + SecretAccessKey: "secretkey", + Bucket: "clay", + Key: "imagekey", + }, + } + + d := fedoratest.New() + arch, err := d.GetArch("x86_64") + if err != nil { + panic("invalid architecture x86_64 for fedoratest") + } + imgType, err := arch.GetImageType("qcow2") + if err != nil { + panic("invalid image type qcow2 for x86_64 @ fedoratest") + } + s := New(nil, arch, nil) + + s.blueprints[bName] = b + s.composes = map[uuid.UUID]Compose{ + uuid.MustParse("30000000-0000-0000-0000-000000000000"): Compose{ + Blueprint: &b, + ImageBuild: ImageBuild{ + QueueStatus: common.IBFinished, + ImageType: imgType, + Targets: []*target.Target{localTarget, awsTarget}, + JobCreated: date, + }, + }, + uuid.MustParse("30000000-0000-0000-0000-000000000001"): Compose{ + Blueprint: &b, + ImageBuild: ImageBuild{ + QueueStatus: common.IBFinished, + ImageType: imgType, + Targets: []*target.Target{localTarget}, + JobCreated: date, + JobStarted: date, + }, + }, + uuid.MustParse("30000000-0000-0000-0000-000000000003"): Compose{ + Blueprint: &b, + ImageBuild: ImageBuild{ + QueueStatus: common.IBFailed, + ImageType: imgType, + Targets: []*target.Target{localTarget, awsTarget}, + JobCreated: date, + JobStarted: date, + JobFinished: date, + }, + }, + } + + return s +} func FixtureEmpty() *Store { var bName = "test" diff --git a/internal/store/json.go b/internal/store/json.go index e3da25ea7..df6f08935 100644 --- a/internal/store/json.go +++ b/internal/store/json.go @@ -107,33 +107,40 @@ func newComposesFromV0(composesStruct composesV0, arch distro.Arch) map[uuid.UUI return composes } +func newImageBuildFromV0(imageBuildStruct imageBuildV0, arch distro.Arch) (ImageBuild, error) { + imgType := imageTypeFromCompatString(imageBuildStruct.ImageType, arch) + if imgType == nil { + // Invalid type strings in serialization format, this may happen + // on upgrades. + return ImageBuild{}, errors.New("invalid Image Type string") + } + return ImageBuild{ + ID: imageBuildStruct.ID, + ImageType: imgType, + Manifest: imageBuildStruct.Manifest, + Targets: imageBuildStruct.Targets, + JobCreated: imageBuildStruct.JobCreated, + JobStarted: imageBuildStruct.JobStarted, + JobFinished: imageBuildStruct.JobFinished, + Size: imageBuildStruct.Size, + JobID: imageBuildStruct.JobID, + QueueStatus: imageBuildStruct.QueueStatus, + }, nil +} + func newComposeFromV0(composeStruct composeV0, arch distro.Arch) (Compose, error) { - c := Compose{ - Blueprint: composeStruct.Blueprint, + if len(composeStruct.ImageBuilds) != 1 { + return Compose{}, errors.New("compose with unsupported number of image builds") } - for _, imgBuild := range composeStruct.ImageBuilds { - imgType := imageTypeFromCompatString(imgBuild.ImageType, arch) - if imgType == nil { - // Invalid type strings in serialization format, this may happen - // on upgrades. - return Compose{}, errors.New("Invalid Image Type string.") - } - ib := ImageBuild{ - ID: imgBuild.ID, - ImageType: imgType, - Manifest: imgBuild.Manifest, - Targets: imgBuild.Targets, - JobCreated: imgBuild.JobCreated, - JobStarted: imgBuild.JobStarted, - JobFinished: imgBuild.JobFinished, - Size: imgBuild.Size, - JobID: imgBuild.JobID, - QueueStatus: imgBuild.QueueStatus, - } - c.ImageBuild = ib - return c, nil + ib, err := newImageBuildFromV0(composeStruct.ImageBuilds[0], arch) + if err != nil { + return Compose{}, err } - return Compose{}, errors.New("No valid image build found in compose.") + bp := composeStruct.Blueprint.DeepCopy() + return Compose{ + Blueprint: &bp, + ImageBuild: ib, + }, nil } func newSourceConfigsFromV0(sourcesStruct sourcesV0) map[string]SourceConfig { @@ -168,7 +175,7 @@ func newChangesFromV0(changesStruct changesV0) map[string]map[string]blueprint.C func newCommitsFromV0(commitsStruct commitsV0) map[string][]string { commits := make(map[string][]string) for name, changes := range commitsStruct { - commits[name] = changes + commits[name] = changes // TODO: deep copy } return commits } @@ -239,30 +246,47 @@ func newStoreFromV0(storeStruct storeV0, arch distro.Arch) *Store { return &store } +func newBlueprintsV0(blueprints map[string]blueprint.Blueprint) blueprintsV0 { + blueprintsStruct := make(blueprintsV0) + for name, blueprint := range blueprints { + blueprintsStruct[name] = blueprint.DeepCopy() + } + return blueprintsStruct +} + +func newWorkspaceV0(workspace map[string]blueprint.Blueprint) workspaceV0 { + workspaceStruct := make(workspaceV0) + for name, blueprint := range workspace { + workspaceStruct[name] = blueprint.DeepCopy() + } + return workspaceStruct +} + +func newComposeV0(compose Compose) composeV0 { + bp := compose.Blueprint.DeepCopy() + return composeV0{ + Blueprint: &bp, + ImageBuilds: []imageBuildV0{ + imageBuildV0{ + ID: compose.ImageBuild.ID, + ImageType: imageTypeToCompatString(compose.ImageBuild.ImageType), + Manifest: compose.ImageBuild.Manifest, + Targets: compose.ImageBuild.Targets, + JobCreated: compose.ImageBuild.JobCreated, + JobStarted: compose.ImageBuild.JobStarted, + JobFinished: compose.ImageBuild.JobFinished, + Size: compose.ImageBuild.Size, + JobID: compose.ImageBuild.JobID, + QueueStatus: compose.ImageBuild.QueueStatus, + }, + }, + } +} + func newComposesV0(composes map[uuid.UUID]Compose) composesV0 { composesStruct := make(composesV0) for composeID, compose := range composes { - c := composeV0{ - Blueprint: compose.Blueprint, - } - imgType := imageTypeToCompatString(compose.ImageBuild.ImageType) - if imgType == "" { - panic("invalid image type: " + compose.ImageBuild.ImageType.Name()) - } - ib := imageBuildV0{ - ID: compose.ImageBuild.ID, - ImageType: imgType, - Manifest: compose.ImageBuild.Manifest, - Targets: compose.ImageBuild.Targets, - JobCreated: compose.ImageBuild.JobCreated, - JobStarted: compose.ImageBuild.JobStarted, - JobFinished: compose.ImageBuild.JobFinished, - Size: compose.ImageBuild.Size, - JobID: compose.ImageBuild.JobID, - QueueStatus: compose.ImageBuild.QueueStatus, - } - c.ImageBuilds = append(c.ImageBuilds, ib) - composesStruct[composeID] = c + composesStruct[composeID] = newComposeV0(compose) } return composesStruct } @@ -302,8 +326,8 @@ func newCommitsV0(commits map[string][]string) commitsV0 { func (store *Store) toStoreV0() *storeV0 { return &storeV0{ - Blueprints: store.blueprints, - Workspace: store.workspace, + Blueprints: newBlueprintsV0(store.blueprints), + Workspace: newWorkspaceV0(store.workspace), Composes: newComposesV0(store.composes), Sources: newSourcesV0(store.sources), Changes: newChangesV0(store.blueprintsChanges), @@ -312,20 +336,25 @@ func (store *Store) toStoreV0() *storeV0 { } var imageTypeCompatMapping = map[string]string{ - "vhd": "Azure", - "ami": "AWS", - "liveiso": "LiveISO", - "openstack": "OpenStack", - "qcow2": "qcow2", - "vmdk": "VMWare", - "ext4-filesystem": "Raw-filesystem", - "partitioned-disk": "Partitioned-disk", - "tar": "Tar", - "test_type": "test_type", + "vhd": "Azure", + "ami": "AWS", + "liveiso": "LiveISO", + "openstack": "OpenStack", + "qcow2": "qcow2", + "vmdk": "VMWare", + "ext4-filesystem": "Raw-filesystem", + "partitioned-disk": "Partitioned-disk", + "tar": "Tar", + "test_type": "test_type", // used only in json_test.go + "test_type_invalid": "test_type_invalid", // used only in json_test.go } func imageTypeToCompatString(imgType distro.ImageType) string { - return imageTypeCompatMapping[imgType.Name()] + imgTypeString, exists := imageTypeCompatMapping[imgType.Name()] + if !exists { + panic("No mapping exists for " + imgType.Name()) + } + return imgTypeString } func imageTypeFromCompatString(input string, arch distro.Arch) distro.ImageType { diff --git a/internal/store/json_test.go b/internal/store/json_test.go index 271d9c9f0..3776f67f3 100644 --- a/internal/store/json_test.go +++ b/internal/store/json_test.go @@ -1,9 +1,13 @@ package store import ( + "reflect" "testing" + "github.com/google/uuid" + "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/distro" + "github.com/osbuild/osbuild-composer/internal/distro/fedoratest" "github.com/osbuild/osbuild-composer/internal/distro/test_distro" ) @@ -53,13 +57,21 @@ func Test_imageTypeFromCompatString(t *testing.T) { want: &test_distro.TestImageType{}, }, { - name: "invalid", + name: "invalid mapping", args: args{ input: "foo", arch: &test_distro.TestArch{}, }, want: nil, }, + { + name: "invalid distro name", + args: args{ + input: "test_type_invalid", + arch: &test_distro.TestArch{}, + }, + want: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -70,3 +82,123 @@ func Test_imageTypeFromCompatString(t *testing.T) { }) } } + +func TestMarshalEmpty(t *testing.T) { + d := fedoratest.New() + arch, err := d.GetArch("x86_64") + if err != nil { + panic("invalid architecture x86_64 for fedoratest") + } + store1 := FixtureEmpty() + storeV0 := store1.toStoreV0() + store2 := newStoreFromV0(*storeV0, arch, nil) + if !reflect.DeepEqual(store1, store2) { + t.Errorf("marshal/unmarshal roundtrip not a noop for empty store: %v != %v", store1, store2) + } +} + +func TestMarshalFinished(t *testing.T) { + d := fedoratest.New() + arch, err := d.GetArch("x86_64") + if err != nil { + panic("invalid architecture x86_64 for fedoratest") + } + store1 := FixtureFinished() + storeV0 := store1.toStoreV0() + store2 := newStoreFromV0(*storeV0, arch, nil) + if !reflect.DeepEqual(store1, store2) { + t.Errorf("marshal/unmarshal roundtrip not a noop for base store: %v != %v", store1, store2) + } +} + +func TestStore_toStoreV0(t *testing.T) { + type fields struct { + blueprints map[string]blueprint.Blueprint + workspace map[string]blueprint.Blueprint + composes map[uuid.UUID]Compose + sources map[string]SourceConfig + blueprintsChanges map[string]map[string]blueprint.Change + blueprintsCommits map[string][]string + } + tests := []struct { + name string + fields fields + want *storeV0 + }{ + { + name: "empty", + fields: fields{}, + want: &storeV0{ + Blueprints: make(blueprintsV0), + Workspace: make(workspaceV0), + Composes: make(composesV0), + Sources: make(sourcesV0), + Changes: make(changesV0), + Commits: make(commitsV0), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + store := &Store{ + blueprints: tt.fields.blueprints, + workspace: tt.fields.workspace, + composes: tt.fields.composes, + sources: tt.fields.sources, + blueprintsChanges: tt.fields.blueprintsChanges, + blueprintsCommits: tt.fields.blueprintsCommits, + } + if got := store.toStoreV0(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Store.toStoreV0() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_newStoreFromV0(t *testing.T) { + type args struct { + storeStruct storeV0 + arch distro.Arch + } + tests := []struct { + name string + args args + want *Store + }{ + { + name: "empty", + args: args{ + storeStruct: storeV0{}, + arch: &test_distro.TestArch{}, + }, + want: New(nil, &test_distro.TestArch{}), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newStoreFromV0(tt.args.storeStruct, tt.args.arch); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newStoreFromV0() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_newCommitsV0(t *testing.T) { + type args struct { + commits map[string][]string + } + tests := []struct { + name string + args args + want commitsV0 + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newCommitsV0(tt.args.commits); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newCommitsV0() = %v, want %v", got, tt.want) + } + }) + } +}