// Copyright 2016 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 testing

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strings"
	"testing"

	"github.com/docker/docker/api/types/swarm"
	docker "github.com/fsouza/go-dockerclient"
)

func TestSwarmInit(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/init", bytes.NewReader(nil))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmInit: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var id string
	err = json.Unmarshal(recorder.Body.Bytes(), &id)
	if err != nil {
		t.Fatalf("SwarmInit: got error. %s", err.Error())
	}
	if id == "" {
		t.Fatal("SwarmInit: id not found")
	}
	if server.swarm == nil {
		t.Fatalf("SwarmInit: expected swarm to be set.")
	}
	if len(server.nodes) != 1 {
		t.Fatalf("SwarmInit: expected node len to be 1, got: %d", len(server.nodes))
	}
	if server.nodes[0].ManagerStatus.Addr != server.SwarmAddress() {
		t.Fatalf("SwarmInit: expected current node to have addr %q, got: %q", server.SwarmAddress(), server.nodes[0].ManagerStatus.Addr)
	}
	if !server.nodes[0].ManagerStatus.Leader {
		t.Fatalf("SwarmInit: expected current node to be leader")
	}
}

func TestSwarmInitDynamicAdvertiseAddrPort(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	data := `{"ListenAddr": "127.0.0.1:0", "AdvertiseAddr": "localhost"}`
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/init", strings.NewReader(data))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmInit: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.nodes) != 1 {
		t.Fatalf("SwarmInit: expected node len to be 1, got: %d", len(server.nodes))
	}
	_, port, _ := net.SplitHostPort(server.SwarmAddress())
	expectedAddr := fmt.Sprintf("localhost:%s", port)
	if server.nodes[0].ManagerStatus.Addr != expectedAddr {
		t.Fatalf("SwarmInit: expected current node to have addr %q, got: %q", expectedAddr, server.nodes[0].ManagerStatus.Addr)
	}
}

func TestSwarmInitAlreadyInSwarm(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	server.swarm = &swarm.Swarm{}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/init", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotAcceptable {
		t.Fatalf("SwarmInit: wrong status. Want %d. Got %d.", http.StatusNotAcceptable, recorder.Code)
	}
}

func TestSwarmJoinNoBody(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/join", bytes.NewReader(nil))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusInternalServerError {
		t.Fatalf("SwarmJoin: wrong status. Want %d. Got %d.", http.StatusInternalServerError, recorder.Code)
	}
	if server.swarm != nil {
		t.Fatalf("SwarmJoin: expected swarm not to be set.")
	}
}

func TestSwarmJoin(t *testing.T) {
	server1, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server1.Stop()
	server2, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server2.Stop()
	data, err := json.Marshal(swarm.InitRequest{})
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/init", bytes.NewReader(data))
	server1.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmJoin: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	data, err = json.Marshal(swarm.JoinRequest{
		RemoteAddrs: []string{server1.SwarmAddress()},
	})
	if err != nil {
		t.Fatal(err)
	}
	recorder = httptest.NewRecorder()
	request, _ = http.NewRequest(http.MethodPost, "/swarm/join", bytes.NewReader(data))
	server2.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmJoin: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if server1.swarm == nil || server2.swarm == nil {
		t.Fatalf("SwarmJoin: expected swarm to be set.")
	}
	if len(server1.nodes) != 2 {
		t.Fatalf("SwarmJoin: expected node len to be 2, got: %d", len(server1.nodes))
	}
	if server1.nodes[0].ManagerStatus.Addr != server1.SwarmAddress() {
		t.Fatalf("SwarmJoin: expected nodes[0] to have addr %q, got: %q", server1.SwarmAddress(), server1.nodes[0].ManagerStatus.Addr)
	}
	if server1.nodes[1].ManagerStatus.Leader {
		t.Fatalf("SwarmInit: expected nodes[1] not to be leader")
	}
	if server1.nodes[1].ManagerStatus.Addr != server2.SwarmAddress() {
		t.Fatalf("SwarmJoin: expected nodes[1] to have addr %q, got: %q", server2.SwarmAddress(), server1.nodes[1].ManagerStatus.Addr)
	}
	if !reflect.DeepEqual(server1.nodes, server2.nodes) {
		t.Fatalf("SwarmJoin: expected nodes to be equal in server1 and server2, got:\n%#v\n%#v", server1.nodes, server2.nodes)
	}
}

func TestSwarmJoinWithService(t *testing.T) {
	server1, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server1.Stop()
	server2, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server2.Stop()
	data, err := json.Marshal(swarm.InitRequest{})
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/init", bytes.NewReader(data))
	server1.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmJoin: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	serviceCreateOpts := docker.CreateServiceOptions{
		ServiceSpec: swarm.ServiceSpec{
			TaskTemplate: swarm.TaskSpec{
				ContainerSpec: &swarm.ContainerSpec{
					Image: "test/test",
				},
			},
		},
	}
	buf, err := json.Marshal(serviceCreateOpts)
	if err != nil {
		t.Fatalf("ServiceCreate error: %s", err.Error())
	}
	recorder = httptest.NewRecorder()
	request, _ = http.NewRequest(http.MethodPost, "/services/create", bytes.NewBuffer(buf))
	server1.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmJoin: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	data, err = json.Marshal(swarm.JoinRequest{
		RemoteAddrs: []string{server1.SwarmAddress()},
	})
	if err != nil {
		t.Fatal(err)
	}
	recorder = httptest.NewRecorder()
	request, _ = http.NewRequest(http.MethodPost, "/swarm/join", bytes.NewReader(data))
	server2.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmJoin: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server1.services) != len(server2.services) {
		t.Fatalf("SwarmJoin: expected len services to be equal in server1 and server2, got:\n%#v\n%#v", len(server1.services), len(server2.services))
	}
	if !compareServices(server1.services[0], server2.services[0]) {
		t.Fatalf("SwarmJoin: expected services to be equal in server1 and server2, got:\n%#v\n%#v", server1.services[0], server2.services[0])
	}
	if len(server1.tasks) != len(server2.tasks) {
		t.Fatalf("SwarmJoin: expected len tasks to be equal in server1 and server2, got:\n%#v\n%#v", len(server1.tasks), len(server2.tasks))
	}
	if !compareTasks(server1.tasks[0], server2.tasks[0]) {
		t.Fatalf("SwarmJoin: expected tasks to be equal in server1 and server2, got:\n%#v\n%#v", server1.tasks[0], server2.tasks[0])
	}
}

func TestSwarmJoinAlreadyInSwarm(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	server.swarm = &swarm.Swarm{}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/join", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotAcceptable {
		t.Fatalf("SwarmJoin: wrong status. Want %d. Got %d.", http.StatusNotAcceptable, recorder.Code)
	}
}

func TestSwarmLeave(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	server.swarm = &swarm.Swarm{}
	server.swarmServer, _ = newSwarmServer(server, "127.0.0.1:0")
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/leave", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmLeave: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if server.swarm != nil {
		t.Fatalf("SwarmLeave: expected swarm to be nil. Got %+v.", server.swarm)
	}
}

func TestSwarmLeaveNotInSwarm(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/leave", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotAcceptable {
		t.Fatalf("SwarmLeave: wrong status. Want %d. Got %d.", http.StatusNotAcceptable, recorder.Code)
	}
	if server.swarm != nil {
		t.Fatalf("SwarmLeave: expected swarm to be nil. Got %+v.", server.swarm)
	}
}

func TestSwarmInspect(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	expected := &swarm.Swarm{
		ClusterInfo: swarm.ClusterInfo{
			ID: "swarm-id",
		},
	}
	server.swarm = expected
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/swarm", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("SwarmInspect: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var swarmInspect *swarm.Swarm
	err = json.Unmarshal(recorder.Body.Bytes(), &swarmInspect)
	if err != nil {
		t.Fatalf("SwarmInspect: got error. %s", err.Error())
	}
	if expected.ClusterInfo.ID != swarmInspect.ClusterInfo.ID {
		t.Fatalf("SwarmInspect: wrong response. Want %+v. Got %+v.", expected, swarmInspect)
	}
}

func TestSwarmInspectNotInSwarm(t *testing.T) {
	server, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/swarm", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotAcceptable {
		t.Fatalf("SwarmInspect: wrong status. Want %d. Got %d.", http.StatusNotAcceptable, recorder.Code)
	}
}

func TestServiceCreate(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	serviceCreateOpts := docker.CreateServiceOptions{
		ServiceSpec: swarm.ServiceSpec{
			Annotations: swarm.Annotations{
				Name: "test",
			},
			TaskTemplate: swarm.TaskSpec{
				ContainerSpec: &swarm.ContainerSpec{
					Image:   "test/test",
					Command: []string{"sh"},
					Args:    []string{"--test"},
					Env:     []string{"ENV=1"},
					User:    "test",
				},
			},
			EndpointSpec: &swarm.EndpointSpec{
				Mode: swarm.ResolutionModeVIP,
				Ports: []swarm.PortConfig{{
					Protocol:      swarm.PortConfigProtocolTCP,
					TargetPort:    uint32(80),
					PublishedPort: uint32(80),
				}},
			},
		},
	}
	buf, err := json.Marshal(serviceCreateOpts)
	if err != nil {
		t.Fatalf("ServiceCreate error: %s", err.Error())
	}
	var params io.Reader = bytes.NewBuffer(buf)
	request, _ := http.NewRequest(http.MethodPost, "/services/create", params)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceCreate: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.services) != 1 || len(server.tasks) != 1 || len(server.containers) != 1 {
		t.Fatalf("ServiceCreate: wrong item count. Want 1. Got services: %d, tasks: %d, containers: %d.", len(server.services), len(server.tasks), len(server.containers))
	}
	cont := getContainer(server)
	expectedContainer := &docker.Container{
		ID:      cont.ID,
		Created: cont.Created,
		Image:   "test/test",
		Name:    "test-0",
		Config: &docker.Config{
			Entrypoint: []string{"sh"},
			Cmd:        []string{"--test"},
			Env:        []string{"ENV=1"},
		},
		HostConfig: &docker.HostConfig{},
		State: docker.State{
			Running:   true,
			StartedAt: cont.State.StartedAt,
			Pid:       cont.State.Pid,
			ExitCode:  0,
		},
	}
	if !reflect.DeepEqual(cont, expectedContainer) {
		t.Fatalf("ServiceCreate: wrong cont. Want\n%#v\nGot\n%#v", expectedContainer, cont)
	}
	srv := server.services[0]
	expectedService := &swarm.Service{
		ID:   srv.ID,
		Spec: serviceCreateOpts.ServiceSpec,
		Endpoint: swarm.Endpoint{
			Spec:  *serviceCreateOpts.ServiceSpec.EndpointSpec,
			Ports: []swarm.PortConfig{{Protocol: "tcp", TargetPort: 80, PublishedPort: 80}},
		},
	}
	if !reflect.DeepEqual(srv, expectedService) {
		t.Fatalf("ServiceCreate: wrong service. Want\n%#v\nGot\n%#v", expectedService, srv)
	}
	task := server.tasks[0]
	expectedTask := &swarm.Task{
		ID:        task.ID,
		ServiceID: srv.ID,
		NodeID:    server.nodes[0].ID,
		Status: swarm.TaskStatus{
			State: swarm.TaskStateReady,
			ContainerStatus: &swarm.ContainerStatus{
				ContainerID: cont.ID,
			},
		},
		DesiredState: swarm.TaskStateReady,
		Spec:         serviceCreateOpts.ServiceSpec.TaskTemplate,
	}
	if !reflect.DeepEqual(task, expectedTask) {
		t.Fatalf("ServiceCreate: wrong task. Want\n%#v\nGot\n%#v", expectedTask, task)
	}
}

func TestServiceCreateDynamicPort(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	serviceCreateOpts := docker.CreateServiceOptions{
		ServiceSpec: swarm.ServiceSpec{
			Annotations: swarm.Annotations{
				Name: "test",
			},
			TaskTemplate: swarm.TaskSpec{
				ContainerSpec: &swarm.ContainerSpec{
					Image:   "test/test",
					Command: []string{"sh"},
					Args:    []string{"--test"},
					Env:     []string{"ENV=1"},
					User:    "test",
				},
			},
			EndpointSpec: &swarm.EndpointSpec{
				Mode: swarm.ResolutionModeVIP,
				Ports: []swarm.PortConfig{{
					Protocol:      swarm.PortConfigProtocolTCP,
					TargetPort:    uint32(80),
					PublishedPort: uint32(0),
				}},
			},
		},
	}
	buf, err := json.Marshal(serviceCreateOpts)
	if err != nil {
		t.Fatalf("ServiceCreate error: %s", err.Error())
	}
	var params io.Reader = bytes.NewBuffer(buf)
	request, _ := http.NewRequest(http.MethodPost, "/services/create", params)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceCreate: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.services) != 1 || len(server.tasks) != 1 || len(server.containers) != 1 {
		t.Fatalf("ServiceCreate: wrong item count. Want 1. Got services: %d, tasks: %d, containers: %d.", len(server.services), len(server.tasks), len(server.containers))
	}
	srv := server.services[0]
	expectedService := &swarm.Service{
		ID:   srv.ID,
		Spec: serviceCreateOpts.ServiceSpec,
		Endpoint: swarm.Endpoint{
			Spec:  *serviceCreateOpts.ServiceSpec.EndpointSpec,
			Ports: []swarm.PortConfig{{Protocol: "tcp", TargetPort: 80, PublishedPort: 30000}},
		},
	}
	if !reflect.DeepEqual(srv, expectedService) {
		t.Fatalf("ServiceCreate: wrong service. Want\n%#v\nGot\n%#v", expectedService, srv)
	}
}

func TestServiceCreateMultipleServers(t *testing.T) {
	server1, server2 := setUpSwarm(t)
	defer server1.Stop()
	defer server2.Stop()
	_, err := addTestService(server1)
	if err != nil {
		t.Fatal(err)
	}
	if len(server1.services) != 1 {
		t.Fatalf("ServiceCreate: expected services to have len 1, got: %d", len(server1.services))
	}
	if len(server1.services) != len(server2.services) {
		t.Fatalf("ServiceCreate: expected len services to be equal in server1 and server2, got:\n%#v\n%#v", len(server1.services), len(server2.services))
	}
	if !compareServices(server1.services[0], server2.services[0]) {
		t.Fatalf("ServiceCreate: expected services to be equal in server1 and server2, got:\n%#v\n%#v", server1.services[0], server2.services[0])
	}
	if len(server1.tasks) != len(server2.tasks) {
		t.Fatalf("ServiceCreate: expected len tasks to be equal in server1 and server2, got:\n%#v\n%#v", len(server1.tasks), len(server2.tasks))
	}
	if !compareTasks(server1.tasks[0], server2.tasks[0]) {
		t.Fatalf("ServiceCreate: expected tasks to be equal in server1 and server2, got:\n%#v\n%#v", server1.tasks[0], server2.tasks[0])
	}
}

func TestServiceCreateNoContainers(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	serviceCreateOpts := docker.CreateServiceOptions{
		ServiceSpec: swarm.ServiceSpec{
			Annotations: swarm.Annotations{
				Name: "test",
			},
		},
	}
	buf, err := json.Marshal(serviceCreateOpts)
	if err != nil {
		t.Fatalf("ServiceCreate error: %s", err.Error())
	}
	var params io.Reader = bytes.NewBuffer(buf)
	request, _ := http.NewRequest(http.MethodPost, "/services/create", params)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceCreate: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.services) != 1 || len(server.tasks) != 0 || len(server.containers) != 0 {
		t.Fatalf("ServiceCreate: wrong item count. Want 1 service and 0 tasks. Got services: %d, tasks: %d, containers: %d.", len(server.services), len(server.tasks), len(server.containers))
	}
}

func compareServices(srv1 *swarm.Service, srv2 *swarm.Service) bool {
	srv1.CreatedAt = srv2.CreatedAt
	srv1.UpdatedAt = srv2.UpdatedAt
	if srv1.UpdateStatus != nil {
		srv1.UpdateStatus.StartedAt = srv2.UpdateStatus.StartedAt
		srv1.UpdateStatus.CompletedAt = srv2.UpdateStatus.CompletedAt
	}
	return reflect.DeepEqual(srv1, srv2)
}

func compareTasks(task1 *swarm.Task, task2 *swarm.Task) bool {
	task1.CreatedAt = task2.CreatedAt
	task1.UpdatedAt = task2.UpdatedAt
	task1.Status.Timestamp = task2.Status.Timestamp
	return reflect.DeepEqual(task1, task2)
}

func TestServiceInspect(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/services/"+srv.ID, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceInspect: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var srvInspect swarm.Service
	err = json.Unmarshal(recorder.Body.Bytes(), &srvInspect)
	if err != nil {
		t.Fatalf("ServiceInspect: unable to unmarshal response body: %s", err)
	}
	if !compareServices(srv, &srvInspect) {
		t.Fatalf("ServiceInspect: wrong service. Want\n%#v\nGot\n%#v", srv, &srvInspect)
	}
}

func TestServiceInspectByName(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/services/"+srv.Spec.Name, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceInspect: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var srvInspect swarm.Service
	err = json.Unmarshal(recorder.Body.Bytes(), &srvInspect)
	if err != nil {
		t.Fatalf("ServiceInspect: unable to unmarshal response body: %s", err)
	}
	if !compareServices(srv, &srvInspect) {
		t.Fatalf("ServiceInspect: wrong service. Want\n%#v\nGot\n%#v", srv, &srvInspect)
	}
}

func TestServiceInspectNotFound(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/services/abcd", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Fatalf("ServiceInspect: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestTaskInspect(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/tasks/"+task.ID, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskInspect: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskInspect: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect) {
		t.Fatalf("TaskInspect: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
}

func TestTaskInspectNotFound(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/tasks/abcd", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Fatalf("TaskInspect: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestServiceList(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/services", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var srvInspect []swarm.Service
	err = json.Unmarshal(recorder.Body.Bytes(), &srvInspect)
	if err != nil {
		t.Fatalf("ServiceList: unable to unmarshal response body: %s", err)
	}
	if !compareServices(srv, &srvInspect[0]) {
		t.Fatalf("ServiceList: wrong service. Want\n%#v\nGot\n%#v", srv, &srvInspect)
	}
}

func TestServiceListFilterID(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(`/services?filters={"id":[%q]}`, srv.ID), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var srvInspect []swarm.Service
	err = json.Unmarshal(recorder.Body.Bytes(), &srvInspect)
	if err != nil {
		t.Fatalf("ServiceList: unable to unmarshal response body: %s", err)
	}
	if !compareServices(srv, &srvInspect[0]) {
		t.Fatalf("ServiceList: wrong service. Want\n%#v\nGot\n%#v", srv, &srvInspect)
	}
}

func TestServiceListFilterName(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(`/services?filters={"name":[%q]}`, srv.Spec.Name), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var srvInspect []swarm.Service
	err = json.Unmarshal(recorder.Body.Bytes(), &srvInspect)
	if err != nil {
		t.Fatalf("ServiceList: unable to unmarshal response body: %s", err)
	}
	if !compareServices(srv, &srvInspect[0]) {
		t.Fatalf("ServiceList: wrong service. Want\n%#v\nGot\n%#v", srv, &srvInspect)
	}
}

func TestServiceListFilterEmpty(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/services?filters="+url.QueryEscape(`{"id":["something"]}`), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var srvInspect []swarm.Service
	err = json.Unmarshal(recorder.Body.Bytes(), &srvInspect)
	if err != nil {
		t.Fatalf("ServiceList: unable to unmarshal response body: %s", err)
	}
	if len(srvInspect) != 0 {
		t.Fatalf("ServiceList: expected empty list got %d.", len(srvInspect))
	}
}

func TestTaskList(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, "/tasks", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
}

func TestTaskListFilterID(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(`/tasks?filters={"id":[%q]}`, task.ID), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
}

func TestTaskListFilterServiceID(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(`/tasks?filters={"service":[%q]}`, task.ServiceID), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
}

func TestTaskListFilterServiceName(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(`/tasks?filters={"service":[%q]}`, srv.Spec.Name), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
}

func TestTaskListFilterMultipleFields(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(`/tasks?filters={"service":[%q], "id":[%q]}`, srv.Spec.Name, task.ID), nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
}

func TestTaskListFilterMultipleFieldsNotFound(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	filterParam := url.QueryEscape(fmt.Sprintf(`{"service":[%q], "id":["abc"]}`, srv.Spec.Name))
	request, _ := http.NewRequest(http.MethodGet, "/tasks?filters="+filterParam, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if len(taskInspect) != 0 {
		t.Fatalf("TaskList: Want\nempty task list\nGot\n%#v", &taskInspect)
	}
}

func TestTaskListFilterNotFound(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	filter := url.QueryEscape(`{"id":["something"]}`)
	request, _ := http.NewRequest(http.MethodGet, "/tasks?filters="+filter, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if len(taskInspect) != 0 {
		t.Fatalf("TaskList: expected empty list got %d.", len(taskInspect))
	}
}

func TestTaskListFilterLabel(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	_, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	task := server.tasks[0]
	recorder := httptest.NewRecorder()
	filter := url.QueryEscape(`{"label":["mykey=myvalue"]}`)
	request, _ := http.NewRequest(http.MethodGet, "/tasks?filters="+filter, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	var taskInspect []swarm.Task
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
	filter = url.QueryEscape(`{"label":["mykey"]}`)
	request, _ = http.NewRequest(http.MethodGet, "/tasks?filters="+filter, nil)
	recorder = httptest.NewRecorder()
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if !compareTasks(task, &taskInspect[0]) {
		t.Fatalf("TaskList: wrong task. Want\n%#v\nGot\n%#v", task, &taskInspect)
	}
	filter = url.QueryEscape(`{"label":["otherkey"]}`)
	request, _ = http.NewRequest(http.MethodGet, "/tasks?filters="+filter, nil)
	recorder = httptest.NewRecorder()
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("TaskList: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	err = json.Unmarshal(recorder.Body.Bytes(), &taskInspect)
	if err != nil {
		t.Fatalf("TaskList: unable to unmarshal response body: %s", err)
	}
	if len(taskInspect) != 0 {
		t.Fatalf("TaskList: wrong size. Want 0, Got %d", len(taskInspect))
	}
}

func TestServiceDelete(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodDelete, "/services/"+srv.ID, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceDelete: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.services) != 0 {
		t.Fatalf("ServiceDelete: expected empty services, got %d", len(server.services))
	}
	if len(server.tasks) != 0 {
		t.Fatalf("ServiceDelete: expected empty tasks, got %d", len(server.tasks))
	}
	if len(server.containers) != 0 {
		t.Fatalf("ServiceDelete: expected empty containers, got %d", len(server.containers))
	}
}

func TestServiceDeleteNotFound(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodDelete, "/services/blahblah", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Fatalf("ServiceDelete: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestServiceUpdate(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	updateOpts := swarm.ServiceSpec{
		Annotations: swarm.Annotations{
			Name: "test",
		},
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: &swarm.ContainerSpec{
				Image: "test/test2",
				Args:  []string{"--test2"},
				Env:   []string{"ENV=2"},
				User:  "test",
			},
		},
		EndpointSpec: &swarm.EndpointSpec{
			Mode: swarm.ResolutionModeVIP,
			Ports: []swarm.PortConfig{{
				Protocol:      swarm.PortConfigProtocolTCP,
				TargetPort:    uint32(80),
				PublishedPort: uint32(80),
			}},
		},
	}
	buf, err := json.Marshal(updateOpts)
	if err != nil {
		t.Fatalf("ServiceUpdate error: %s", err.Error())
	}
	request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/services/%s/update", srv.ID), bytes.NewReader(buf))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceUpdate: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.services) != 1 || len(server.tasks) != 1 || len(server.containers) != 1 {
		t.Fatalf("ServiceUpdate: wrong item count. Want 1. Got services: %d, tasks: %d, containers: %d.", len(server.services), len(server.tasks), len(server.containers))
	}
	cont := getContainer(server)
	expectedContainer := &docker.Container{
		ID:      cont.ID,
		Created: cont.Created,
		Image:   "test/test2",
		Name:    "test-0-updated",
		Config: &docker.Config{
			Cmd: []string{"--test2"},
			Env: []string{"ENV=2"},
		},
		HostConfig: &docker.HostConfig{},
		State: docker.State{
			Running:   true,
			StartedAt: cont.State.StartedAt,
			Pid:       cont.State.Pid,
			ExitCode:  0,
		},
	}
	if !reflect.DeepEqual(cont, expectedContainer) {
		t.Fatalf("ServiceUpdate: wrong cont. Want\n%#v\nGot\n%#v", expectedContainer, cont)
	}
	srv = server.services[0]
	expectedService := &swarm.Service{
		ID:   srv.ID,
		Spec: updateOpts,
		Endpoint: swarm.Endpoint{
			Spec:  *updateOpts.EndpointSpec,
			Ports: []swarm.PortConfig{{Protocol: "tcp", TargetPort: 80, PublishedPort: 80}},
		},
		UpdateStatus: &swarm.UpdateStatus{
			State: swarm.UpdateStateCompleted,
		},
	}
	srv.UpdateStatus.CompletedAt = nil
	srv.UpdateStatus.StartedAt = nil
	if !reflect.DeepEqual(srv, expectedService) {
		t.Fatalf("ServiceUpdate: wrong service. Want\n%#v\nGot\n%#v", expectedService, srv)
	}
	task := server.tasks[0]
	expectedTask := &swarm.Task{
		ID:        task.ID,
		ServiceID: srv.ID,
		NodeID:    server.nodes[1].ID,
		Status: swarm.TaskStatus{
			State: swarm.TaskStateReady,
			ContainerStatus: &swarm.ContainerStatus{
				ContainerID: cont.ID,
			},
		},
		DesiredState: swarm.TaskStateReady,
		Spec:         updateOpts.TaskTemplate,
	}
	if !reflect.DeepEqual(task, expectedTask) {
		t.Fatalf("ServiceUpdate: wrong task. Want\n%#v\nGot\n%#v", expectedTask, task)
	}
}

func TestServiceUpdateMoreReplicas(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	srv, err := addTestService(server)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	replicas := uint64(3)
	updateOpts := swarm.ServiceSpec{
		Annotations: swarm.Annotations{
			Name: "test",
		},
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: &swarm.ContainerSpec{
				Image: "test/test2",
				Args:  []string{"--test2"},
				Env:   []string{"ENV=2"},
				User:  "test",
			},
		},
		EndpointSpec: &swarm.EndpointSpec{
			Mode: swarm.ResolutionModeVIP,
			Ports: []swarm.PortConfig{{
				Protocol:      swarm.PortConfigProtocolTCP,
				TargetPort:    uint32(80),
				PublishedPort: uint32(80),
			}},
		},
		Mode: swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{
				Replicas: &replicas,
			},
		},
	}
	buf, err := json.Marshal(updateOpts)
	if err != nil {
		t.Fatalf("ServiceUpdate error: %s", err.Error())
	}
	request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/services/%s/update", srv.ID), bytes.NewReader(buf))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("ServiceUpdate: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if len(server.services) != 1 || len(server.tasks) != 3 || len(server.containers) != 3 {
		t.Fatalf("ServiceUpdate: wrong item count. Want 1 service and 3 replicas. Got services: %d, tasks: %d, containers: %d.", len(server.services), len(server.tasks), len(server.containers))
	}
}

func TestServiceUpdateNotFound(t *testing.T) {
	server, unused := setUpSwarm(t)
	defer server.Stop()
	defer unused.Stop()
	recorder := httptest.NewRecorder()
	updateOpts := swarm.ServiceSpec{
		Annotations: swarm.Annotations{
			Name: "test",
		},
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: &swarm.ContainerSpec{
				Image: "test/test2",
				Args:  []string{"--test2"},
				Env:   []string{"ENV=2"},
				User:  "test",
			},
		},
		EndpointSpec: &swarm.EndpointSpec{
			Mode: swarm.ResolutionModeVIP,
			Ports: []swarm.PortConfig{{
				Protocol:      swarm.PortConfigProtocolTCP,
				TargetPort:    uint32(80),
				PublishedPort: uint32(80),
			}},
		},
	}
	buf, err := json.Marshal(updateOpts)
	if err != nil {
		t.Fatalf("ServiceUpdate error: %s", err.Error())
	}
	request, _ := http.NewRequest(http.MethodPost, "/services/pale/update", bytes.NewReader(buf))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Fatalf("ServiceUpdate: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestNodeList(t *testing.T) {
	srv1, srv2 := setUpSwarm(t)
	defer srv1.Stop()
	defer srv2.Stop()
	for _, srv := range []*DockerServer{srv1, srv2} {
		recorder := httptest.NewRecorder()
		request, _ := http.NewRequest(http.MethodGet, "/nodes", nil)
		srv.ServeHTTP(recorder, request)
		if recorder.Code != http.StatusOK {
			t.Fatalf("invalid status code: %d", recorder.Code)
		}
		var nodes []swarm.Node
		err := json.NewDecoder(recorder.Body).Decode(&nodes)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(nodes, srv1.nodes) {
			t.Fatalf("expected nodes to equal %#v, got: %#v", srv1.nodes, nodes)
		}
		if !reflect.DeepEqual(nodes, srv2.nodes) {
			t.Fatalf("expected nodes to equal %#v, got: %#v", srv2.nodes, nodes)
		}
	}
}

func TestNodeInfo(t *testing.T) {
	srv1, srv2 := setUpSwarm(t)
	defer srv1.Stop()
	defer srv2.Stop()
	for _, srv := range []*DockerServer{srv1, srv2} {
		recorder := httptest.NewRecorder()
		request, _ := http.NewRequest(http.MethodGet, "/nodes/"+srv.nodes[0].ID, nil)
		srv.ServeHTTP(recorder, request)
		if recorder.Code != http.StatusOK {
			t.Fatalf("invalid status code: %d", recorder.Code)
		}
		var node swarm.Node
		err := json.NewDecoder(recorder.Body).Decode(&node)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(node, srv1.nodes[0]) {
			t.Fatalf("expected node to equal %#v, got: %#v", srv1.nodes[0], node)
		}
		if !reflect.DeepEqual(node, srv2.nodes[0]) {
			t.Fatalf("expected node to equal %#v, got: %#v", srv2.nodes[0], node)
		}
	}
}

func TestNodeUpdate(t *testing.T) {
	srv1, srv2 := setUpSwarm(t)
	defer srv1.Stop()
	defer srv2.Stop()
	recorder := httptest.NewRecorder()
	for i, srv := range []*DockerServer{srv1, srv2} {
		key := fmt.Sprintf("l%d", i)
		data, err := json.Marshal(swarm.NodeSpec{
			Annotations: swarm.Annotations{Labels: map[string]string{key: "value"}},
		})
		if err != nil {
			t.Fatal(err)
		}
		body := bytes.NewReader(data)
		request, _ := http.NewRequest(http.MethodPost, "/nodes/"+srv.nodes[0].ID+"/update", body)
		srv.ServeHTTP(recorder, request)
		if recorder.Code != http.StatusOK {
			t.Fatalf("invalid status code: %d", recorder.Code)
		}
		if srv1.nodes[0].Spec.Annotations.Labels[key] != "value" {
			t.Fatalf("expected node to have label %s", key)
		}
		if srv2.nodes[0].Spec.Annotations.Labels[key] != "value" {
			t.Fatalf("expected node to have label %s", key)
		}
	}
}

func TestNodeDelete(t *testing.T) {
	srv1, srv2 := setUpSwarm(t)
	defer srv1.Stop()
	defer srv2.Stop()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodDelete, "/nodes/"+srv1.nodes[0].ID, nil)
	srv1.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("invalid status code: %d", recorder.Code)
	}
	if len(srv1.nodes) != 1 {
		t.Fatalf("expected len(nodes) to be 1, got %d", len(srv1.nodes))
	}
	if len(srv2.nodes) != 1 {
		t.Fatalf("expected len(nodes) to be 1, got %d", len(srv2.nodes))
	}
}

func setUpSwarm(t *testing.T) (*DockerServer, *DockerServer) {
	server1, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	server2, err := NewServer("127.0.0.1:0", nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest(http.MethodPost, "/swarm/init", bytes.NewReader(nil))
	server1.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("setUpSwarm: invalid status code swarm init %d", recorder.Code)
	}
	data, err := json.Marshal(swarm.JoinRequest{
		RemoteAddrs: []string{server1.SwarmAddress()},
	})
	if err != nil {
		t.Fatal(err)
	}
	recorder = httptest.NewRecorder()
	request, _ = http.NewRequest(http.MethodPost, "/swarm/join", bytes.NewReader(data))
	server2.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Fatalf("setUpSwarm: invalid status code swarm join %d", recorder.Code)
	}
	return server1, server2
}

func addTestService(server *DockerServer) (*swarm.Service, error) {
	recorder := httptest.NewRecorder()
	serviceCreateOpts := docker.CreateServiceOptions{
		ServiceSpec: swarm.ServiceSpec{
			Annotations: swarm.Annotations{
				Name: "test",
				Labels: map[string]string{
					"mykey": "myvalue",
				},
			},
			TaskTemplate: swarm.TaskSpec{
				ContainerSpec: &swarm.ContainerSpec{
					Image: "test/test",
					Args:  []string{"--test"},
					Env:   []string{"ENV=1"},
					User:  "test",
				},
			},
			EndpointSpec: &swarm.EndpointSpec{
				Mode: swarm.ResolutionModeVIP,
				Ports: []swarm.PortConfig{{
					Protocol:      swarm.PortConfigProtocolTCP,
					TargetPort:    uint32(80),
					PublishedPort: uint32(80),
				}},
			},
		},
	}
	buf, err := json.Marshal(serviceCreateOpts)
	if err != nil {
		return nil, err
	}
	var params io.Reader = bytes.NewBuffer(buf)
	request, _ := http.NewRequest(http.MethodPost, "/services/create", params)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		return nil, fmt.Errorf("unexpected status %d", recorder.Code)
	}
	if len(server.services) == 0 {
		return nil, errors.New("no service created on server")
	}
	if len(server.tasks) == 0 {
		return nil, errors.New("no tasks created on server")
	}
	return server.services[0], nil
}

func TestMutateTask(t *testing.T) {
	server := DockerServer{failures: make(map[string]string)}
	server.buildMuxer()
	server.tasks = append(server.tasks, &swarm.Task{ID: "id123"})
	newTask := swarm.Task{Status: swarm.TaskStatus{State: swarm.TaskStateFailed}}
	err := server.MutateTask("id123", newTask)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(server.tasks[0], &newTask) {
		t.Errorf("Wrong task after mutation.\nWant %#v.\nGot %#v.",
			newTask, server.tasks[0])
	}
}

func TestMutateTaskNotFound(t *testing.T) {
	server := DockerServer{failures: make(map[string]string)}
	server.buildMuxer()
	newTask := swarm.Task{Status: swarm.TaskStatus{State: swarm.TaskStateFailed}}
	err := server.MutateTask("id123", newTask)
	if err == nil {
		t.Error("Unexpected <nil> error")
	}
	if err.Error() != "task not found" {
		t.Errorf("wrong error message. Want %q. Got %q.", "task not found", err)
	}
}
