Skip to content

Commit

Permalink
draft how tests could look like
Browse files Browse the repository at this point in the history
  • Loading branch information
xrstf committed May 25, 2021
1 parent 8407e43 commit b3b42f1
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 6 deletions.
43 changes: 43 additions & 0 deletions internal/fake/client_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors
SPDX-License-Identifier: Apache-2.0
*/
package fake

import (
"errors"

"github.com/gardener/gardenctl-v2/pkg/target"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type fakeClientProvider struct {
fakeClient client.Client
}

var _ target.ClientProvider = &fakeClientProvider{}

// NewFakeClientProvider returns a new ClientProvider that returns the same
// client for all FromFile/FromBytes calls.
func NewFakeClientProvider(fakeClient client.Client) target.ClientProvider {
return &fakeClientProvider{
fakeClient: fakeClient,
}
}

func (p *fakeClientProvider) FromFile(kubeconfigFile string) (client.Client, error) {
if p.fakeClient == nil {
return nil, errors.New("no fake client configured")
}

return p.fakeClient, nil
}

func (p *fakeClientProvider) FromBytes(kubeconfig []byte) (client.Client, error) {
if p.fakeClient == nil {
return nil, errors.New("no fake client configured")
}

return p.fakeClient, nil
}
93 changes: 93 additions & 0 deletions internal/fake/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors
SPDX-License-Identifier: Apache-2.0
*/

package fake

import (
"github.com/gardener/gardenctl-v2/internal/util"
"github.com/gardener/gardenctl-v2/pkg/target"
)

// Factory implements util.Factory interface
type Factory struct {
// Either set a specific Manager instance, or overwrite the
// individual providers/caches down below.
ManagerImpl target.Manager

// Override these to customize the created manager.
Config *target.Config
ClientProviderImpl target.ClientProvider
KubeconfigCacheImpl target.KubeconfigCache
TargetProviderImpl target.TargetProvider

// Override the clock implementation. Will use a real clock if not set.
ClockImpl util.Clock

// GardenHomeDirectory is the home directory for all gardenctl
// related files. While some files can be explicitly loaded from
// different locations, cache files will always be placed inside
// the garden home.
GardenHomeDirectory string

// ConfigFile is the location of the gardenctlv2 configuration file.
// This can be overriden via a CLI flag and defaults to ~/.garden/gardenctlv2.yaml
// if empty.
ConfigFile string

// TargetFile is the filename where the currently active target is located.
TargetFile string
}

func NewFakeManagerFactory(manager target.Manager) util.Factory {
return &Factory{
ManagerImpl: manager,
}
}

func NewFakeFactory(config *target.Config, clientProvider target.ClientProvider, kubeconfigCache target.KubeconfigCache, targetProvider target.TargetProvider) util.Factory {
if config == nil {
config = &target.Config{}
}

if clientProvider == nil {
clientProvider = NewFakeClientProvider(nil)
}

if kubeconfigCache == nil {
kubeconfigCache = NewFakeKubeconfigCache()
}

if targetProvider == nil {
targetProvider = NewFakeTargetProvider(nil)
}

return &Factory{
Config: config,
ClientProviderImpl: clientProvider,
KubeconfigCacheImpl: kubeconfigCache,
TargetProviderImpl: targetProvider,
}
}

func (f *Factory) Manager() (target.Manager, error) {
if f.ManagerImpl != nil {
return f.ManagerImpl, nil
}

return target.NewManager(f.Config, f.TargetProviderImpl, f.ClientProviderImpl, f.KubeconfigCacheImpl)
}

func (f *Factory) HomeDir() string {
return f.GardenHomeDirectory
}

func (f *Factory) Clock() util.Clock {
if f.ClockImpl != nil {
return f.ClockImpl
}

return &util.RealClock{}
}
45 changes: 45 additions & 0 deletions internal/fake/kubeconfig_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors
SPDX-License-Identifier: Apache-2.0
*/

package fake

import (
"errors"
"fmt"

"github.com/gardener/gardenctl-v2/pkg/target"
)

type fakeKubeconfigCache struct {
kubeconfigs map[string][]byte
}

var _ target.KubeconfigCache = &fakeKubeconfigCache{}

func NewFakeKubeconfigCache() target.KubeconfigCache {
return &fakeKubeconfigCache{
kubeconfigs: map[string][]byte{},
}
}

func (c *fakeKubeconfigCache) key(t target.Target) string {
return fmt.Sprintf("%s;%s;%s;%s", t.GardenName(), t.ProjectName(), t.SeedName(), t.ShootName())
}

func (c *fakeKubeconfigCache) Read(t target.Target) ([]byte, error) {
kubeconfig, ok := c.kubeconfigs[c.key(t)]
if !ok {
return nil, errors.New("could not find kubeconfig")
}

return kubeconfig, nil
}

func (c *fakeKubeconfigCache) Write(t target.Target, kubeconfig []byte) error {
c.kubeconfigs[c.key(t)] = kubeconfig

return nil
}
42 changes: 42 additions & 0 deletions internal/fake/target_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors
SPDX-License-Identifier: Apache-2.0
*/
package fake

import (
"errors"

"github.com/gardener/gardenctl-v2/pkg/target"
)

type fakeTargetProvider struct {
t target.Target
}

var _ target.TargetProvider = &fakeTargetProvider{}

// NewFakeTargetProvider returns a new TargetProvider that
// reads and writes from memory.
func NewFakeTargetProvider(t target.Target) target.TargetProvider {
return &fakeTargetProvider{
t: t,
}
}

// Read returns the current target.
func (p *fakeTargetProvider) Read() (target.Target, error) {
if p.t == nil {
return nil, errors.New("no target set")
}

return p.t, nil
}

// Write takes a target and saves it permanently.
func (p *fakeTargetProvider) Write(t target.Target) error {
p.t = t

return nil
}
30 changes: 26 additions & 4 deletions pkg/cmd/target/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,43 @@ SPDX-License-Identifier: Apache-2.0
package target_test

import (
internalfake "github.com/gardener/gardenctl-v2/internal/fake"
"github.com/gardener/gardenctl-v2/internal/util"
. "github.com/gardener/gardenctl-v2/pkg/cmd/target"
"github.com/gardener/gardenctl-v2/pkg/target"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

var _ = Describe("Command", func() {
It("should print target", func() {
streams, _, out, _ := genericclioptions.NewTestIOStreams()
It("should reject bad options", func() {
streams, _, _, _ := genericclioptions.NewTestIOStreams()
o := NewOptions(streams)
cmd := NewCommand(&util.FactoryImpl{}, o)

Expect(cmd.RunE(cmd, nil)).To(Succeed())
Expect(out.String()).To(ContainSubstring("GitTarget"))
Expect(cmd.RunE(cmd, nil)).NotTo(Succeed())
})

It("should be able to target a garden", func() {
streams, _, _, _ := genericclioptions.NewTestIOStreams()

gardenName := "mygarden"
config := &target.Config{
Gardens: []target.Garden{{
Name: gardenName,
Kubeconfig: "",
}},
}
targetProvider := internalfake.NewFakeTargetProvider(target.NewTarget("", "", "", ""))
factory := internalfake.NewFakeFactory(config, nil, nil, targetProvider)
cmd := NewCommand(factory, NewOptions(streams))

Expect(cmd.RunE(cmd, []string{"garden", gardenName})).To(Succeed())

currentTarget, err := targetProvider.Read()
Expect(err).NotTo(HaveOccurred())
Expect(currentTarget.GardenName()).To(Equal(gardenName))
})
})
15 changes: 13 additions & 2 deletions pkg/cmd/target/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@ var _ = Describe("Options", func() {
It("should validate", func() {
streams, _, _, _ := genericclioptions.NewTestIOStreams()
o := NewOptions(streams)
err := o.Validate()
Expect(err).ToNot(HaveOccurred())
o.Kind = TargetKindGarden
o.TargetName = "foo"

Expect(o.Validate()).To(Succeed())
})

It("should reject invalid kinds", func() {
streams, _, _, _ := genericclioptions.NewTestIOStreams()
o := NewOptions(streams)
o.Kind = TargetKind("not a kind")
o.TargetName = "foo"

Expect(o.Validate()).NotTo(Succeed())
})
})
12 changes: 12 additions & 0 deletions pkg/target/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ type targetImpl struct {

var _ Target = &targetImpl{}

// NewTarget should mostly be used in tests. Regular program code should always
// use the Manager to read/save the current target. This function does not
// perform any validation, so the returned target can be invalid.
func NewTarget(gardenName, projectName, seedName, shootName string) Target {
return &targetImpl{
Garden: gardenName,
Project: projectName,
Seed: seedName,
Shoot: shootName,
}
}

// Validate checks that the target is not malformed and all required
// steps are configured correctly.
func (t *targetImpl) Validate() error {
Expand Down

0 comments on commit b3b42f1

Please sign in to comment.