package loader

import (
	"testing"

	"github.com/docker/cli/cli/compose/types"
	"github.com/docker/docker/pkg/testutil"
	"github.com/stretchr/testify/assert"
)

func TestParseVolumeAnonymousVolume(t *testing.T) {
	for _, path := range []string{"/path", "/path/foo"} {
		volume, err := parseVolume(path)
		expected := types.ServiceVolumeConfig{Type: "volume", Target: path}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}

func TestParseVolumeAnonymousVolumeWindows(t *testing.T) {
	for _, path := range []string{"C:\\path", "Z:\\path\\foo"} {
		volume, err := parseVolume(path)
		expected := types.ServiceVolumeConfig{Type: "volume", Target: path}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}

func TestParseVolumeTooManyColons(t *testing.T) {
	_, err := parseVolume("/foo:/foo:ro:foo")
	assert.EqualError(t, err, "invalid spec: /foo:/foo:ro:foo: too many colons")
}

func TestParseVolumeShortVolumes(t *testing.T) {
	for _, path := range []string{".", "/a"} {
		volume, err := parseVolume(path)
		expected := types.ServiceVolumeConfig{Type: "volume", Target: path}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}

func TestParseVolumeMissingSource(t *testing.T) {
	for _, spec := range []string{":foo", "/foo::ro"} {
		_, err := parseVolume(spec)
		testutil.ErrorContains(t, err, "empty section between colons")
	}
}

func TestParseVolumeBindMount(t *testing.T) {
	for _, path := range []string{"./foo", "~/thing", "../other", "/foo", "/home/user"} {
		volume, err := parseVolume(path + ":/target")
		expected := types.ServiceVolumeConfig{
			Type:   "bind",
			Source: path,
			Target: "/target",
		}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}

func TestParseVolumeRelativeBindMountWindows(t *testing.T) {
	for _, path := range []string{
		"./foo",
		"~/thing",
		"../other",
		"D:\\path", "/home/user",
	} {
		volume, err := parseVolume(path + ":d:\\target")
		expected := types.ServiceVolumeConfig{
			Type:   "bind",
			Source: path,
			Target: "d:\\target",
		}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}

func TestParseVolumeWithBindOptions(t *testing.T) {
	volume, err := parseVolume("/source:/target:slave")
	expected := types.ServiceVolumeConfig{
		Type:   "bind",
		Source: "/source",
		Target: "/target",
		Bind:   &types.ServiceVolumeBind{Propagation: "slave"},
	}
	assert.NoError(t, err)
	assert.Equal(t, expected, volume)
}

func TestParseVolumeWithBindOptionsWindows(t *testing.T) {
	volume, err := parseVolume("C:\\source\\foo:D:\\target:ro,rprivate")
	expected := types.ServiceVolumeConfig{
		Type:     "bind",
		Source:   "C:\\source\\foo",
		Target:   "D:\\target",
		ReadOnly: true,
		Bind:     &types.ServiceVolumeBind{Propagation: "rprivate"},
	}
	assert.NoError(t, err)
	assert.Equal(t, expected, volume)
}

func TestParseVolumeWithInvalidVolumeOptions(t *testing.T) {
	_, err := parseVolume("name:/target:bogus")
	assert.EqualError(t, err, "invalid spec: name:/target:bogus: unknown option: bogus")
}

func TestParseVolumeWithVolumeOptions(t *testing.T) {
	volume, err := parseVolume("name:/target:nocopy")
	expected := types.ServiceVolumeConfig{
		Type:   "volume",
		Source: "name",
		Target: "/target",
		Volume: &types.ServiceVolumeVolume{NoCopy: true},
	}
	assert.NoError(t, err)
	assert.Equal(t, expected, volume)
}

func TestParseVolumeWithReadOnly(t *testing.T) {
	for _, path := range []string{"./foo", "/home/user"} {
		volume, err := parseVolume(path + ":/target:ro")
		expected := types.ServiceVolumeConfig{
			Type:     "bind",
			Source:   path,
			Target:   "/target",
			ReadOnly: true,
		}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}

func TestParseVolumeWithRW(t *testing.T) {
	for _, path := range []string{"./foo", "/home/user"} {
		volume, err := parseVolume(path + ":/target:rw")
		expected := types.ServiceVolumeConfig{
			Type:     "bind",
			Source:   path,
			Target:   "/target",
			ReadOnly: false,
		}
		assert.NoError(t, err)
		assert.Equal(t, expected, volume)
	}
}
