ssc 0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +14 -0
- data/Gemfile.lock +39 -0
- data/README.rdoc +22 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/ssc +4 -231
- data/lib/directory_manager.rb +194 -0
- data/lib/handlers/all.rb +33 -0
- data/lib/handlers/appliance.rb +74 -0
- data/lib/handlers/file.rb +102 -0
- data/lib/handlers/helper.rb +79 -0
- data/lib/handlers/package.rb +153 -0
- data/lib/handlers/repository.rb +109 -0
- data/lib/handlers/template.rb +25 -0
- data/lib/ssc.rb +18 -0
- data/test/handlers/test_appliance.rb +20 -0
- data/test/handlers/test_helper.rb +45 -0
- data/test/handlers/test_repository.rb +20 -0
- data/test/handlers/test_template.rb +27 -0
- data/test/helper.rb +19 -0
- data/test/test_argument_parser.rb +55 -0
- data/test/test_directory_manager.rb +40 -0
- data/test/test_ssc.rb +39 -0
- metadata +147 -31
- data/README +0 -1
- data/lib/appliancehandler.rb +0 -69
- data/lib/buildhandler.rb +0 -125
- data/lib/checkouthandler.rb +0 -322
- data/lib/commandhandler.rb +0 -38
- data/lib/request.rb +0 -29
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module SSC
|
4
|
+
module Handler
|
5
|
+
class OverlayFile < Base
|
6
|
+
|
7
|
+
|
8
|
+
no_tasks do
|
9
|
+
cattr_reader :local_source
|
10
|
+
@@local_source= 'files/'
|
11
|
+
end
|
12
|
+
|
13
|
+
# must be run in appliance directory
|
14
|
+
# takes the following argument:
|
15
|
+
# file_path => (relative positions("." and "..") allowed and ~ for home directory allowed)
|
16
|
+
# takes the following options:
|
17
|
+
# --path="/path/to/file_directory/" => optional (by default it is the path of the file on the local system)
|
18
|
+
# --name="file_name" => optional (by default it is the name of the file on the local system)
|
19
|
+
# --permissions="0766" => optional (default: 0755)
|
20
|
+
# --owner="user" => optional (default: root)
|
21
|
+
desc 'file create PATH', 'create a new overlay file'
|
22
|
+
require_appliance_id
|
23
|
+
allow_remote_option
|
24
|
+
method_option :path, :type => :string, :default => ''
|
25
|
+
method_option :name, :type => :string, :default => ''
|
26
|
+
method_option :permissions, :type => :string, :default => '0755'
|
27
|
+
method_option :owner, :type => :string, :default => 'root'
|
28
|
+
def create(path)
|
29
|
+
absolute_path= File.expand_path(path)
|
30
|
+
optional_file_params= {:permissions => options.permissions,
|
31
|
+
:owner => options.owner}
|
32
|
+
file_dir, file_name= File.split(absolute_path)
|
33
|
+
file_dir = options.path == '' ? file_dir : options.path
|
34
|
+
file_name = options.name == '' ? file_name : options.name
|
35
|
+
id= nil
|
36
|
+
if options.remote?
|
37
|
+
require_appliance do |appliance|
|
38
|
+
file_params= ({:path => file_dir, :filename => file_name})
|
39
|
+
file_params.merge!(optional_file_params)
|
40
|
+
File.open(absolute_path) do |file|
|
41
|
+
file= StudioApi::File.upload(file, appliance.id, file_params)
|
42
|
+
id= file.id.to_i
|
43
|
+
end
|
44
|
+
say "Overlay file saved. Id: #{id}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
local_copy= initiate_file(file_dir, file_name, id)
|
48
|
+
say "Created #{local_copy}"
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'file show FILE_NAME', 'show the contents of the file'
|
52
|
+
require_appliance_id
|
53
|
+
allow_remote_option
|
54
|
+
def show(file_name)
|
55
|
+
if options.remote?
|
56
|
+
id= find_file_id(file_name)
|
57
|
+
response= StudioApi::File.find(id)
|
58
|
+
say response.content
|
59
|
+
else
|
60
|
+
say show_file(file_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'file diff FILE_NAME', 'show the diff of the remote file and the local one'
|
65
|
+
require_appliance_id
|
66
|
+
def diff(file_name)
|
67
|
+
begin
|
68
|
+
id= find_file_id(file_name)
|
69
|
+
file_content= StudioApi::File.find(id).content
|
70
|
+
rescue
|
71
|
+
say "unable to connect or not in appliance directory", :red
|
72
|
+
end
|
73
|
+
|
74
|
+
begin
|
75
|
+
tempfile=Tempfile.new('ssc_file')
|
76
|
+
tempfile.write(file_content)
|
77
|
+
say find_diff(tempfile.path, full_local_file_path(file_name))
|
78
|
+
tempfile.close; tempfile.unlink
|
79
|
+
rescue Errno::ENOENT
|
80
|
+
say "diff not installed", :red
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'file list', 'show all overlay files'
|
85
|
+
require_appliance_id
|
86
|
+
allow_remote_option
|
87
|
+
def list
|
88
|
+
require_appliance do |appliance|
|
89
|
+
out= if options.remote? || file_list_empty?
|
90
|
+
response= StudioApi::File.find(:all, :params => {:appliance_id => appliance.id})
|
91
|
+
response.collect do |file|
|
92
|
+
{file.filename => {"id" => id, "path" => file.path}}
|
93
|
+
end
|
94
|
+
else
|
95
|
+
list_local_files
|
96
|
+
end
|
97
|
+
say out.to_yaml
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module SSC
|
4
|
+
module Handler
|
5
|
+
module Helper
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.class_eval do
|
10
|
+
|
11
|
+
end
|
12
|
+
base.send :include, InstanceMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def require_authorization
|
17
|
+
config= get_config
|
18
|
+
method_option :username, :type => :string, :required => true,
|
19
|
+
:default => config["username"]
|
20
|
+
method_option :password, :type => :string, :required => true,
|
21
|
+
:default => config["password"]
|
22
|
+
method_option :proxy, :type => :string
|
23
|
+
method_option :timeout, :type => :string
|
24
|
+
end
|
25
|
+
|
26
|
+
def require_appliance_id
|
27
|
+
require_authorization
|
28
|
+
config= get_config
|
29
|
+
method_option :appliance_id, :type => :numeric, :required => true,
|
30
|
+
:default => config["appliance_id"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def allow_remote_option
|
34
|
+
method_option :remote, :type => :boolean, :default => false
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_config
|
38
|
+
begin
|
39
|
+
YAML::load File.read(File.join('.', '.sscrc'))
|
40
|
+
rescue Errno::ENOENT
|
41
|
+
return {'username' => nil, 'password' => nil, 'appliance_id' => nil}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module InstanceMethods
|
47
|
+
|
48
|
+
# Establish connection to Suse Studio with username, password
|
49
|
+
def connect(user, pass, connection_options)
|
50
|
+
@connection= StudioApi::Connection.new(user, pass, self.class::API_URL, connection_options)
|
51
|
+
StudioApi::Util.configure_studio_connection @connection
|
52
|
+
end
|
53
|
+
|
54
|
+
def filter_options(options, keys)
|
55
|
+
keys.inject({}) do |out, key|
|
56
|
+
(options.respond_to?(key) && options.send(key)) ? out.merge({ key => options.send(key) }) : out
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def say_array(array, color= nil)
|
61
|
+
# NOTE
|
62
|
+
# Included for those methods that still return arrays for printing
|
63
|
+
# Can be removed eventually
|
64
|
+
# Still seems to be a nice way to format the text output of methods
|
65
|
+
say array.join("\n"), color
|
66
|
+
end
|
67
|
+
|
68
|
+
def require_appliance
|
69
|
+
if options.appliance_id
|
70
|
+
yield(StudioApi::Appliance.find(options.appliance_id))
|
71
|
+
else
|
72
|
+
raise "Unable to find the appliance"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module SSC
|
2
|
+
module Handler
|
3
|
+
class Package < Base
|
4
|
+
|
5
|
+
# Structure of the 'software' file:
|
6
|
+
#
|
7
|
+
# ---
|
8
|
+
# list:
|
9
|
+
# installed:
|
10
|
+
# <name>:
|
11
|
+
# version: <package.version>
|
12
|
+
# .
|
13
|
+
# .
|
14
|
+
# .
|
15
|
+
# selected:
|
16
|
+
# <name>:
|
17
|
+
# .
|
18
|
+
# .
|
19
|
+
# .
|
20
|
+
# add:
|
21
|
+
# <name>
|
22
|
+
# .
|
23
|
+
# .
|
24
|
+
# .
|
25
|
+
# remove:
|
26
|
+
# <name>
|
27
|
+
# .
|
28
|
+
# .
|
29
|
+
# .
|
30
|
+
# ban:
|
31
|
+
# <name>
|
32
|
+
# .
|
33
|
+
# .
|
34
|
+
# .
|
35
|
+
# unban:
|
36
|
+
# <name>
|
37
|
+
# .
|
38
|
+
# .
|
39
|
+
# .
|
40
|
+
|
41
|
+
no_tasks do
|
42
|
+
cattr_reader :local_source
|
43
|
+
@@local_source= 'software'
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'package search SEARCH_STRING', 'search available packages and patterns'
|
47
|
+
require_appliance_id
|
48
|
+
method_option :all_repos, :type => :boolean, :default => true
|
49
|
+
def search(search_string)
|
50
|
+
require_appliance_id(@options) do |appliance|
|
51
|
+
params= {:all_repos => options.all_repos} if options.all_repos
|
52
|
+
software= appliance.search_software(search_string, params)
|
53
|
+
say_array software.collect do |software|
|
54
|
+
"#{software.name} v#{software.version}. Repo Id: #{software.repository_id}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'package list [selected|installed]', 'list all selected or installed packages'
|
60
|
+
require_appliance_id
|
61
|
+
allow_remote_option
|
62
|
+
method_option :build_id, :type => :numeric
|
63
|
+
def list(type)
|
64
|
+
say("installed | selected package only", :red) unless ['installed', 'selected'].include?(type)
|
65
|
+
out= if options.remote? || no_local_list?
|
66
|
+
require_appliance do |appliance|
|
67
|
+
params= {:build_id => options.build_id} if options.build_id
|
68
|
+
software= appliance.send("#{type}_software")
|
69
|
+
formatted_list= software.collect do |package|
|
70
|
+
version= package.version ? { "version" => package.version } : nil
|
71
|
+
{package.name => version}
|
72
|
+
end
|
73
|
+
save(type, formatted_list)
|
74
|
+
formatted_list
|
75
|
+
end
|
76
|
+
else
|
77
|
+
read(type)
|
78
|
+
end
|
79
|
+
say out.to_yaml
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
desc 'package add NAME', 'add a package to the appliance'
|
84
|
+
require_appliance_id
|
85
|
+
allow_remote_option
|
86
|
+
def add(name)
|
87
|
+
if options.remote?
|
88
|
+
require_appliance do |appliance|
|
89
|
+
response= appliance.add_package(name)
|
90
|
+
say case response['state']
|
91
|
+
when "fixed"
|
92
|
+
"Package Added. State: #{response['state']}"
|
93
|
+
when "equal"
|
94
|
+
"Package Not Added."
|
95
|
+
when "broken"
|
96
|
+
"Package Added. State: #{response['state']}. Please resolve dependencies"
|
97
|
+
else
|
98
|
+
"unknown code"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
else
|
102
|
+
save("add", [ name ])
|
103
|
+
say "#{name} marked for addition"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
desc 'package remove NAME', 'remove a package from the appliance'
|
108
|
+
require_appliance_id
|
109
|
+
allow_remote_option
|
110
|
+
def remove(name)
|
111
|
+
if options.remote?
|
112
|
+
require_appliance do |appliance|
|
113
|
+
response= appliance.remove_package(name)
|
114
|
+
say "State: #{response['state']}"
|
115
|
+
end
|
116
|
+
else
|
117
|
+
save("remove", [ name ])
|
118
|
+
say "#{name} marked for removal"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
desc 'package ban NAME', 'ban a package from the appliance'
|
123
|
+
require_appliance_id
|
124
|
+
allow_remote_option
|
125
|
+
def ban(name)
|
126
|
+
if options.remote?
|
127
|
+
require_appliance do |appliance|
|
128
|
+
response= appliance.ban_package(name)
|
129
|
+
response.collect{|key, val| "#{key}: #{val}"}
|
130
|
+
end
|
131
|
+
else
|
132
|
+
save("ban", [ name ])
|
133
|
+
say "#{name} marked to be banned"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
desc 'package unban NAME', 'unban a package for the appliance'
|
138
|
+
require_appliance_id
|
139
|
+
allow_remote_option
|
140
|
+
def unban(name)
|
141
|
+
if options.remote?
|
142
|
+
require_appliance do |appliance|
|
143
|
+
response= appliance.unban_package(name)
|
144
|
+
response.collect{|key, val| "#{key}: #{val}"}
|
145
|
+
end
|
146
|
+
else
|
147
|
+
save("unban", [ name ])
|
148
|
+
say "#{name} marked to be unbanned"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module SSC
|
2
|
+
module Handler
|
3
|
+
class Repository < Base
|
4
|
+
|
5
|
+
# Structure of the 'repositories' file:
|
6
|
+
#
|
7
|
+
# ---
|
8
|
+
# list:
|
9
|
+
# <id>:
|
10
|
+
# name: <repo.name>
|
11
|
+
# type: <repo.type>
|
12
|
+
# base_system: <repo.base_url>
|
13
|
+
# .
|
14
|
+
# .
|
15
|
+
# .
|
16
|
+
# add:
|
17
|
+
# <id>
|
18
|
+
# .
|
19
|
+
# .
|
20
|
+
# .
|
21
|
+
# remove:
|
22
|
+
# <id>
|
23
|
+
# .
|
24
|
+
# .
|
25
|
+
# .
|
26
|
+
# import:
|
27
|
+
# name: <name>
|
28
|
+
# url: <url>
|
29
|
+
no_tasks do
|
30
|
+
cattr_reader :local_source
|
31
|
+
@@local_source= 'repositories'
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "repository search SEARCH_STRING", "search all available repositories"
|
35
|
+
require_authorization
|
36
|
+
method_option :base_system, :type => :string
|
37
|
+
def search(search_string)
|
38
|
+
params= {:filter => search_string}
|
39
|
+
params= params.merge({:base_system => options.base_system}) if options.base_system
|
40
|
+
repos= StudioApi::Repository.find(:all, :params => params)
|
41
|
+
say_array(repos.collect do |repo|
|
42
|
+
"#{repo.id}.) #{repo.name}: #{repo.base_url}
|
43
|
+
#{[repo.matches.software_name].flatten.join(', ')}\n"
|
44
|
+
end)
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "repository list", "list all repositories in a given appliance"
|
48
|
+
require_appliance_id
|
49
|
+
allow_remote_option
|
50
|
+
def list
|
51
|
+
list= if options.remote? || no_local_list?
|
52
|
+
require_appliance do |appliance|
|
53
|
+
appliance.repositories.collect do |repo|
|
54
|
+
{ repo.id => { 'name' => repo.name,
|
55
|
+
'type' => repo.type,
|
56
|
+
'base_system' => repo.base_system}}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
else
|
60
|
+
read('list')
|
61
|
+
end
|
62
|
+
save('list', list) unless options.remote?
|
63
|
+
say list.to_yaml
|
64
|
+
end
|
65
|
+
|
66
|
+
desc 'repository add REPO_IDS', 'add existing repositories to the appliance'
|
67
|
+
require_appliance_id
|
68
|
+
allow_remote_option
|
69
|
+
def add(*repo_ids)
|
70
|
+
if options.remote?
|
71
|
+
require_appliance do |appliance|
|
72
|
+
response= appliance.add_repository(repo_ids)
|
73
|
+
say "Added"+( response.collect{|repos| repos.name} ).join(", ")
|
74
|
+
end
|
75
|
+
else
|
76
|
+
save('add', repo_ids)
|
77
|
+
say "Marked the following for addition #{repo_ids.join(", ")}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'repository remove REPO_IDS', 'remove existing repositories from appliance'
|
82
|
+
require_appliance_id
|
83
|
+
allow_remote_option
|
84
|
+
def remove(*repo_ids)
|
85
|
+
if options.remote?
|
86
|
+
require_appliance do |appliance|
|
87
|
+
response= appliance.remove_repository(repo_ids)
|
88
|
+
say "Removed #{repo_ids.join(", ")}"
|
89
|
+
end
|
90
|
+
else
|
91
|
+
save('remove', repo_ids)
|
92
|
+
say "Marked the following for removal #{repo_ids.join(", ")}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc 'repository import URL NAME', 'import a 3rd party repository into appliance'
|
97
|
+
allow_remote_option
|
98
|
+
def import(url, name)
|
99
|
+
if options.remote?
|
100
|
+
repository= StudioApi::Repository.import(url, name)
|
101
|
+
say "Added #{repository.name} at #{url}"
|
102
|
+
else
|
103
|
+
save("import", [{"name" => name, "url" => url}])
|
104
|
+
say "Marked #{name} for import"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SSC
|
2
|
+
module Handler
|
3
|
+
class Template < Base
|
4
|
+
|
5
|
+
desc 'template list_sets', 'list all available template sets'
|
6
|
+
require_authorization
|
7
|
+
def list_sets
|
8
|
+
templates= StudioApi::TemplateSet.find(:all)
|
9
|
+
say_array templates.collect {|template| template.name}
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'template list SET_NAME', 'show details of a particular template set'
|
13
|
+
require_authorization
|
14
|
+
def list(name)
|
15
|
+
template_set= StudioApi::TemplateSet.find(name)
|
16
|
+
out = [template_set.name+' : '+template_set.description]
|
17
|
+
out += template_set.template.collect do |appliance|
|
18
|
+
"#{appliance.appliance_id}: #{appliance.name}"
|
19
|
+
end
|
20
|
+
say_array out
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/lib/ssc.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module SSC
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'thor'
|
5
|
+
require 'thor/group'
|
6
|
+
require 'directory_manager'
|
7
|
+
require 'handlers/all'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
module SSC
|
11
|
+
class Base < Thor
|
12
|
+
register Handler::Appliance, :appliance, "appliance", "manage appliances"
|
13
|
+
register Handler::Repository, :repository, "repository","manage repositories"
|
14
|
+
register Handler::Package, :package, "package", "manage packages"
|
15
|
+
register Handler::Template, :template, "template", "manage templates"
|
16
|
+
register Handler::OverlayFile, :file, "file", "manage files"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestHandlerAppliance < Test::Unit::TestCase
|
4
|
+
context "SSC::Handler::Template" do
|
5
|
+
context "#list" do
|
6
|
+
setup do
|
7
|
+
@handler= SSC::Handler::Appliance.new()
|
8
|
+
@handler.stubs(:connect)
|
9
|
+
end
|
10
|
+
|
11
|
+
should "call find(:all) on StudioApi::Appliance" do
|
12
|
+
mock_app_list= mock('appliance list')
|
13
|
+
mock_app_list.stubs(:collect)
|
14
|
+
mock_app_list.stubs(:empty?)
|
15
|
+
StudioApi::Appliance.expects(:find).with(:all).returns(mock_app_list)
|
16
|
+
@handler.list
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestHandlerHelper < Test::Unit::TestCase
|
4
|
+
context "SSC::Handler::Helper" do
|
5
|
+
setup do
|
6
|
+
class TestObject
|
7
|
+
include SSC::Handler::Helper
|
8
|
+
end
|
9
|
+
|
10
|
+
@objekt= TestObject.new
|
11
|
+
end
|
12
|
+
|
13
|
+
context "#connect" do
|
14
|
+
should "create connection and configure StudioApi to use it" do
|
15
|
+
mock_connection= mock('connection')
|
16
|
+
StudioApi::Connection.expects(:new)
|
17
|
+
.with('user', 'pass', 'https://susestudio.com/api/v1/user',
|
18
|
+
{:proxy => 'proxy'})
|
19
|
+
.returns(mock_connection)
|
20
|
+
StudioApi::Util.expects(:configure_studio_connection).with(mock_connection)
|
21
|
+
@objekt.connect('user', 'pass', {:proxy => 'proxy', :another_option => 'value'})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "#filter_options" do
|
26
|
+
should "return a hash of only the specified keys" do
|
27
|
+
out= @objekt.filter_options({:a => 'a', :b => 'b'}, [:a])
|
28
|
+
assert_equal({:a => 'a'}, out)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "#require_appliance_id" do
|
33
|
+
should "raise and error if the appliance id option is not passed" do
|
34
|
+
assert_raise(RuntimeError) { @objekt.require_appliance_id({}) }
|
35
|
+
end
|
36
|
+
|
37
|
+
should "not raise error if appliance id is provided" do
|
38
|
+
StudioApi::Appliance.expects(:find).with(1).returns(nil)
|
39
|
+
assert_nothing_raised do
|
40
|
+
@objekt.require_appliance_id(:appliance_id=>1) {|i| i}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestHandlerRepository < Test::Unit::TestCase
|
4
|
+
context "SSC::Handler::Repository" do
|
5
|
+
setup do
|
6
|
+
@handler= SSC::Handler::Repository.new()
|
7
|
+
@handler.stubs(:connect)
|
8
|
+
end
|
9
|
+
context "#search" do
|
10
|
+
|
11
|
+
should "call .find(:all, params_hash) on StudioApi::Repository" do
|
12
|
+
mock_collection= mock('collection')
|
13
|
+
mock_collection.stubs(:collect)
|
14
|
+
StudioApi::Repository.expects(:find).with(:all, :params => {:filter => 'chess', :base_system => '11.1'}).returns(mock_collection)
|
15
|
+
@handler.instance_variable_set('@options', {:base_system => '11.1'})
|
16
|
+
@handler.search('chess')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestTemplateHandler < Test::Unit::TestCase
|
4
|
+
context "SSC::Handler::Template" do
|
5
|
+
context "#list" do
|
6
|
+
setup do
|
7
|
+
@handler= SSC::Handler::Template.new()
|
8
|
+
@handler.stubs(:connect)
|
9
|
+
end
|
10
|
+
|
11
|
+
should "call .find(:all) on StudioApi::TemplateSet" do
|
12
|
+
mock_template= mock('template_list')
|
13
|
+
mock_template.stubs(:collect)
|
14
|
+
StudioApi::TemplateSet.expects(:find).with(:all).returns(mock_template)
|
15
|
+
@handler.list
|
16
|
+
end
|
17
|
+
|
18
|
+
should "return a list of strings of type 'template.id: template.name'" do
|
19
|
+
mock_template= StudioApi::TemplateSet.new(:name => 'Template Name')
|
20
|
+
StudioApi::TemplateSet.stubs(:find).with(:all).returns([mock_template])
|
21
|
+
assert_equal ["Template Name"],
|
22
|
+
@handler.list
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
require 'mocha'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
require 'ssc'
|
17
|
+
|
18
|
+
class Test::Unit::TestCase
|
19
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestArgumentParser < Test::Unit::TestCase
|
4
|
+
context "SSC::ArgumentParser" do
|
5
|
+
|
6
|
+
context "when arguments are good" do
|
7
|
+
setup do
|
8
|
+
@parser= SSC::ArgumentParser.new(['appliance', 'create', 'act_arg1', 'act_arg2', '--option', 'value', '-o', 'v', '--flag', '-f'])
|
9
|
+
end
|
10
|
+
|
11
|
+
should "set @klass to Appliance" do
|
12
|
+
assert_equal SSC::Handler::Appliance, @parser.klass
|
13
|
+
end
|
14
|
+
|
15
|
+
should "set @action to create" do
|
16
|
+
assert_equal 'create', @parser.action
|
17
|
+
end
|
18
|
+
|
19
|
+
should "set @options to option hash" do
|
20
|
+
assert_equal({:option => 'value',
|
21
|
+
:o => 'v',
|
22
|
+
:flag => true,
|
23
|
+
:f => true }, @parser.options)
|
24
|
+
end
|
25
|
+
|
26
|
+
should "set @action_arguments to argument array" do
|
27
|
+
#only one of the arguments must be taken since create take only one argument
|
28
|
+
assert_equal(['act_arg1'], @parser.action_arguments)
|
29
|
+
end
|
30
|
+
|
31
|
+
should "have the entire list if arity of the method is -1(splat)" do
|
32
|
+
parser= SSC::ArgumentParser.new(['repository', 'add', 'act_arg1', 'act_arg2'])
|
33
|
+
assert_equal(['act_arg1', 'act_arg2'], parser.action_arguments )
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when handler is unknown" do
|
40
|
+
should "raise UnkownOptionError" do
|
41
|
+
assert_raise(SSC::UnkownOptionError) do
|
42
|
+
SSC::ArgumentParser.new(['apliance', 'create'])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when handler method is unknown" do
|
48
|
+
should "raise UnkownOptionError" do
|
49
|
+
assert_raise(SSC::UnkownOptionError) do
|
50
|
+
SSC::ArgumentParser.new(['appliance', 'unkown_method'])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|