From 2f90c2da90221100d57f22052a8ad537a962fe29 Mon Sep 17 00:00:00 2001 From: Kelly Lyon Date: Fri, 30 Sep 2016 11:23:06 -0700 Subject: [PATCH] Wrote V1 Rest API Validation Tests: - Wrote tests in rest_v1_tests.go that test the various routes found in the addRoutes function of server.go. Each test calls mock methods from the fixtures package and compares its response to a constant, expected response found in the mock files. - Added mock manager classes in the fixtures class for managing config, metric, task, and tribe routes individually. Each of these classes has a mock manager, mock objects, necessary methods for implementing those, and constants for comparing against the v1 tests. --- mgmt/rest/config_test.go | 49 ++ mgmt/rest/fixtures/mock_config_manager.go | 109 ++++ mgmt/rest/fixtures/mock_metric_manager.go | 302 +++++++++++ mgmt/rest/fixtures/mock_task_manager.go | 347 +++++++++++++ mgmt/rest/fixtures/mock_tribe_manager.go | 416 +++++++++++++++ mgmt/rest/plugin_test.go | 77 +-- mgmt/rest/rest_func_test.go | 23 - mgmt/rest/rest_v1_test.go | 587 ++++++++++++++++++++++ 8 files changed, 1812 insertions(+), 98 deletions(-) create mode 100644 mgmt/rest/config_test.go create mode 100644 mgmt/rest/fixtures/mock_config_manager.go create mode 100644 mgmt/rest/fixtures/mock_metric_manager.go create mode 100644 mgmt/rest/fixtures/mock_task_manager.go create mode 100644 mgmt/rest/fixtures/mock_tribe_manager.go create mode 100644 mgmt/rest/rest_v1_test.go diff --git a/mgmt/rest/config_test.go b/mgmt/rest/config_test.go new file mode 100644 index 000000000..706b67105 --- /dev/null +++ b/mgmt/rest/config_test.go @@ -0,0 +1,49 @@ +// +build legacy small medium large + +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + +Copyright 2016 Intel Corporation + +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. +*/ + +package rest + +import ( + "github.com/intelsdi-x/snap/control" + "github.com/intelsdi-x/snap/scheduler" +) + +// Since we do not have a global snap package that could be imported +// we create a mock config struct to mock what is in snapd.go + +type mockConfig struct { + LogLevel int `json:"-"yaml:"-"` + GoMaxProcs int `json:"-"yaml:"-"` + LogPath string `json:"-"yaml:"-"` + Control *control.Config + Scheduler *scheduler.Config `json:"-",yaml:"-"` + RestAPI *Config `json:"-",yaml:"-"` +} + +func getDefaultMockConfig() *mockConfig { + return &mockConfig{ + LogLevel: 3, + GoMaxProcs: 1, + LogPath: "", + Control: control.GetDefaultConfig(), + Scheduler: scheduler.GetDefaultConfig(), + RestAPI: GetDefaultConfig(), + } +} diff --git a/mgmt/rest/fixtures/mock_config_manager.go b/mgmt/rest/fixtures/mock_config_manager.go new file mode 100644 index 000000000..6bc0dbe2f --- /dev/null +++ b/mgmt/rest/fixtures/mock_config_manager.go @@ -0,0 +1,109 @@ +// +build legacy small medium large + +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + +Copyright 2016 Intel Corporation + +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. +*/ + +package fixtures + +import ( + "github.com/intelsdi-x/snap/core" + "github.com/intelsdi-x/snap/core/cdata" + "github.com/intelsdi-x/snap/core/ctypes" +) + +var mockConfig *cdata.ConfigDataNode + +func init() { + mockConfig = cdata.NewNode() + mockConfig.AddItem("User", ctypes.ConfigValueStr{Value: "KELLY"}) + mockConfig.AddItem("Port", ctypes.ConfigValueInt{Value: 2}) +} + +type MockConfigManager struct{} + +func (MockConfigManager) GetPluginConfigDataNode(core.PluginType, string, int) cdata.ConfigDataNode { + return *mockConfig +} +func (MockConfigManager) GetPluginConfigDataNodeAll() cdata.ConfigDataNode { + return *mockConfig +} +func (MockConfigManager) MergePluginConfigDataNode( + pluginType core.PluginType, name string, ver int, cdn *cdata.ConfigDataNode) cdata.ConfigDataNode { + return *cdn +} +func (MockConfigManager) MergePluginConfigDataNodeAll(cdn *cdata.ConfigDataNode) cdata.ConfigDataNode { + return cdata.ConfigDataNode{} +} +func (MockConfigManager) DeletePluginConfigDataNodeField( + pluginType core.PluginType, name string, ver int, fields ...string) cdata.ConfigDataNode { + for _, field := range fields { + mockConfig.DeleteItem(field) + + } + return *mockConfig +} + +func (MockConfigManager) DeletePluginConfigDataNodeFieldAll(fields ...string) cdata.ConfigDataNode { + for _, field := range fields { + mockConfig.DeleteItem(field) + + } + return *mockConfig +} + +// These constants are the expected plugin config responses from running +// rest_v1_test.go on the plugin config routes found in mgmt/rest/server.go +const ( + SET_PLUGIN_CONFIG_ITEM = `{ + "meta": { + "code": 200, + "message": "Plugin config item(s) set", + "type": "config_plugin_item_created", + "version": 1 + }, + "body": { + "user": "Jane" + } +}` + + GET_PLUGIN_CONFIG_ITEM = `{ + "meta": { + "code": 200, + "message": "Plugin config item retrieved", + "type": "config_plugin_item_returned", + "version": 1 + }, + "body": { + "Port": 2, + "User": "KELLY" + } +}` + + DELETE_PLUGIN_CONFIG_ITEM = `{ + "meta": { + "code": 200, + "message": "Plugin config item field(s) deleted", + "type": "config_plugin_item_deleted", + "version": 1 + }, + "body": { + "Port": 2, + "User": "KELLY" + } +}` +) diff --git a/mgmt/rest/fixtures/mock_metric_manager.go b/mgmt/rest/fixtures/mock_metric_manager.go new file mode 100644 index 000000000..7905cb252 --- /dev/null +++ b/mgmt/rest/fixtures/mock_metric_manager.go @@ -0,0 +1,302 @@ +// +build legacy small medium large + +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + +Copyright 2016 Intel Corporation + +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. +*/ + +package fixtures + +import ( + "errors" + "time" + + "github.com/intelsdi-x/snap/control/plugin/cpolicy" + "github.com/intelsdi-x/snap/core" + "github.com/intelsdi-x/snap/core/serror" +) + +var pluginCatalog []core.CatalogedPlugin = []core.CatalogedPlugin{ + MockLoadedPlugin{MyName: "foo", MyType: "collector", MyVersion: 2}, + MockLoadedPlugin{MyName: "bar", MyType: "publisher", MyVersion: 3}, + MockLoadedPlugin{MyName: "foo", MyType: "collector", MyVersion: 4}, + MockLoadedPlugin{MyName: "baz", MyType: "publisher", MyVersion: 5}, + MockLoadedPlugin{MyName: "foo", MyType: "processor", MyVersion: 6}, + MockLoadedPlugin{MyName: "foobar", MyType: "processor", MyVersion: 1}, +} + +var metricCatalog []core.CatalogedMetric = []core.CatalogedMetric{ + MockCatalogedMetric{}, +} + +//////MockLoadedPlugin///// + +type MockLoadedPlugin struct { + MyName string + MyType string + MyVersion int +} + +func (m MockLoadedPlugin) Name() string { return m.MyName } +func (m MockLoadedPlugin) TypeName() string { return m.MyType } +func (m MockLoadedPlugin) Version() int { return m.MyVersion } +func (m MockLoadedPlugin) Plugin() string { return "" } +func (m MockLoadedPlugin) IsSigned() bool { return false } +func (m MockLoadedPlugin) Status() string { return "" } +func (m MockLoadedPlugin) PluginPath() string { return "" } +func (m MockLoadedPlugin) LoadedTimestamp() *time.Time { + t := time.Date(2016, time.September, 6, 0, 0, 0, 0, time.UTC) + return &t +} +func (m MockLoadedPlugin) Policy() *cpolicy.ConfigPolicy { return cpolicy.New() } +func (m MockLoadedPlugin) HitCount() int { return 0 } +func (m MockLoadedPlugin) LastHit() time.Time { return time.Now() } +func (m MockLoadedPlugin) ID() uint32 { return 0 } + +//////MockCatalogedMetric///// + +type MockCatalogedMetric struct{} + +func (m MockCatalogedMetric) Namespace() core.Namespace { + return core.NewNamespace("one", "two", "three") +} +func (m MockCatalogedMetric) Version() int { return 5 } +func (m MockCatalogedMetric) LastAdvertisedTime() time.Time { return time.Time{} } +func (m MockCatalogedMetric) Policy() *cpolicy.ConfigPolicyNode { return cpolicy.NewPolicyNode() } +func (m MockCatalogedMetric) Description() string { return "This Is A Description" } +func (m MockCatalogedMetric) Unit() string { return "" } + +//////MockManagesMetrics///// + +type MockManagesMetrics struct{} + +func (m MockManagesMetrics) MetricCatalog() ([]core.CatalogedMetric, error) { + return metricCatalog, nil +} +func (m MockManagesMetrics) FetchMetrics(core.Namespace, int) ([]core.CatalogedMetric, error) { + return metricCatalog, nil +} +func (m MockManagesMetrics) GetMetricVersions(core.Namespace) ([]core.CatalogedMetric, error) { + return metricCatalog, nil +} +func (m MockManagesMetrics) GetMetric(core.Namespace, int) (core.CatalogedMetric, error) { + return MockCatalogedMetric{}, nil +} +func (m MockManagesMetrics) Load(*core.RequestedPlugin) (core.CatalogedPlugin, serror.SnapError) { + return MockLoadedPlugin{"foo", "collector", 1}, nil +} +func (m MockManagesMetrics) Unload(plugin core.Plugin) (core.CatalogedPlugin, serror.SnapError) { + for _, pl := range pluginCatalog { + if plugin.Name() == pl.Name() && + plugin.Version() == pl.Version() && + plugin.TypeName() == pl.TypeName() { + return pl, nil + } + } + return nil, serror.New(errors.New("plugin not found")) +} + +func (m MockManagesMetrics) PluginCatalog() core.PluginCatalog { + return pluginCatalog +} +func (m MockManagesMetrics) AvailablePlugins() []core.AvailablePlugin { + return []core.AvailablePlugin{ + MockLoadedPlugin{MyName: "foo", MyType: "collector", MyVersion: 2}, + MockLoadedPlugin{MyName: "bar", MyType: "publisher", MyVersion: 3}, + MockLoadedPlugin{MyName: "foo", MyType: "collector", MyVersion: 4}, + MockLoadedPlugin{MyName: "baz", MyType: "publisher", MyVersion: 5}, + MockLoadedPlugin{MyName: "foo", MyType: "processor", MyVersion: 6}, + MockLoadedPlugin{MyName: "foobar", MyType: "processor", MyVersion: 1}, + } +} +func (m MockManagesMetrics) GetAutodiscoverPaths() []string { + return nil +} + +// These constants are the expected plugin responses from running +// rest_v1_test.go on the plugin routes found in mgmt/rest/server.go +const ( + GET_PLUGINS_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Plugin list returned", + "type": "plugin_list_returned", + "version": 1 + }, + "body": { + "loaded_plugins": [ + { + "name": "foo", + "version": 2, + "type": "collector", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/collector/foo/2" + }, + { + "name": "bar", + "version": 3, + "type": "publisher", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/publisher/bar/3" + }, + { + "name": "foo", + "version": 4, + "type": "collector", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/collector/foo/4" + }, + { + "name": "baz", + "version": 5, + "type": "publisher", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/publisher/baz/5" + }, + { + "name": "foo", + "version": 6, + "type": "processor", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/processor/foo/6" + }, + { + "name": "foobar", + "version": 1, + "type": "processor", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/processor/foobar/1" + } + ] + } +}` + + GET_PLUGINS_RESPONSE_TYPE = `{ + "meta": { + "code": 200, + "message": "Plugin list returned", + "type": "plugin_list_returned", + "version": 1 + }, + "body": { + "loaded_plugins": [ + { + "name": "foo", + "version": 2, + "type": "collector", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/collector/foo/2" + }, + { + "name": "foo", + "version": 4, + "type": "collector", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/collector/foo/4" + } + ] + } +}` + + GET_PLUGINS_RESPONSE_TYPE_NAME = `{ + "meta": { + "code": 200, + "message": "Plugin list returned", + "type": "plugin_list_returned", + "version": 1 + }, + "body": { + "loaded_plugins": [ + { + "name": "bar", + "version": 3, + "type": "publisher", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/publisher/bar/3" + } + ] + } +}` + + GET_PLUGINS_RESPONSE_TYPE_NAME_VERSION = `{ + "meta": { + "code": 200, + "message": "Plugin returned", + "type": "plugin_returned", + "version": 1 + }, + "body": { + "name": "bar", + "version": 3, + "type": "publisher", + "signed": false, + "status": "", + "loaded_timestamp": 1473120000, + "href": "http://localhost:%d/v1/plugins/publisher/bar/3" + } +}` + + GET_METRICS_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Metrics returned", + "type": "metrics_returned", + "version": 1 + }, + "body": [ + { + "last_advertised_timestamp": -62135596800, + "namespace": "/one/two/three", + "version": 5, + "dynamic": false, + "description": "This Is A Description", + "href": "http://localhost:%d/v1/metrics?ns=/one/two/three&ver=5" + } + ] +}` + + UNLOAD_PLUGIN_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Plugin successfully unloaded (foov2)", + "type": "plugin_unloaded", + "version": 1 + }, + "body": { + "name": "foo", + "version": 2, + "type": "collector" + } +}` +) diff --git a/mgmt/rest/fixtures/mock_task_manager.go b/mgmt/rest/fixtures/mock_task_manager.go new file mode 100644 index 000000000..fa3161237 --- /dev/null +++ b/mgmt/rest/fixtures/mock_task_manager.go @@ -0,0 +1,347 @@ +// +build legacy small medium large + +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + +Copyright 2016 Intel Corporation + +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. +*/ + +package fixtures + +import ( + "time" + + "github.com/intelsdi-x/snap/core" + "github.com/intelsdi-x/snap/core/serror" + "github.com/intelsdi-x/snap/pkg/schedule" + "github.com/intelsdi-x/snap/scheduler/wmap" +) + +var taskCatalog map[string]core.Task = map[string]core.Task{ + "Task1": &mockTask{ + MyID: "qwertyuiop", + MyName: "TASK1.0", + MyDeadline: "4", + MyCreationTimestamp: time.Now().Unix(), + MyLastRunTimestamp: time.Now().Unix(), + MyHitCount: 44, + MyMissCount: 8, + MyState: "failed", + MyHref: "http://localhost:8181/v2/tasks/qwertyuiop"}, + "Task2": &mockTask{ + MyID: "asdfghjkl", + MyName: "TASK2.0", + MyDeadline: "4", + MyCreationTimestamp: time.Now().Unix(), + MyLastRunTimestamp: time.Now().Unix(), + MyHitCount: 33, + MyMissCount: 7, + MyState: "passed", + MyHref: "http://localhost:8181/v2/tasks/asdfghjkl"}} + +type mockTask struct { + MyID string `json:"id"` + MyName string `json:"name"` + MyDeadline string `json:"deadline"` + MyWorkflow *wmap.WorkflowMap `json:"workflow,omitempty"` + MySchedule *core.Schedule `json:"schedule,omitempty"` + MyCreationTimestamp int64 `json:"creation_timestamp,omitempty"` + MyLastRunTimestamp int64 `json:"last_run_timestamp,omitempty"` + MyHitCount int `json:"hit_count,omitempty"` + MyMissCount int `json:"miss_count,omitempty"` + MyFailedCount int `json:"failed_count,omitempty"` + MyLastFailureMessage string `json:"last_failure_message,omitempty"` + MyState string `json:"task_state"` + MyHref string `json:"href"` +} + +func (t *mockTask) ID() string { return t.MyID } +func (t *mockTask) State() core.TaskState { return core.TaskSpinning } +func (t *mockTask) HitCount() uint { return 0 } +func (t *mockTask) GetName() string { return t.MyName } +func (t *mockTask) SetName(string) { return } +func (t *mockTask) SetID(string) { return } +func (t *mockTask) MissedCount() uint { return 0 } +func (t *mockTask) FailedCount() uint { return 0 } +func (t *mockTask) LastFailureMessage() string { return "" } +func (t *mockTask) LastRunTime() *time.Time { return &time.Time{} } +func (t *mockTask) CreationTime() *time.Time { return &time.Time{} } +func (t *mockTask) DeadlineDuration() time.Duration { return 4 } +func (t *mockTask) SetDeadlineDuration(time.Duration) { return } +func (t *mockTask) SetTaskID(id string) { return } +func (t *mockTask) SetStopOnFailure(int) { return } +func (t *mockTask) GetStopOnFailure() int { return 0 } +func (t *mockTask) Option(...core.TaskOption) core.TaskOption { + return core.TaskDeadlineDuration(0) +} +func (t *mockTask) WMap() *wmap.WorkflowMap { + return wmap.NewWorkflowMap() +} +func (t *mockTask) Schedule() schedule.Schedule { + return schedule.NewSimpleSchedule(time.Second * 1) +} +func (t *mockTask) MaxFailures() int { return 10 } + +type MockTaskManager struct{} + +func (m *MockTaskManager) GetTask(id string) (core.Task, error) { + href := "http://localhost:8181/v2/tasks/" + id + return &mockTask{ + MyID: id, + MyName: "NewTaskCreated", + MyCreationTimestamp: time.Now().Unix(), + MyLastRunTimestamp: time.Now().Unix(), + MyHitCount: 22, + MyMissCount: 4, + MyState: "failed", + MyHref: href}, nil +} +func (m *MockTaskManager) CreateTask( + sch schedule.Schedule, + wmap *wmap.WorkflowMap, + start bool, + opts ...core.TaskOption) (core.Task, core.TaskErrors) { + return &mockTask{ + MyID: "MyTaskID", + MyName: "NewTaskCreated", + MySchedule: &core.Schedule{}, + MyCreationTimestamp: time.Now().Unix(), + MyLastRunTimestamp: time.Now().Unix(), + MyHitCount: 99, + MyMissCount: 5, + MyState: "failed", + MyHref: "http://localhost:8181/v2/tasks/MyTaskID"}, nil +} +func (m *MockTaskManager) GetTasks() map[string]core.Task { + return taskCatalog +} +func (m *MockTaskManager) StartTask(id string) []serror.SnapError { return nil } +func (m *MockTaskManager) StopTask(id string) []serror.SnapError { return nil } +func (m *MockTaskManager) RemoveTask(id string) error { return nil } +func (m *MockTaskManager) WatchTask(id string, handler core.TaskWatcherHandler) (core.TaskWatcherCloser, error) { + return nil, nil +} +func (m *MockTaskManager) EnableTask(id string) (core.Task, error) { + return &mockTask{ + MyID: "alskdjf", + MyName: "Task2", + MyCreationTimestamp: time.Now().Unix(), + MyLastRunTimestamp: time.Now().Unix(), + MyHitCount: 44, + MyMissCount: 8, + MyState: "failed", + MyHref: "http://localhost:8181/v2/tasks/alskdjf"}, nil +} + +// Mock task used in the 'Add tasks' test in rest_v1_test.go +const TASK = `{ + "version": 1, + "schedule": { + "type": "simple", + "interval": "1s" + }, + "max-failures": 10, + "workflow": { + "collect": { + "metrics": { + "/one/two/three": {} + } + } + } +} +` + +// These constants are the expected responses from running the task tests in +// rest_v1_test.go on the task routes found in mgmt/rest/server.go +const ( + GET_TASKS_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Scheduled tasks retrieved", + "type": "scheduled_task_list_returned", + "version": 1 + }, + "body": { + "ScheduledTasks": [ + { + "id": "qwertyuiop", + "name": "TASK1.0", + "deadline": "4ns", + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "http://localhost:%d/v1/tasks/qwertyuiop" + }, + { + "id": "asdfghjkl", + "name": "TASK2.0", + "deadline": "4ns", + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "http://localhost:%d/v1/tasks/asdfghjkl" + } + ] + } +}` + + GET_TASKS_RESPONSE2 = `{ + "meta": { + "code": 200, + "message": "Scheduled tasks retrieved", + "type": "scheduled_task_list_returned", + "version": 1 + }, + "body": { + "ScheduledTasks": [ + { + "id": "asdfghjkl", + "name": "TASK2.0", + "deadline": "4ns", + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "http://localhost:%d/v1/tasks/asdfghjkl" + }, + { + "id": "qwertyuiop", + "name": "TASK1.0", + "deadline": "4ns", + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "http://localhost:%d/v1/tasks/qwertyuiop" + } + ] + } +}` + + GET_TASK_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Scheduled task (:1234) returned", + "type": "scheduled_task_returned", + "version": 1 + }, + "body": { + "id": ":1234", + "name": "NewTaskCreated", + "deadline": "4ns", + "workflow": { + "collect": { + "metrics": {} + } + }, + "schedule": { + "type": "simple", + "interval": "1s" + }, + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "http://localhost:%d/v1/tasks/:1234" + } +}` + + ADD_TASK_RESPONSE = `{ + "meta": { + "code": 201, + "message": "Scheduled task created (MyTaskID)", + "type": "scheduled_task_created", + "version": 1 + }, + "body": { + "id": "MyTaskID", + "name": "NewTaskCreated", + "deadline": "4ns", + "workflow": { + "collect": { + "metrics": {} + } + }, + "schedule": { + "type": "simple", + "interval": "1s" + }, + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "http://localhost:%d/v1/tasks/MyTaskID" + } +}` + + START_TASK_RESPONSE_ID_START = `{ + "meta": { + "code": 200, + "message": "Scheduled task (MockTask1234) started", + "type": "scheduled_task_started", + "version": 1 + }, + "body": { + "id": "MockTask1234" + } +}` + + STOP_TASK_RESPONSE_ID_STOP = `{ + "meta": { + "code": 200, + "message": "Scheduled task (MockTask1234) stopped", + "type": "scheduled_task_stopped", + "version": 1 + }, + "body": { + "id": "MockTask1234" + } +}` + + ENABLE_TASK_RESPONSE_ID_ENABLE = `{ + "meta": { + "code": 200, + "message": "Disabled task (alskdjf) enabled", + "type": "scheduled_task_enabled", + "version": 1 + }, + "body": { + "id": "alskdjf", + "name": "Task2", + "deadline": "4ns", + "workflow": { + "collect": { + "metrics": {} + } + }, + "schedule": { + "type": "simple", + "interval": "1s" + }, + "creation_timestamp": -62135596800, + "last_run_timestamp": -1, + "task_state": "Running", + "href": "" + } +}` + + REMOVE_TASK_RESPONSE_ID = `{ + "meta": { + "code": 200, + "message": "Scheduled task (MockTask1234) removed", + "type": "scheduled_task_removed", + "version": 1 + }, + "body": { + "id": "MockTask1234" + } +}` +) diff --git a/mgmt/rest/fixtures/mock_tribe_manager.go b/mgmt/rest/fixtures/mock_tribe_manager.go new file mode 100644 index 000000000..21c6c7829 --- /dev/null +++ b/mgmt/rest/fixtures/mock_tribe_manager.go @@ -0,0 +1,416 @@ +// +build legacy small medium large + +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + +Copyright 2016 Intel Corporation + +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. +*/ + +package fixtures + +import ( + "net" + + "github.com/hashicorp/memberlist" + "github.com/intelsdi-x/snap/core" + "github.com/intelsdi-x/snap/core/serror" + "github.com/intelsdi-x/snap/mgmt/tribe/agreement" +) + +var ( + mockTribeAgreement *agreement.Agreement + mockTribeMember *agreement.Member +) + +func init() { + mockTribeAgreement = agreement.New("Agree1") + mockTribeAgreement.PluginAgreement.Add( + agreement.Plugin{Name_: "mockVersion", Version_: 1, Type_: core.CollectorPluginType}) + mockTribeAgreement.TaskAgreement.Add( + agreement.Task{ID: "mockTask", StartOnCreate: true}) + mockTribeAgreement.Members["member1"] = agreement.NewMember(&memberlist.Node{ + Name: "mockName", + Addr: net.ParseIP("193.34.23.11"), + Port: uint16(0), + Meta: []byte("meta"), // Metadata from the delegate for this node. + PMin: uint8(0), // Minimum protocol version this understands + PMax: uint8(0), // Maximum protocol version this understands + PCur: uint8(0), // Current version node is speaking + DMin: uint8(0), // Min protocol version for the delegate to understand + DMax: uint8(0), // Max protocol version for the delegate to understand + DCur: uint8(0), // Current version delegate is speaking + }) +} + +type MockTribeManager struct{} + +func (m *MockTribeManager) GetAgreement(name string) (*agreement.Agreement, serror.SnapError) { + return mockTribeAgreement, nil +} +func (m *MockTribeManager) GetAgreements() map[string]*agreement.Agreement { + return map[string]*agreement.Agreement{ + "Agree1": mockTribeAgreement, + "Agree2": mockTribeAgreement} +} +func (m *MockTribeManager) AddAgreement(name string) serror.SnapError { + return nil +} +func (m *MockTribeManager) RemoveAgreement(name string) serror.SnapError { + return nil +} +func (m *MockTribeManager) JoinAgreement(agreementName, memberName string) serror.SnapError { + return nil +} +func (m *MockTribeManager) LeaveAgreement(agreementName, memberName string) serror.SnapError { + return nil +} +func (m *MockTribeManager) GetMembers() []string { + return []string{"one", "two", "three"} +} +func (m *MockTribeManager) GetMember(name string) *agreement.Member { + return &agreement.Member{} +} + +// These constants are the expected tribe responses from running +// rest_v1_test.go on the tribe routes found in mgmt/rest/server.go +const ( + GET_TRIBE_AGREEMENTS_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Tribe agreements retrieved", + "type": "tribe_agreement_list_returned", + "version": 1 + }, + "body": { + "agreements": { + "Agree1": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + }, + "Agree2": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + } + } + } +}` + + GET_TRIBE_AGREEMENTS_RESPONSE_NAME = `{ + "meta": { + "code": 200, + "message": "Tribe agreement returned", + "type": "tribe_agreement_returned", + "version": 1 + }, + "body": { + "agreement": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + } + } +}` + + GET_TRIBE_MEMBERS_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Tribe members retrieved", + "type": "tribe_member_list_returned", + "version": 1 + }, + "body": { + "members": [ + "one", + "two", + "three" + ] + } +}` + + GET_TRIBE_MEMBER_NAME = `{ + "meta": { + "code": 200, + "message": "Tribe member details retrieved", + "type": "tribe_member_details_returned", + "version": 1 + }, + "body": { + "name": "", + "plugin_agreement": "", + "tags": null, + "task_agreements": null + } +}` + + DELETE_TRIBE_AGREEMENT_RESPONSE_NAME = `{ + "meta": { + "code": 200, + "message": "Tribe agreement deleted", + "type": "tribe_agreement_deleted", + "version": 1 + }, + "body": { + "agreements": { + "Agree1": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + }, + "Agree2": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + } + } + } +}` + + JOIN_TRIBE_AGREEMENT_RESPONSE_NAME_JOIN = `{ + "meta": { + "code": 200, + "message": "Tribe agreement joined", + "type": "tribe_agreement_joined", + "version": 1 + }, + "body": { + "agreement": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + } + } +}` + + LEAVE_TRIBE_AGREEMENT_REPSONSE_NAME_LEAVE = `{ + "meta": { + "code": 200, + "message": "Tribe agreement left", + "type": "tribe_agreement_left", + "version": 1 + }, + "body": { + "agreement": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + } + } +}` + + ADD_TRIBE_AGREEMENT_RESPONSE = `{ + "meta": { + "code": 200, + "message": "Tribe agreement created", + "type": "tribe_agreement_created", + "version": 1 + }, + "body": { + "agreements": { + "Agree1": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + }, + "Agree2": { + "name": "Agree1", + "plugin_agreement": { + "plugins": [ + { + "name": "mockVersion", + "version": 1, + "type": 0 + } + ] + }, + "task_agreement": { + "tasks": [ + { + "id": "mockTask", + "start_on_create": true + } + ] + }, + "members": { + "member1": { + "name": "mockName" + } + } + } + } + } +}` +) diff --git a/mgmt/rest/plugin_test.go b/mgmt/rest/plugin_test.go index e39bfeb4a..772a3edce 100644 --- a/mgmt/rest/plugin_test.go +++ b/mgmt/rest/plugin_test.go @@ -23,86 +23,13 @@ package rest import ( "testing" - "time" - "github.com/intelsdi-x/snap/control/plugin/cpolicy" - "github.com/intelsdi-x/snap/core" - "github.com/intelsdi-x/snap/core/serror" + "github.com/intelsdi-x/snap/mgmt/rest/fixtures" . "github.com/smartystreets/goconvey/convey" ) -type MockLoadedPlugin struct { - MyName string - MyType string -} - -func (m MockLoadedPlugin) Name() string { return m.MyName } -func (m MockLoadedPlugin) TypeName() string { return m.MyType } -func (m MockLoadedPlugin) Version() int { return 0 } -func (m MockLoadedPlugin) Plugin() string { return "" } -func (m MockLoadedPlugin) IsSigned() bool { return false } -func (m MockLoadedPlugin) Status() string { return "" } -func (m MockLoadedPlugin) PluginPath() string { return "" } -func (m MockLoadedPlugin) LoadedTimestamp() *time.Time { - now := time.Now() - return &now -} -func (m MockLoadedPlugin) Policy() *cpolicy.ConfigPolicy { return nil } - -// have my mock object also support AvailablePlugin -func (m MockLoadedPlugin) HitCount() int { return 0 } -func (m MockLoadedPlugin) LastHit() time.Time { - return time.Now() -} -func (m MockLoadedPlugin) ID() uint32 { return 0 } - -type MockManagesMetrics struct{} - -func (m MockManagesMetrics) MetricCatalog() ([]core.CatalogedMetric, error) { - return nil, nil -} -func (m MockManagesMetrics) FetchMetrics(core.Namespace, int) ([]core.CatalogedMetric, error) { - return nil, nil -} -func (m MockManagesMetrics) GetMetricVersions(core.Namespace) ([]core.CatalogedMetric, error) { - return nil, nil -} -func (m MockManagesMetrics) GetMetric(core.Namespace, int) (core.CatalogedMetric, error) { - return nil, nil -} -func (m MockManagesMetrics) Load(*core.RequestedPlugin) (core.CatalogedPlugin, serror.SnapError) { - return nil, nil -} -func (m MockManagesMetrics) Unload(core.Plugin) (core.CatalogedPlugin, serror.SnapError) { - return nil, nil -} - -func (m MockManagesMetrics) PluginCatalog() core.PluginCatalog { - return []core.CatalogedPlugin{ - MockLoadedPlugin{MyName: "foo", MyType: "collector"}, - MockLoadedPlugin{MyName: "bar", MyType: "publisher"}, - MockLoadedPlugin{MyName: "foo", MyType: "collector"}, - MockLoadedPlugin{MyName: "baz", MyType: "publisher"}, - MockLoadedPlugin{MyName: "foo", MyType: "processor"}, - MockLoadedPlugin{MyName: "foobar", MyType: "processor"}, - } -} -func (m MockManagesMetrics) AvailablePlugins() []core.AvailablePlugin { - return []core.AvailablePlugin{ - MockLoadedPlugin{MyName: "foo", MyType: "collector"}, - MockLoadedPlugin{MyName: "bar", MyType: "publisher"}, - MockLoadedPlugin{MyName: "foo", MyType: "collector"}, - MockLoadedPlugin{MyName: "baz", MyType: "publisher"}, - MockLoadedPlugin{MyName: "foo", MyType: "processor"}, - MockLoadedPlugin{MyName: "foobar", MyType: "processor"}, - } -} -func (m MockManagesMetrics) GetAutodiscoverPaths() []string { - return nil -} - func TestGetPlugins(t *testing.T) { - mm := MockManagesMetrics{} + mm := fixtures.MockManagesMetrics{} host := "localhost" Convey("Test getPlugns method", t, func() { Convey("Without details", func() { diff --git a/mgmt/rest/rest_func_test.go b/mgmt/rest/rest_func_test.go index 38b3297a5..4960b6492 100644 --- a/mgmt/rest/rest_func_test.go +++ b/mgmt/rest/rest_func_test.go @@ -445,29 +445,6 @@ func deletePluginConfigItem(port int, typ string, name, ver string, fields []str return getAPIResponse(resp) } -// Since we do not have a global snap package that could be imported -// we create a mock config struct to mock what is in snapd.go - -type mockConfig struct { - LogLevel int `json:"-"yaml:"-"` - GoMaxProcs int `json:"-"yaml:"-"` - LogPath string `json:"-"yaml:"-"` - Control *control.Config - Scheduler *scheduler.Config `json:"-",yaml:"-"` - RestAPI *Config `json:"-",yaml:"-"` -} - -func getDefaultMockConfig() *mockConfig { - return &mockConfig{ - LogLevel: 3, - GoMaxProcs: 1, - LogPath: "", - Control: control.GetDefaultConfig(), - Scheduler: scheduler.GetDefaultConfig(), - RestAPI: GetDefaultConfig(), - } -} - // REST API instances that are started are killed when the tests end. // When we eventually have a REST API Stop command this can be killed. func startAPI(cfg *mockConfig) *restAPIInstance { diff --git a/mgmt/rest/rest_v1_test.go b/mgmt/rest/rest_v1_test.go new file mode 100644 index 000000000..dc62c5904 --- /dev/null +++ b/mgmt/rest/rest_v1_test.go @@ -0,0 +1,587 @@ +// +build medium + +/* +http://www.apache.org/licenses/LICENSE-2.0.txt + + +Copyright 2015 Intel Corporation + +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. +*/ + +package rest + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "strings" + "testing" + + . "github.com/smartystreets/goconvey/convey" + + log "github.com/Sirupsen/logrus" + "github.com/intelsdi-x/snap/core/cdata" + "github.com/intelsdi-x/snap/core/ctypes" + "github.com/intelsdi-x/snap/mgmt/rest/fixtures" +) + +var ( + LOG_LEVEL = log.WarnLevel + SNAP_PATH = os.ExpandEnv(os.Getenv("SNAP_PATH")) + MOCK_PLUGIN_PATH1 = SNAP_PATH + "/plugin/snap-plugin-collector-mock1" + MOCK_TASK_PATH1 = SNAP_PATH + "/tasks/snap-task-collector-mock1" +) + +type restAPIInstance struct { + port int + server *Server +} + +func startV1API(cfg *mockConfig) *restAPIInstance { + log.SetLevel(LOG_LEVEL) + r, _ := New(cfg.RestAPI) + mockMetricManager := &fixtures.MockManagesMetrics{} + mockTaskManager := &fixtures.MockTaskManager{} + mockConfigManager := &fixtures.MockConfigManager{} + mockTribeManager := &fixtures.MockTribeManager{} + r.BindMetricManager(mockMetricManager) + r.BindTaskManager(mockTaskManager) + r.BindConfigManager(mockConfigManager) + r.BindTribeManager(mockTribeManager) + go func(ch <-chan error) { + // Block on the error channel. Will return exit status 1 for an error or + // just return if the channel closes. + err, ok := <-ch + if !ok { + return + } + log.Fatal(err) + }(r.Err()) + r.SetAddress("127.0.0.1:0") + r.Start() + return &restAPIInstance{ + port: r.Port(), + server: r, + } +} + +func TestV1(t *testing.T) { + r := startV1API(getDefaultMockConfig()) + Convey("Test REST API V1", t, func() { + + //////////TEST-PLUGIN-ROUTES///////////////// + Convey("Get plugins - v1/plugins", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/plugins", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_PLUGINS_RESPONSE, r.port, r.port, + r.port, r.port, r.port, r.port), + ShouldResemble, + string(body)) + }) + Convey("Get plugins - v1/plugins/:type", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/plugins/collector", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_PLUGINS_RESPONSE_TYPE, r.port, r.port), + ShouldResemble, + string(body)) + }) + Convey("Get plugins - v1/plugins/:type:name", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/plugins/publisher/bar", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_PLUGINS_RESPONSE_TYPE_NAME, r.port), + ShouldResemble, + string(body)) + }) + Convey("Get plugins - v1/plugins/:type:name:version", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/plugins/publisher/bar/3", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_PLUGINS_RESPONSE_TYPE_NAME_VERSION, r.port), + ShouldResemble, + string(body)) + }) + + Convey("Post plugins - v1/plugins/:type:name", func(c C) { + f, err := os.Open(MOCK_PLUGIN_PATH1) + defer f.Close() + So(err, ShouldBeNil) + + // We create a pipe so that we can write the file in multipart + // format and read it in to the body of the http request + reader, writer := io.Pipe() + mwriter := multipart.NewWriter(writer) + bufin := bufio.NewReader(f) + + // A go routine is needed since we must write the multipart file + // to the pipe so we can read from it in the http call + go func() { + part, err := mwriter.CreateFormFile("snap-plugins", "mock") + c.So(err, ShouldBeNil) + bufin.WriteTo(part) + mwriter.Close() + writer.Close() + }() + + resp1, err1 := http.Post( + fmt.Sprintf("http://localhost:%d/v1/plugins", r.port), + mwriter.FormDataContentType(), reader) + So(err1, ShouldBeNil) + So(resp1.StatusCode, ShouldEqual, 201) + }) + + Convey("Delete plugins - v1/plugins/:type:name:version", func() { + c := &http.Client{} + pluginName := "foo" + pluginType := "collector" + pluginVersion := 2 + req, err := http.NewRequest( + "DELETE", + fmt.Sprintf("http://localhost:%d/v1/plugins/%s/%s/%d", + r.port, + pluginType, + pluginName, + pluginVersion), + bytes.NewReader([]byte{})) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.UNLOAD_PLUGIN_RESPONSE), + ShouldResemble, + string(body)) + }) + + Convey("Get plugin config items - v1/plugins/:type/:name/:version/config", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/plugins/publisher/bar/3/config", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_PLUGIN_CONFIG_ITEM), + ShouldResemble, + string(body)) + }) + + Convey("Set plugin config item- v1/plugins/:type/:name/:version/config", func() { + c := &http.Client{} + pluginName := "foo" + pluginType := "collector" + pluginVersion := 2 + cd := cdata.NewNode() + cd.AddItem("user", ctypes.ConfigValueStr{Value: "Jane"}) + body, err := cd.MarshalJSON() + So(err, ShouldBeNil) + + req, err := http.NewRequest( + "PUT", + fmt.Sprintf("http://localhost:%d/v1/plugins/%s/%s/%d/config", + r.port, + pluginType, + pluginName, + pluginVersion), + bytes.NewReader(body)) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.SET_PLUGIN_CONFIG_ITEM), + ShouldResemble, + string(body)) + + }) + + Convey("Delete plugin config item - /v1/plugins/:type/:name/:version/config", func() { + c := &http.Client{} + pluginName := "foo" + pluginType := "collector" + pluginVersion := 2 + cd := []string{"foo"} + body, err := json.Marshal(cd) + So(err, ShouldBeNil) + req, err := http.NewRequest( + "DELETE", + fmt.Sprintf("http://localhost:%d/v1/plugins/%s/%s/%d/config", + r.port, + pluginType, + pluginName, + pluginVersion), + bytes.NewReader(body)) + + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.DELETE_PLUGIN_CONFIG_ITEM), + ShouldResemble, + string(body)) + + }) + + //////////TEST-METRIC-ROUTES///////////////// + + Convey("Get metrics - v1/metrics", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/metrics", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + resp1, err := url.QueryUnescape(string(body)) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_METRICS_RESPONSE, r.port), + ShouldResemble, + resp1) + }) + + Convey("Get metrics from tree - v1/metrics/*namespace", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/metrics/*namespace", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + resp1, err := url.QueryUnescape(string(body)) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_METRICS_RESPONSE, r.port), + ShouldResemble, + resp1) + }) + + //////////TEST-TASK-ROUTES///////////////// + + Convey("Get tasks - v1/tasks", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/tasks", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + responses := []string{ + fmt.Sprintf(fixtures.GET_TASKS_RESPONSE, r.port, r.port), + fmt.Sprintf(fixtures.GET_TASKS_RESPONSE2, r.port, r.port), + } + // GetTasks returns an unordered map, + // thus there is more than one possible response + So( + responses, + ShouldContain, + string(body)) + }) + + Convey("Get task - v1/tasks/:id", func() { + taskID := "1234" + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/tasks/:%s", r.port, taskID)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_TASK_RESPONSE, r.port), + ShouldResemble, + string(body)) + }) + + Convey("Watch tasks - v1/tasks/:id/watch", func() { + taskID := "1234" + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/tasks/:%s/watch", r.port, taskID)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + }) + + Convey("Add tasks - v1/tasks", func() { + reader := strings.NewReader(fixtures.TASK) + resp, err := http.Post( + fmt.Sprintf("http://localhost:%d/v1/tasks", r.port), + http.DetectContentType([]byte(fixtures.TASK)), + reader) + So(err, ShouldBeNil) + So(resp, ShouldNotBeEmpty) + So(resp.StatusCode, ShouldEqual, 201) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.ADD_TASK_RESPONSE, r.port), + ShouldResemble, + string(body)) + }) + + Convey("Start tasks - v1/tasks/:id/start", func() { + c := &http.Client{} + taskID := "MockTask1234" + cd := cdata.NewNode() + cd.AddItem("user", ctypes.ConfigValueStr{Value: "Kelly"}) + body, err := cd.MarshalJSON() + So(err, ShouldBeNil) + + req, err := http.NewRequest( + "PUT", + fmt.Sprintf("http://localhost:%d/v1/tasks/%s/start", r.port, taskID), + bytes.NewReader(body)) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.START_TASK_RESPONSE_ID_START), + ShouldResemble, + string(body)) + }) + + Convey("Stop tasks - v1/tasks/:id/stop", func() { + c := &http.Client{} + taskID := "MockTask1234" + cd := cdata.NewNode() + cd.AddItem("user", ctypes.ConfigValueStr{Value: "Kelly"}) + body, err := cd.MarshalJSON() + So(err, ShouldBeNil) + + req, err := http.NewRequest( + "PUT", + fmt.Sprintf("http://localhost:%d/v1/tasks/%s/stop", r.port, taskID), + bytes.NewReader(body)) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.STOP_TASK_RESPONSE_ID_STOP), + ShouldResemble, + string(body)) + }) + + Convey("Enable tasks - v1/tasks/:id/enable", func() { + c := &http.Client{} + taskID := "MockTask1234" + cd := cdata.NewNode() + cd.AddItem("user", ctypes.ConfigValueStr{Value: "Kelly"}) + body, err := cd.MarshalJSON() + So(err, ShouldBeNil) + + req, err := http.NewRequest( + "PUT", + fmt.Sprintf("http://localhost:%d/v1/tasks/%s/enable", r.port, taskID), + bytes.NewReader(body)) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.ENABLE_TASK_RESPONSE_ID_ENABLE), + ShouldResemble, + string(body)) + }) + + Convey("Remove tasks - V1/tasks/:id", func() { + c := &http.Client{} + taskID := "MockTask1234" + cd := []string{"foo"} + body, err := json.Marshal(cd) + So(err, ShouldBeNil) + req, err := http.NewRequest( + "DELETE", + fmt.Sprintf("http://localhost:%d/v1/tasks/%s", + r.port, + taskID), + bytes.NewReader([]byte{})) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.REMOVE_TASK_RESPONSE_ID), + ShouldResemble, + string(body)) + }) + + //////////TEST-TRIBE-ROUTES///////////////// + + Convey("Get tribe agreements - v1/tribe/agreements", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/tribe/agreements", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_TRIBE_AGREEMENTS_RESPONSE), + ShouldResemble, + string(body)) + }) + + Convey("Add tribe agreements - /v1/tribe/agreements", func() { + agreement := "{\"Name\": \"Agree2\"}" + reader := strings.NewReader(agreement) + resp, err := http.Post(fmt.Sprintf("http://localhost:%d/v1/tribe/agreements", r.port), + http.DetectContentType([]byte(agreement)), reader) + So(err, ShouldBeNil) + So(resp, ShouldNotBeEmpty) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.ADD_TRIBE_AGREEMENT_RESPONSE), + ShouldResemble, + string(body)) + }) + + Convey("Get tribe agreements - v1/tribe/agreements/:name", func() { + tribeName := "Agree1" + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/tribe/agreements/%s", r.port, tribeName)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_TRIBE_AGREEMENTS_RESPONSE_NAME), + ShouldResemble, + string(body)) + }) + + Convey("Get tribe members - v1/tribe/members", func() { + resp, err := http.Get( + fmt.Sprintf("http://localhost:%d/v1/tribe/members", r.port)) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, 200) + body, err := ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.GET_TRIBE_MEMBERS_RESPONSE), + ShouldResemble, + string(body)) + }) + + Convey("Delete tribe agreement - v1/tribe/agreements/:name", func() { + c := &http.Client{} + tribeName := "Agree1" + cd := []string{"foo"} + body, err := json.Marshal(cd) + So(err, ShouldBeNil) + req, err := http.NewRequest( + "DELETE", + fmt.Sprintf("http://localhost:%d/v1/tribe/agreements/%s", + r.port, + tribeName), + bytes.NewReader([]byte{})) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.DELETE_TRIBE_AGREEMENT_RESPONSE_NAME), + ShouldResemble, + string(body)) + }) + + Convey("Leave tribe agreement - v1/tribe/agreements/:name/leave", func() { + c := &http.Client{} + tribeName := "Agree1" + cd := map[string]string{"Apple": "a", "Ball": "b", "Cat": "c"} + body, err := json.Marshal(cd) + So(err, ShouldBeNil) + req, err := http.NewRequest( + "DELETE", + fmt.Sprintf("http://localhost:%d/v1/tribe/agreements/%s/leave", + r.port, + tribeName), + bytes.NewReader(body)) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.LEAVE_TRIBE_AGREEMENT_REPSONSE_NAME_LEAVE), + ShouldResemble, + string(body)) + }) + + Convey("Join tribe agreement - v1/tribe/agreements/:name/join", func() { + c := &http.Client{} + tribeName := "Agree1" + cd := cdata.NewNode() + cd.AddItem("user", ctypes.ConfigValueStr{Value: "Kelly"}) + body, err := cd.MarshalJSON() + So(err, ShouldBeNil) + + req, err := http.NewRequest( + "PUT", + fmt.Sprintf("http://localhost:%d/v1/tribe/agreements/%s/join", r.port, tribeName), + bytes.NewReader(body)) + So(err, ShouldBeNil) + resp, err := c.Do(req) + So(err, ShouldBeNil) + So(resp.StatusCode, ShouldEqual, http.StatusOK) + body, err = ioutil.ReadAll(resp.Body) + So(err, ShouldBeNil) + So( + fmt.Sprintf(fixtures.JOIN_TRIBE_AGREEMENT_RESPONSE_NAME_JOIN), + ShouldResemble, + string(body)) + + }) + }) +}