From 8e6826e74363f15bf9481a4a04a77173c16aeae6 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 16 Feb 2022 10:35:54 +0100 Subject: [PATCH] Move `OSBuildMetadataToRPMs` and `PackageMetadataToSignature` to osbuild2 Move `OSBuildMetadataToRPMs()` and `PackageMetadataToSignature()` functions from the `rpmmd` package to `osbuild2` package to prevent import cycles while de-duplicating `rpmStageInputs()` function from `stage_inputs.go` of distro definitions. Rename `PackageMetadataToSignature()` to `RPMPackageMetadataToSignature()`, since it takes specifically `RPMPackageMetadata` type as an argument. Adjust affected parts of code (unit tests, cloudapi, worker). Signed-off-by: Tomas Hozza --- cmd/osbuild-worker/jobimpl-koji-finalize.go | 5 +- internal/cloudapi/v2/v2.go | 2 +- internal/osbuild2/rpm_stage.go | 37 ++++++++++ internal/osbuild2/rpm_stage_test.go | 72 ++++++++++++++++++ internal/rpmmd/metadata.go | 35 --------- internal/rpmmd/metadata_test.go | 81 ++------------------- 6 files changed, 119 insertions(+), 113 deletions(-) diff --git a/cmd/osbuild-worker/jobimpl-koji-finalize.go b/cmd/osbuild-worker/jobimpl-koji-finalize.go index 8d4780834..7a21a5892 100644 --- a/cmd/osbuild-worker/jobimpl-koji-finalize.go +++ b/cmd/osbuild-worker/jobimpl-koji-finalize.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" + osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/upload/koji" "github.com/osbuild/osbuild-composer/internal/worker" @@ -127,7 +128,7 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error { // collect packages from stages in build pipelines for _, plName := range buildArgs.PipelineNames.Build { buildPipelineMd := buildArgs.OSBuildOutput.Metadata[plName] - buildRPMs = append(buildRPMs, rpmmd.OSBuildMetadataToRPMs(buildPipelineMd)...) + buildRPMs = append(buildRPMs, osbuild.OSBuildMetadataToRPMs(buildPipelineMd)...) } // this dedupe is usually not necessary since we generally only have // one rpm stage in one build pipeline, but it's not invalid to have @@ -155,7 +156,7 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error { imageRPMs := make([]rpmmd.RPM, 0) for _, plName := range buildArgs.PipelineNames.Payload { payloadPipelineMd := buildArgs.OSBuildOutput.Metadata[plName] - imageRPMs = append(imageRPMs, rpmmd.OSBuildMetadataToRPMs(payloadPipelineMd)...) + imageRPMs = append(imageRPMs, osbuild.OSBuildMetadataToRPMs(payloadPipelineMd)...) } // deduplicate diff --git a/internal/cloudapi/v2/v2.go b/internal/cloudapi/v2/v2.go index b3732c9ab..ddfe7d28e 100644 --- a/internal/cloudapi/v2/v2.go +++ b/internal/cloudapi/v2/v2.go @@ -1013,7 +1013,7 @@ func stagesToPackageMetadata(stages []osbuild.RPMStageMetadata) []PackageMetadat Epoch: rpm.Epoch, Arch: rpm.Arch, Sigmd5: rpm.SigMD5, - Signature: rpmmd.PackageMetadataToSignature(rpm), + Signature: osbuild.RPMPackageMetadataToSignature(rpm), }, ) } diff --git a/internal/osbuild2/rpm_stage.go b/internal/osbuild2/rpm_stage.go index ee2ef2d00..993411006 100644 --- a/internal/osbuild2/rpm_stage.go +++ b/internal/osbuild2/rpm_stage.go @@ -1,5 +1,9 @@ package osbuild2 +import ( + "github.com/osbuild/osbuild-composer/internal/rpmmd" +) + type RPMStageOptions struct { // Array of GPG key contents to import GPGKeys []string `json:"gpgkeys,omitempty"` @@ -70,3 +74,36 @@ type RPMPackageMetadata struct { } func (RPMStageMetadata) isStageMetadata() {} + +func OSBuildMetadataToRPMs(stagesMetadata map[string]StageMetadata) []rpmmd.RPM { + rpms := make([]rpmmd.RPM, 0) + for _, md := range stagesMetadata { + switch metadata := md.(type) { + case *RPMStageMetadata: + for _, pkg := range metadata.Packages { + rpms = append(rpms, rpmmd.RPM{ + Type: "rpm", + Name: pkg.Name, + Epoch: pkg.Epoch, + Version: pkg.Version, + Release: pkg.Release, + Arch: pkg.Arch, + Sigmd5: pkg.SigMD5, + Signature: RPMPackageMetadataToSignature(pkg), + }) + } + default: + continue + } + } + return rpms +} + +func RPMPackageMetadataToSignature(pkg RPMPackageMetadata) *string { + if pkg.SigGPG != "" { + return &pkg.SigGPG + } else if pkg.SigPGP != "" { + return &pkg.SigPGP + } + return nil +} diff --git a/internal/osbuild2/rpm_stage_test.go b/internal/osbuild2/rpm_stage_test.go index fbc73f1e2..a23e55eb2 100644 --- a/internal/osbuild2/rpm_stage_test.go +++ b/internal/osbuild2/rpm_stage_test.go @@ -1,9 +1,13 @@ package osbuild2 import ( + "encoding/json" + "fmt" "testing" + "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewRPMStage(t *testing.T) { @@ -15,3 +19,71 @@ func TestNewRPMStage(t *testing.T) { actualStage := NewRPMStage(&RPMStageOptions{}, &RPMStageInputs{}) assert.Equal(t, expectedStage, actualStage) } + +func Test_OSBuildMetadataToRPMs(t *testing.T) { + raw := ` +{ + "org.osbuild.rpm": { + "packages": [ + { + "name": "python3-pyserial", + "version": "3.4", + "release": "7.fc32", + "epoch": null, + "arch": "noarch", + "sigmd5": "378cb32f9f850b275ac4e04d21e8144b", + "sigpgp": "89023304000108001d162104963a2beb02009608fe67ea4249fd77499570ff3105025f5a272b000a091049fd77499570ff31ccdb0ffe38b95a55ebf3c021526b3cd4f2358c7e23f7767d1f5ce4b7cccef7b33653c6a96a23022313a818fbaf7abeb41837910f0d3ac15664e02838d5939d38ff459aa0076e248728a032d3ae09ddfaec955f941601081a2e3f9bbd49586fd65c1bc1b31685aeb0405687d1791471eab7359ccf00d5584ddef680e99ebc8a4846316391b9baa68ac8ed8ad696ee16fd625d847f8edd92517df3ea6920a46b77b4f119715a0f619f38835d25e0bd0eb5cfad08cd9c796eace6a2b28f4d3dee552e6068255d9748dc2a1906c951e0ba8aed9922ab24e1f659413a06083f8a0bfea56cfff14bddef23bced449f36bcd369da72f90ddf0512e7b0801ba5a0c8eaa8eb0582c630815e992192042cfb0a7c7239f76219197c2fdf18b6553260c105280806d4f037d7b04bdf3da9fd7e9a207db5c71f7e548f4288928f047c989c4cb9cbb8088eec7bd2fa5c252e693f51a3cfc660f666af6a255a5ca0fd2216d5ccd66cbd9c11afa61067d7f615ec8d0dc0c879b5fe633d8c9443f97285da597e4da8a3993af36f0be06acfa9b8058ec70bbc78b876e4c6c5d2108fb05c15a74ba48a3d7ded697cbc1748c228d77d1e0794a41fd5240fa67c3ed745fe47555a47c3d6163d8ce95fd6c2d0d6fa48f8e5b411e571e442109b1cb200d9a8117ee08bfe645f96aca34f7b7559622bbab75143dcad59f126ae0d319e6668ebba417e725638c4febf2e", + "siggpg": "883f0305005f2310139ec3e4c0f7e257e611023e11009f639c5fe64abaa76224dab3a9f70c2714a84c63bd009d1cc184fb4b428dfcd7c3556f4a5f860cc0187740" + }, + { + "name": "libgcc", + "version": "10.0.1", + "release": "0.11.fc32", + "epoch": null, + "arch": "x86_64", + "sigmd5": "84fc907a5047aeebaf8da1642925a417", + "sigpgp": "89023304000108001d162104963a2beb02009608fe67ea4249fd77499570ff3105025f5a272b000a091049fd77499570ff31ccdb0ffe38b95a55ebf3c021526b3cd4f2358c7e23f7767d1f5ce4b7cccef7b33653c6a96a23022313a818fbaf7abeb41837910f0d3ac15664e02838d5939d38ff459aa0076e248728a032d3ae09ddfaec955f941601081a2e3f9bbd49586fd65c1bc1b31685aeb0405687d1791471eab7359ccf00d5584ddef680e99ebc8a4846316391b9baa68ac8ed8ad696ee16fd625d847f8edd92517df3ea6920a46b77b4f119715a0f619f38835d25e0bd0eb5cfad08cd9c796eace6a2b28f4d3dee552e6068255d9748dc2a1906c951e0ba8aed9922ab24e1f659413a06083f8a0bfea56cfff14bddef23bced449f36bcd369da72f90ddf0512e7b0801ba5a0c8eaa8eb0582c630815e992192042cfb0a7c7239f76219197c2fdf18b6553260c105280806d4f037d7b04bdf3da9fd7e9a207db5c71f7e548f4288928f047c989c4cb9cbb8088eec7bd2fa5c252e693f51a3cfc660f666af6a255a5ca0fd2216d5ccd66cbd9c11afa61067d7f615ec8d0dc0c879b5fe633d8c9443f97285da597e4da8a3993af36f0be06acfa9b8058ec70bbc78b876e4c6c5d2108fb05c15a74ba48a3d7ded697cbc1748c228d77d1e0794a41fd5240fa67c3ed745fe47555a47c3d6163d8ce95fd6c2d0d6fa48f8e5b411e571e442109b1cb200d9a8117ee08bfe645f96aca34f7b7559622bbab75143dcad59f126ae0d319e6668ebba417e725638c4febf2e", + "siggpg": null + }, + { + "name": "libgcc-madeup", + "version": "10.0.1", + "release": "0.11.fc32", + "epoch": null, + "arch": "x86_64", + "sigmd5": "84fc907a5047aeebaf8da1642925a418", + "sigpgp": null, + "siggpg": null + } + ] + } +} +` + metadata := new(PipelineMetadata) + err := json.Unmarshal([]byte(raw), metadata) + require.NoError(t, err) + + fmt.Printf("Result: %#v", metadata) + rpms := OSBuildMetadataToRPMs(*metadata) + + require.Len(t, rpms, 3) + + signature1 := "89023304000108001d162104963a2beb02009608fe67ea4249fd77499570ff3105025f5a272b000a091049fd77499570ff31ccdb0ffe38b95a55ebf3c021526b3cd4f2358c7e23f7767d1f5ce4b7cccef7b33653c6a96a23022313a818fbaf7abeb41837910f0d3ac15664e02838d5939d38ff459aa0076e248728a032d3ae09ddfaec955f941601081a2e3f9bbd49586fd65c1bc1b31685aeb0405687d1791471eab7359ccf00d5584ddef680e99ebc8a4846316391b9baa68ac8ed8ad696ee16fd625d847f8edd92517df3ea6920a46b77b4f119715a0f619f38835d25e0bd0eb5cfad08cd9c796eace6a2b28f4d3dee552e6068255d9748dc2a1906c951e0ba8aed9922ab24e1f659413a06083f8a0bfea56cfff14bddef23bced449f36bcd369da72f90ddf0512e7b0801ba5a0c8eaa8eb0582c630815e992192042cfb0a7c7239f76219197c2fdf18b6553260c105280806d4f037d7b04bdf3da9fd7e9a207db5c71f7e548f4288928f047c989c4cb9cbb8088eec7bd2fa5c252e693f51a3cfc660f666af6a255a5ca0fd2216d5ccd66cbd9c11afa61067d7f615ec8d0dc0c879b5fe633d8c9443f97285da597e4da8a3993af36f0be06acfa9b8058ec70bbc78b876e4c6c5d2108fb05c15a74ba48a3d7ded697cbc1748c228d77d1e0794a41fd5240fa67c3ed745fe47555a47c3d6163d8ce95fd6c2d0d6fa48f8e5b411e571e442109b1cb200d9a8117ee08bfe645f96aca34f7b7559622bbab75143dcad59f126ae0d319e6668ebba417e725638c4febf2e" + require.Equal(t, rpmmd.RPM{ + Type: "rpm", + Name: "libgcc", + Version: "10.0.1", + Release: "0.11.fc32", + Epoch: nil, + Arch: "x86_64", + Sigmd5: "84fc907a5047aeebaf8da1642925a417", + Signature: &signature1, + }, rpms[1]) + + // GPG has a priority over PGP + signature0 := "883f0305005f2310139ec3e4c0f7e257e611023e11009f639c5fe64abaa76224dab3a9f70c2714a84c63bd009d1cc184fb4b428dfcd7c3556f4a5f860cc0187740" + require.Equal(t, signature0, *rpms[0].Signature) + + // if neither GPG nor PGP is set, the signature is nil + require.Nil(t, rpms[2].Signature) +} diff --git a/internal/rpmmd/metadata.go b/internal/rpmmd/metadata.go index e0bfd9820..5ea531be3 100644 --- a/internal/rpmmd/metadata.go +++ b/internal/rpmmd/metadata.go @@ -2,8 +2,6 @@ package rpmmd import ( "fmt" - - osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" ) type RPM struct { @@ -26,39 +24,6 @@ func (r RPM) String() string { return fmt.Sprintf("%s-%s%s-%s.%s", r.Name, epoch, r.Version, r.Release, r.Arch) } -func OSBuildMetadataToRPMs(stagesMetadata map[string]osbuild.StageMetadata) []RPM { - rpms := make([]RPM, 0) - for _, md := range stagesMetadata { - switch metadata := md.(type) { - case *osbuild.RPMStageMetadata: - for _, pkg := range metadata.Packages { - rpms = append(rpms, RPM{ - Type: "rpm", - Name: pkg.Name, - Epoch: pkg.Epoch, - Version: pkg.Version, - Release: pkg.Release, - Arch: pkg.Arch, - Sigmd5: pkg.SigMD5, - Signature: PackageMetadataToSignature(pkg), - }) - } - default: - continue - } - } - return rpms -} - -func PackageMetadataToSignature(pkg osbuild.RPMPackageMetadata) *string { - if pkg.SigGPG != "" { - return &pkg.SigGPG - } else if pkg.SigPGP != "" { - return &pkg.SigPGP - } - return nil -} - // Deduplicate a list of RPMs based on NEVRA string func DeduplicateRPMs(rpms []RPM) []RPM { rpmMap := make(map[string]struct{}, len(rpms)) diff --git a/internal/rpmmd/metadata_test.go b/internal/rpmmd/metadata_test.go index b504825c6..7f5786305 100644 --- a/internal/rpmmd/metadata_test.go +++ b/internal/rpmmd/metadata_test.go @@ -1,85 +1,16 @@ -package rpmmd +// a different package is used to prevent import cycles between `rpmmd` and `osbuild2` +package rpmmd_test import ( - "encoding/json" - "fmt" "sort" "testing" "github.com/osbuild/osbuild-composer/internal/common" osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2" + "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func Test_osbuildStagesToRPMs(t *testing.T) { - raw := ` -{ - "org.osbuild.rpm": { - "packages": [ - { - "name": "python3-pyserial", - "version": "3.4", - "release": "7.fc32", - "epoch": null, - "arch": "noarch", - "sigmd5": "378cb32f9f850b275ac4e04d21e8144b", - "sigpgp": "89023304000108001d162104963a2beb02009608fe67ea4249fd77499570ff3105025f5a272b000a091049fd77499570ff31ccdb0ffe38b95a55ebf3c021526b3cd4f2358c7e23f7767d1f5ce4b7cccef7b33653c6a96a23022313a818fbaf7abeb41837910f0d3ac15664e02838d5939d38ff459aa0076e248728a032d3ae09ddfaec955f941601081a2e3f9bbd49586fd65c1bc1b31685aeb0405687d1791471eab7359ccf00d5584ddef680e99ebc8a4846316391b9baa68ac8ed8ad696ee16fd625d847f8edd92517df3ea6920a46b77b4f119715a0f619f38835d25e0bd0eb5cfad08cd9c796eace6a2b28f4d3dee552e6068255d9748dc2a1906c951e0ba8aed9922ab24e1f659413a06083f8a0bfea56cfff14bddef23bced449f36bcd369da72f90ddf0512e7b0801ba5a0c8eaa8eb0582c630815e992192042cfb0a7c7239f76219197c2fdf18b6553260c105280806d4f037d7b04bdf3da9fd7e9a207db5c71f7e548f4288928f047c989c4cb9cbb8088eec7bd2fa5c252e693f51a3cfc660f666af6a255a5ca0fd2216d5ccd66cbd9c11afa61067d7f615ec8d0dc0c879b5fe633d8c9443f97285da597e4da8a3993af36f0be06acfa9b8058ec70bbc78b876e4c6c5d2108fb05c15a74ba48a3d7ded697cbc1748c228d77d1e0794a41fd5240fa67c3ed745fe47555a47c3d6163d8ce95fd6c2d0d6fa48f8e5b411e571e442109b1cb200d9a8117ee08bfe645f96aca34f7b7559622bbab75143dcad59f126ae0d319e6668ebba417e725638c4febf2e", - "siggpg": "883f0305005f2310139ec3e4c0f7e257e611023e11009f639c5fe64abaa76224dab3a9f70c2714a84c63bd009d1cc184fb4b428dfcd7c3556f4a5f860cc0187740" - }, - { - "name": "libgcc", - "version": "10.0.1", - "release": "0.11.fc32", - "epoch": null, - "arch": "x86_64", - "sigmd5": "84fc907a5047aeebaf8da1642925a417", - "sigpgp": "89023304000108001d162104963a2beb02009608fe67ea4249fd77499570ff3105025f5a272b000a091049fd77499570ff31ccdb0ffe38b95a55ebf3c021526b3cd4f2358c7e23f7767d1f5ce4b7cccef7b33653c6a96a23022313a818fbaf7abeb41837910f0d3ac15664e02838d5939d38ff459aa0076e248728a032d3ae09ddfaec955f941601081a2e3f9bbd49586fd65c1bc1b31685aeb0405687d1791471eab7359ccf00d5584ddef680e99ebc8a4846316391b9baa68ac8ed8ad696ee16fd625d847f8edd92517df3ea6920a46b77b4f119715a0f619f38835d25e0bd0eb5cfad08cd9c796eace6a2b28f4d3dee552e6068255d9748dc2a1906c951e0ba8aed9922ab24e1f659413a06083f8a0bfea56cfff14bddef23bced449f36bcd369da72f90ddf0512e7b0801ba5a0c8eaa8eb0582c630815e992192042cfb0a7c7239f76219197c2fdf18b6553260c105280806d4f037d7b04bdf3da9fd7e9a207db5c71f7e548f4288928f047c989c4cb9cbb8088eec7bd2fa5c252e693f51a3cfc660f666af6a255a5ca0fd2216d5ccd66cbd9c11afa61067d7f615ec8d0dc0c879b5fe633d8c9443f97285da597e4da8a3993af36f0be06acfa9b8058ec70bbc78b876e4c6c5d2108fb05c15a74ba48a3d7ded697cbc1748c228d77d1e0794a41fd5240fa67c3ed745fe47555a47c3d6163d8ce95fd6c2d0d6fa48f8e5b411e571e442109b1cb200d9a8117ee08bfe645f96aca34f7b7559622bbab75143dcad59f126ae0d319e6668ebba417e725638c4febf2e", - "siggpg": null - }, - { - "name": "libgcc-madeup", - "version": "10.0.1", - "release": "0.11.fc32", - "epoch": null, - "arch": "x86_64", - "sigmd5": "84fc907a5047aeebaf8da1642925a418", - "sigpgp": null, - "siggpg": null - } - ] - } -} -` - metadata := new(osbuild.PipelineMetadata) - err := json.Unmarshal([]byte(raw), metadata) - require.NoError(t, err) - - fmt.Printf("Result: %#v", metadata) - rpms := OSBuildMetadataToRPMs(*metadata) - - require.Len(t, rpms, 3) - - signature1 := "89023304000108001d162104963a2beb02009608fe67ea4249fd77499570ff3105025f5a272b000a091049fd77499570ff31ccdb0ffe38b95a55ebf3c021526b3cd4f2358c7e23f7767d1f5ce4b7cccef7b33653c6a96a23022313a818fbaf7abeb41837910f0d3ac15664e02838d5939d38ff459aa0076e248728a032d3ae09ddfaec955f941601081a2e3f9bbd49586fd65c1bc1b31685aeb0405687d1791471eab7359ccf00d5584ddef680e99ebc8a4846316391b9baa68ac8ed8ad696ee16fd625d847f8edd92517df3ea6920a46b77b4f119715a0f619f38835d25e0bd0eb5cfad08cd9c796eace6a2b28f4d3dee552e6068255d9748dc2a1906c951e0ba8aed9922ab24e1f659413a06083f8a0bfea56cfff14bddef23bced449f36bcd369da72f90ddf0512e7b0801ba5a0c8eaa8eb0582c630815e992192042cfb0a7c7239f76219197c2fdf18b6553260c105280806d4f037d7b04bdf3da9fd7e9a207db5c71f7e548f4288928f047c989c4cb9cbb8088eec7bd2fa5c252e693f51a3cfc660f666af6a255a5ca0fd2216d5ccd66cbd9c11afa61067d7f615ec8d0dc0c879b5fe633d8c9443f97285da597e4da8a3993af36f0be06acfa9b8058ec70bbc78b876e4c6c5d2108fb05c15a74ba48a3d7ded697cbc1748c228d77d1e0794a41fd5240fa67c3ed745fe47555a47c3d6163d8ce95fd6c2d0d6fa48f8e5b411e571e442109b1cb200d9a8117ee08bfe645f96aca34f7b7559622bbab75143dcad59f126ae0d319e6668ebba417e725638c4febf2e" - require.Equal(t, RPM{ - Type: "rpm", - Name: "libgcc", - Version: "10.0.1", - Release: "0.11.fc32", - Epoch: nil, - Arch: "x86_64", - Sigmd5: "84fc907a5047aeebaf8da1642925a417", - Signature: &signature1, - }, rpms[1]) - - // GPG has a priority over PGP - signature0 := "883f0305005f2310139ec3e4c0f7e257e611023e11009f639c5fe64abaa76224dab3a9f70c2714a84c63bd009d1cc184fb4b428dfcd7c3556f4a5f860cc0187740" - require.Equal(t, signature0, *rpms[0].Signature) - - // if neither GPG nor PGP is set, the signature is nil - require.Nil(t, rpms[2].Signature) -} - func TestRPMDeduplication(t *testing.T) { assert := assert.New(t) // start with metadata, that includes duplicates, convert, then deduplicate @@ -183,12 +114,12 @@ func TestRPMDeduplication(t *testing.T) { testNames := []string{"dupename", "dupename", "package-with-epoch", "python38", "python38", "unique", "vim-minimal", "vim-minimal"} testNamesDeduped := []string{"dupename", "dupename", "package-with-epoch", "python38", "unique", "vim-minimal"} - rpms := OSBuildMetadataToRPMs(metadata) + rpms := osbuild.OSBuildMetadataToRPMs(metadata) // basic sanity checks assert.Len(rpms, 8) - sortedNames := func(rpms []RPM) []string { + sortedNames := func(rpms []rpmmd.RPM) []string { names := make([]string, len(rpms)) for idx, rpm := range rpms { names[idx] = rpm.Name @@ -201,7 +132,7 @@ func TestRPMDeduplication(t *testing.T) { names := sortedNames(rpms) assert.Equal(names, testNames) - deduped := DeduplicateRPMs(rpms) + deduped := rpmmd.DeduplicateRPMs(rpms) assert.Len(deduped, 6) dedupedNames := sortedNames(deduped) assert.Equal(dedupedNames, testNamesDeduped)