asteris-llc/converge

View on GitHub
resource/docker/volume/volume_test.go

Summary

Maintainability
D
2 days
Test Coverage
// Copyright © 2016 Asteris, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !solaris

package volume_test

import (
    "errors"
    "testing"

    "github.com/asteris-llc/converge/helpers/comparison"
    "github.com/asteris-llc/converge/helpers/fakerenderer"
    "github.com/asteris-llc/converge/helpers/logging"
    "github.com/asteris-llc/converge/resource"
    "github.com/asteris-llc/converge/resource/docker/volume"
    dc "github.com/fsouza/go-dockerclient"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
    "github.com/stretchr/testify/require"
    "golang.org/x/net/context"
)

// TestVolumeInterface verifies that Volume implements the resource.Task
// interface
func TestVolumeInterface(t *testing.T) {
    t.Parallel()
    defer logging.HideLogs(t)()

    assert.Implements(t, (*resource.Task)(nil), new(volume.Volume))
}

// TestVolumeCheck tests the Volume.Check function
func TestVolumeCheck(t *testing.T) {
    t.Parallel()
    defer logging.HideLogs(t)()

    t.Run("state: absent", func(t *testing.T) {
        t.Run("volume does not exist", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "absent"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(nil, nil)

            status, err := vol.Check(context.Background(), fakerenderer.New())
            assert.Nil(t, err)
            assert.False(t, status.HasChanges())
            assert.Equal(t, 1, len(status.Diffs()))
            comparison.AssertDiff(t, status.Diffs(), "test-volume", "absent", "absent")
        })

        t.Run("volume exists", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "absent"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(&dc.Volume{Name: "test-volume"}, nil)

            status, err := vol.Check(context.Background(), fakerenderer.New())
            assert.Nil(t, err)
            assert.True(t, status.HasChanges())
            assert.Equal(t, 1, len(status.Diffs()))
            comparison.AssertDiff(t, status.Diffs(), "test-volume", "present", "absent")
        })
    })

    t.Run("state: present", func(t *testing.T) {
        t.Run("volume does not exist", func(t *testing.T) {
            vol := &volume.Volume{
                Name:  "test-volume",
                State: "present",
            }
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(nil, nil)

            status, err := vol.Check(context.Background(), fakerenderer.New())
            assert.Nil(t, err)
            assert.True(t, status.HasChanges())
            assert.Equal(t, 1, len(status.Diffs()))
            comparison.AssertDiff(t, status.Diffs(), "test-volume", "absent", "present")
        })

        t.Run("volume exists", func(t *testing.T) {
            vol := &volume.Volume{
                Name:  "test-volume",
                State: "present",
            }
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(&dc.Volume{Name: "test-volume"}, nil)

            status, err := vol.Check(context.Background(), fakerenderer.New())
            assert.Nil(t, err)
            assert.False(t, status.HasChanges())
            assert.Equal(t, 1, len(status.Diffs()))
            comparison.AssertDiff(t, status.Diffs(), "test-volume", "present", "present")
        })

        t.Run("volume exists, force: true", func(t *testing.T) {
            t.Run("labels", func(t *testing.T) {
                vol := &volume.Volume{
                    Name:   "test-volume",
                    State:  "present",
                    Labels: map[string]string{"key": "val", "test": "val2"},
                    Force:  true,
                }
                c := &mockClient{}
                vol.SetClient(c)
                c.On("FindVolume", "test-volume").Return(&dc.Volume{Name: "test-volume"}, nil)

                status, err := vol.Check(context.Background(), fakerenderer.New())
                assert.Nil(t, err)
                assert.True(t, status.HasChanges())
                assert.True(t, len(status.Diffs()) > 1)
                comparison.AssertDiff(t, status.Diffs(), "test-volume", "present", "present")
                comparison.AssertDiff(t, status.Diffs(), "labels", "", "key=val, test=val2")
            })

            t.Run("driver", func(t *testing.T) {
                vol := &volume.Volume{
                    Name:   "test-volume",
                    State:  "present",
                    Driver: "flocker",
                    Force:  true,
                }
                c := &mockClient{}
                vol.SetClient(c)
                c.On("FindVolume", "test-volume").
                    Return(&dc.Volume{Name: "test-volume", Driver: "local"}, nil)
                vol.SetClient(c)

                status, err := vol.Check(context.Background(), fakerenderer.New())
                assert.Nil(t, err)
                assert.True(t, status.HasChanges())
                assert.True(t, len(status.Diffs()) > 1)
                comparison.AssertDiff(t, status.Diffs(), "test-volume", "present", "present")
                comparison.AssertDiff(t, status.Diffs(), "driver", "local", "flocker")
            })
        })
    })

    t.Run("docker api error", func(t *testing.T) {
        vol := &volume.Volume{Name: "test-volume", State: "present"}
        c := &mockClient{}
        vol.SetClient(c)
        c.On("FindVolume", "test-volume").Return(nil, errors.New("error"))

        status, err := vol.Check(context.Background(), fakerenderer.New())
        require.Error(t, err)
        assert.Equal(t, resource.StatusFatal, status.StatusCode())
    })
}

// TestVolumeApply tests the Volume.Apply function
func TestVolumeApply(t *testing.T) {
    t.Parallel()
    defer logging.HideLogs(t)()

    t.Run("docker find volume error", func(t *testing.T) {
        vol := &volume.Volume{Name: "test-volume"}
        c := &mockClient{}
        vol.SetClient(c)
        c.On("FindVolume", "test-volume").Return(nil, errors.New("error"))

        status, err := vol.Apply(context.Background())
        require.Error(t, err)
        assert.Equal(t, resource.StatusFatal, status.StatusCode())
    })

    t.Run("state: absent", func(t *testing.T) {
        t.Run("volume exists", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "absent"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(&dc.Volume{Name: "test-volume"}, nil)
            c.On("RemoveVolume", mock.AnythingOfType("string")).Return(nil)

            status, err := vol.Apply(context.Background())
            require.NoError(t, err)
            assert.True(t, status.HasChanges())
            c.AssertCalled(t, "RemoveVolume", "test-volume")
        })

        t.Run("volume does not exist", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "absent"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(nil, nil)

            status, err := vol.Apply(context.Background())
            require.NoError(t, err)
            assert.False(t, status.HasChanges())
            c.AssertNotCalled(t, "RemoveVolume", "test-volume")
        })

        t.Run("docker remove volume error", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "absent"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(&dc.Volume{Name: "test-volume"}, nil)
            c.On("RemoveVolume", mock.AnythingOfType("string")).Return(errors.New("test-volume"))

            status, err := vol.Apply(context.Background())
            require.Error(t, err)
            assert.Equal(t, resource.StatusFatal, status.StatusCode())
        })
    })

    t.Run("state: present", func(t *testing.T) {
        t.Run("volume does not exist", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "present"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(nil, nil)
            c.On("CreateVolume", mock.AnythingOfType("docker.CreateVolumeOptions")).
                Return(&dc.Volume{Name: "test-volume"}, nil)

            status, err := vol.Apply(context.Background())
            require.NoError(t, err)
            assert.True(t, status.HasChanges())
            c.AssertCalled(t, "CreateVolume", mock.AnythingOfType("docker.CreateVolumeOptions"))
            c.AssertNotCalled(t, "RemoveVolume", "test-volume")
        })

        t.Run("volume exists, force: false", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "present"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(&dc.Volume{Name: "test-volume"}, nil)

            status, err := vol.Apply(context.Background())
            require.NoError(t, err)
            assert.False(t, status.HasChanges())
            c.AssertNotCalled(t, "CreateVolume", mock.AnythingOfType("docker.CreateVolumeOptions"))
            c.AssertNotCalled(t, "RemoveVolume", "test-volume")
        })

        t.Run("volume exists, force: true", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "present", Force: true}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").
                Return(&dc.Volume{Name: "test-volume", Driver: "flocker"}, nil)
            c.On("RemoveVolume", mock.AnythingOfType("string")).Return(nil)
            c.On("CreateVolume", mock.AnythingOfType("docker.CreateVolumeOptions")).
                Return(&dc.Volume{Name: "test-volume"}, nil)

            status, err := vol.Apply(context.Background())
            require.NoError(t, err)
            assert.True(t, status.HasChanges())
            c.AssertCalled(t, "RemoveVolume", "test-volume")
            c.AssertCalled(t, "CreateVolume", mock.AnythingOfType("docker.CreateVolumeOptions"))
        })

        t.Run("docker create volume error", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "present"}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").Return(nil, nil)
            c.On("CreateVolume", mock.AnythingOfType("docker.CreateVolumeOptions")).
                Return(nil, errors.New("error"))

            status, err := vol.Apply(context.Background())
            require.Error(t, err)
            assert.Equal(t, resource.StatusFatal, status.StatusCode())
        })

        t.Run("docker remove volume error", func(t *testing.T) {
            vol := &volume.Volume{Name: "test-volume", State: "present", Force: true}
            c := &mockClient{}
            vol.SetClient(c)
            c.On("FindVolume", "test-volume").
                Return(&dc.Volume{Name: "test-volume", Driver: "flocker"}, nil)
            c.On("RemoveVolume", mock.AnythingOfType("string")).Return(errors.New("error"))

            status, err := vol.Apply(context.Background())
            require.Error(t, err)
            assert.Equal(t, resource.StatusFatal, status.StatusCode())
        })
    })
}

type mockClient struct {
    mock.Mock
}

func (m *mockClient) FindVolume(name string) (*dc.Volume, error) {
    args := m.Called(name)

    ret := args.Get(0)
    if ret == nil {
        return nil, args.Error(1)
    }

    return ret.(*dc.Volume), args.Error(1)
}

func (m *mockClient) CreateVolume(opts dc.CreateVolumeOptions) (*dc.Volume, error) {
    args := m.Called(opts)
    ret := args.Get(0)
    if ret == nil {
        return nil, args.Error(1)
    }
    return ret.(*dc.Volume), args.Error(1)
}

func (m *mockClient) RemoveVolume(name string) error {
    args := m.Called(name)
    return args.Error(0)
}