// Copyright 2018 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"reflect"

	"testing"
)

var (
	expectPluginDetail = PluginDetail{
		ID:     "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078",
		Name:   "tiborvass/sample-volume-plugin",
		Tag:    "latest",
		Active: true,
		Settings: PluginSettings{
			Env:     []string{"DEBUG=0"},
			Args:    nil,
			Devices: nil,
		},
		Config: PluginConfig{
			Description:   "A sample volume plugin for Docker",
			Documentation: "https://docs.docker.com/engine/extend/plugins/",
			Interface: PluginInterface{
				Types:  []string{"docker.volumedriver/1.0"},
				Socket: "plugins.sock",
			},
			Entrypoint: []string{
				"/usr/bin/sample-volume-plugin",
				"/data",
			},
			WorkDir:         "",
			User:            PluginUser{},
			Network:         PluginNetwork{Type: ""},
			Linux:           PluginLinux{Capabilities: nil, AllowAllDevices: false, Devices: nil},
			Mounts:          nil,
			PropagatedMount: "/data",
			Env: []PluginEnv{
				{
					Name:        "DEBUG",
					Description: "If set, prints debug messages",
					Settable:    nil,
					Value:       "0",
				},
			},
			Args: PluginArgs{
				Name:        "args",
				Description: "command line arguments",
				Settable:    nil,
				Value:       []string{},
			},
		},
	}
)

const (
	jsonPluginDetail = `{
    "Id": "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078",
    "Name": "tiborvass/sample-volume-plugin",
    "Tag": "latest",
    "Active": true,
    "Settings": {
      "Env": [
        "DEBUG=0"
      ],
      "Args": null,
      "Devices": null
    },
    "Config": {
      "Description": "A sample volume plugin for Docker",
      "Documentation": "https://docs.docker.com/engine/extend/plugins/",
      "Interface": {
        "Types": [
          "docker.volumedriver/1.0"
        ],
        "Socket": "plugins.sock"
      },
      "Entrypoint": [
        "/usr/bin/sample-volume-plugin",
        "/data"
      ],
      "WorkDir": "",
      "User": {},
      "Network": {
        "Type": ""
      },
      "Linux": {
        "Capabilities": null,
        "AllowAllDevices": false,
        "Devices": null
      },
      "Mounts": null,
      "PropagatedMount": "/data",
      "Env": [
        {
          "Name": "DEBUG",
          "Description": "If set, prints debug messages",
          "Settable": null,
          "Value": "0"
        }
      ],
      "Args": {
        "Name": "args",
        "Description": "command line arguments",
        "Settable": null,
        "Value": []
      }
    }
  }`
)

func TestListPlugins(t *testing.T) {
	t.Parallel()
	jsonPlugins := fmt.Sprintf("[%s]", jsonPluginDetail)
	var expected []PluginDetail
	err := json.Unmarshal([]byte(jsonPlugins), &expected)
	if err != nil {
		t.Fatal(err)
	}
	client := newTestClient(&FakeRoundTripper{message: jsonPlugins, status: http.StatusOK})
	pluginDetails, err := client.ListPlugins(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(pluginDetails, expected) {
		t.Errorf("ListPlugins: Expected %#v. Got %#v.", expected, pluginDetails)
	}
}

func TestListFilteredPlugins(t *testing.T) {
	t.Parallel()
	jsonPlugins := fmt.Sprintf("[%s]", jsonPluginDetail)
	var expected []PluginDetail
	err := json.Unmarshal([]byte(jsonPlugins), &expected)
	if err != nil {
		t.Fatal(err)
	}
	client := newTestClient(&FakeRoundTripper{message: jsonPlugins, status: http.StatusOK})

	pluginDetails, err := client.ListFilteredPlugins(
		ListFilteredPluginsOptions{
			Filters: map[string][]string{
				"capability": {"volumedriver"},
				"enabled":    {"true"},
			},
			Context: context.Background()})
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(pluginDetails, expected) {
		t.Errorf("ListPlugins: Expected %#v. Got %#v.", expected, pluginDetails)
	}
}

func TestListFilteredPluginsFailure(t *testing.T) {
	t.Parallel()
	var tests = []struct {
		status  int
		message string
	}{
		{400, "bad parameter"},
		{500, "internal server error"},
	}
	for _, tt := range tests {
		client := newTestClient(&FakeRoundTripper{message: tt.message, status: tt.status})
		expected := Error{Status: tt.status, Message: tt.message}
		pluginDetails, err := client.ListFilteredPlugins(ListFilteredPluginsOptions{})
		if !reflect.DeepEqual(expected, *err.(*Error)) {
			t.Errorf("Wrong error in ListFilteredPlugins. Want %#v. Got %#v.", expected, err)
		}
		if len(pluginDetails) > 0 {
			t.Errorf("ListFilteredPlugins failure. Expected empty list. Got %#v.", pluginDetails)
		}
	}
}

func TestGetPluginPrivileges(t *testing.T) {
	t.Parallel()
	name := "test_plugin"
	jsonPluginPrivileges := `[ { "Name": "network", "Description": "", "Value": [ "host" ] }]`
	fakeRT := &FakeRoundTripper{message: jsonPluginPrivileges, status: http.StatusNoContent}
	client := newTestClient(fakeRT)
	expected := []PluginPrivilege{
		{
			Name:        "network",
			Description: "",
			Value:       []string{"host"},
		}}
	pluginPrivileges, err := client.GetPluginPrivileges(name, context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(pluginPrivileges, expected) {
		t.Errorf("PluginPrivileges: Expected %#v. Got %#v.", expected, pluginPrivileges)
	}
}

func TestInstallPlugins(t *testing.T) {
	opts := InstallPluginOptions{
		Remote: "", Name: "test",
		Plugins: []PluginPrivilege{
			{
				Name:        "network",
				Description: "",
				Value:       []string{"host"},
			},
		},
		Context: context.Background(),
		Auth:    AuthConfiguration{},
	}

	client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusOK})
	err := client.InstallPlugins(opts)
	if err != nil {
		t.Fatal(err)
	}
}

func TestInspectPlugin(t *testing.T) {
	name := "test_plugin"
	fakeRT := &FakeRoundTripper{message: jsonPluginDetail, status: http.StatusNoContent}
	client := newTestClient(fakeRT)
	pluginPrivileges, err := client.InspectPlugins(name, context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(pluginPrivileges, &expectPluginDetail) {
		t.Errorf("InspectPlugins: Expected %#v. Got %#v.", &expectPluginDetail, pluginPrivileges)
	}
}

func TestRemovePlugin(t *testing.T) {
	opts := RemovePluginOptions{
		Name:    "test_plugin",
		Force:   false,
		Context: context.Background(),
	}
	fakeRT := &FakeRoundTripper{message: jsonPluginDetail, status: http.StatusNoContent}
	client := newTestClient(fakeRT)
	pluginPrivileges, err := client.RemovePlugin(opts)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(pluginPrivileges, &expectPluginDetail) {
		t.Errorf("RemovePlugin: Expected %#v. Got %#v.", &expectPluginDetail, pluginPrivileges)
	}
}

func TestEnablePlugin(t *testing.T) {
	opts := EnablePluginOptions{
		Name:    "test",
		Timeout: 5,
		Context: context.Background(),
	}
	client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusOK})
	err := client.EnablePlugin(opts)
	if err != nil {
		t.Fatal(err)
	}
}

func TestDisablePlugin(t *testing.T) {
	opts := DisablePluginOptions{
		Name:    "test",
		Context: context.Background(),
	}
	client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusOK})
	err := client.DisablePlugin(opts)
	if err != nil {
		t.Fatal(err)
	}
}

func TestCreatePlugin(t *testing.T) {
	opts := CreatePluginOptions{
		Name:    "test",
		Path:    "",
		Context: context.Background(),
	}
	client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusOK})
	_, err := client.CreatePlugin(opts)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPushPlugin(t *testing.T) {
	opts := PushPluginOptions{
		Name:    "test",
		Context: context.Background(),
	}
	client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusOK})
	err := client.PushPlugin(opts)
	if err != nil {
		t.Fatal(err)
	}
}

func TestConfigurePlugin(t *testing.T) {
	opts := ConfigurePluginOptions{
		Name:    "test",
		Envs:    []string{},
		Context: context.Background(),
	}
	client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusOK})
	err := client.ConfigurePlugin(opts)
	if err != nil {
		t.Fatal(err)
	}
}
