snuggie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,98 @@
1
+ # Snuggie [![Build Status](http://travis-ci.org/site5/snuggie.png)][Build Status]
2
+
3
+ Snuggie wraps the Softaculous API in a warm, loving ruby embrace.
4
+
5
+ Snuggie has been tested on on MRI 1.8.4, MRI 1.8.7, MRI 1.9.2,
6
+ Rubinius 2.0.0pre, and JRuby 1.6.2.
7
+
8
+ [Build Status]: http://travis-ci.org/site5/snuggie
9
+
10
+ ## Installation
11
+
12
+ gem install snuggie
13
+
14
+ ## Usage
15
+
16
+ Create a new `Snuggie::NOC` object with your credentials:
17
+
18
+ noc = Snuggie::NOC.new(
19
+ :username => 'marty',
20
+ :password => 'mcSUPERfly'
21
+ )
22
+
23
+ Your Softaculous credentials can also be configured globally:
24
+
25
+ Snuggie.configure do |config|
26
+ config.username = 'marty'
27
+ config.password = 'mcSUPERfly'
28
+ end
29
+
30
+ noc = Snuggie::NOC.new
31
+
32
+ Buy/renew a license
33
+
34
+ noc.buy_license(
35
+ :ip => '127.0.0.1',
36
+ :months_to_add => 1,
37
+ :server_type => :dedicated,
38
+ :auth_email => 'marty@hilldale.edu',
39
+ :auto_renew => true
40
+ )
41
+
42
+ Refund a transaction
43
+
44
+ noc.refund :action_id => 99999
45
+
46
+ List all licenses
47
+
48
+ noc.list_licenses
49
+
50
+ List licenses by IP
51
+
52
+ noc.list_licenses :ip => '127.0.0.1'
53
+
54
+ List all expired licenses
55
+
56
+ noc.list_licenses :expired => 1
57
+
58
+ List licenses expiring in 7 days
59
+
60
+ noc.list_licenses :expired => 2
61
+
62
+ List licenses expiring in 15 days
63
+
64
+ noc.list_licenses :expired => 3
65
+
66
+ Get invoice details
67
+
68
+ noc.invoice_details :invoice_id => 99999
69
+
70
+ Get unbilled transactions for the current month:
71
+
72
+ noc.invoice_details
73
+
74
+ Cancel a license by key
75
+
76
+ noc.cancel_license :key => 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'
77
+
78
+ Cancel a license by IP
79
+
80
+ noc.cancel_license :ip => '127.0.0.1'
81
+
82
+ Get Action/Activity logs for a license
83
+
84
+ noc.license_logs :key => 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'
85
+
86
+ ## Note on Patches/Pull Requests
87
+
88
+ * Fork the project.
89
+ * Make your feature addition or bug fix.
90
+ * Add tests for it. This is important so I don't break it in a future version
91
+ unintentionally.
92
+ * Commit, do not bump version. (If you want to have your own version, that is
93
+ fine but bump version in a commit by itself I can ignore when I pull.)
94
+ * Send me a pull request. Bonus points for topic branches.
95
+
96
+ ## Copyright
97
+
98
+ Copyright (c) 2011 Site5 LLC. See LICENSE for details.
@@ -0,0 +1,31 @@
1
+ $:.unshift 'lib'
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |test|
6
+ test.libs << "test"
7
+ test.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ desc "Open an irb session preloaded with this library"
13
+ task :console do
14
+ sh "irb -rubygems -r ./lib/snuggie.rb -I ./lib"
15
+ end
16
+
17
+ desc "Push a new version to rubygems.org"
18
+ task :publish do
19
+ require 'snuggie/version'
20
+
21
+ ver = Snuggie::Version
22
+
23
+ mkdir("pkg") unless File.exists?("pkg")
24
+
25
+ sh "gem build snuggie.gemspec"
26
+ sh "gem push snuggie-#{ver}.gem"
27
+ sh "git tag -a -m 'Snuggie v#{ver}' v#{ver}"
28
+ sh "git push origin v#{ver}"
29
+ sh "git push origin master"
30
+ sh "mv snuggie-#{ver}.gem pkg"
31
+ end
@@ -0,0 +1,17 @@
1
+ module Snuggie
2
+ autoload :Version, "snuggie/version"
3
+ autoload :Errors, "snuggie/errors"
4
+ autoload :Config, "snuggie/config"
5
+ autoload :NOC, "snuggie/noc"
6
+
7
+ class << self
8
+ attr_accessor :config
9
+ end
10
+
11
+ def self.configure
12
+ yield config if block_given?
13
+ config
14
+ end
15
+
16
+ self.config = Config.new
17
+ end
@@ -0,0 +1,10 @@
1
+ module Snuggie
2
+ # The Snuggie::Config class stores your Softaculous API credentials
3
+ class Config
4
+ # Your Softaculous username
5
+ attr_accessor :username
6
+
7
+ # Your Softaculous password
8
+ attr_accessor :password
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ module Snuggie
2
+ module Errors
3
+ class MissingArgument < ArgumentError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,276 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'php_serialize'
4
+
5
+ module Snuggie
6
+ # Snuggie::NOC is the main class for communicating with the
7
+ # Softaculous NOC API.
8
+ class NOC
9
+
10
+ # The Softaculous NOC API lives here
11
+ API_URL = 'http://www.softaculous.com/noc'
12
+
13
+ # Creates a new Snuggie::NOC instance
14
+ #
15
+ # Params
16
+ #
17
+ # * credentials - (Optional) a hash with :username, :password to
18
+ # be used to authenticate with Softaculous. If not
19
+ # provided, the Snuggie will try using those
20
+ # configured via `Snuggie#configure`
21
+ def initialize(credentials = {})
22
+ credentials = {
23
+ :username => Snuggie.config.username,
24
+ :password => Snuggie.config.password
25
+ } if credentials.empty? && Snuggie.config.username && Snuggie.config.password
26
+
27
+ @credentials = credentials
28
+ end
29
+
30
+ # Buy a license
31
+ #
32
+ # NOTE: You must specify either months_to_add or years_to_add
33
+ #
34
+ # Params
35
+ #
36
+ # * ip - The IP of the license to be Purchased or Renewed
37
+ # * months_to_add - Months to buy/renew the license for
38
+ # * years_to_add - Years to buy/renew the license for
39
+ # * server_type - Type of server used by this license, should be
40
+ # :dedicated or :vps
41
+ # * auth_email - When buying a new license, this is required to
42
+ # verify the owner of the License. This address will
43
+ # be used to send reminders when the license is
44
+ # expiring. Not required for renewals
45
+ # * auto_renew - Renew this license automatically before
46
+ # expiration. Set to true or false.
47
+ def buy_license(params = {})
48
+ params[:ips] = params.delete(:ip) if params[:ip]
49
+ params[:autorenew] = params.delete(:auto_renew) if params[:auto_renew]
50
+ params[:authemail] = params.delete(:auth_email) if params[:auth_email]
51
+ params[:servertype] = params.delete(:server_type) if params[:server_type]
52
+
53
+ if params[:months_to_add]
54
+ params[:toadd] = "#{params.delete(:months_to_add)}M"
55
+ elsif params[:years_to_add]
56
+ params[:toadd] = "#{params.delete(:years_to_add)}Y"
57
+ end
58
+
59
+ if [:dedicated, :vps].include?(params[:servertype])
60
+ params[:servertype] = params[:servertype] == :dedicated ? 1 : 2
61
+ end
62
+
63
+ if params.has_key?(:autorenew)
64
+ params[:autorenew] = params[:autorenew] === true ? 1 : 2
65
+ end
66
+
67
+ commit(params.merge!(:ca => :buy, :purchase => 1),
68
+ :require => [:purchase, :ips, :toadd, :servertype, :autorenew]
69
+ )
70
+ end
71
+
72
+ # Refund a transaction
73
+ #
74
+ # NOTE: A refund can only be issued within 7 days of buying/renewing
75
+ # a license.
76
+ #
77
+ # Params
78
+ #
79
+ # * action_id - The Action ID to clain a refund for
80
+ def refund(params = {})
81
+ params[:actid] = params.delete(:action_id) if params[:action_id]
82
+ params[:ca] = :refund
83
+ commit(params, :require => [:actid])
84
+ end
85
+
86
+ # List licenses
87
+ #
88
+ # NOTE: All parameters are optional. If called without parameters, a
89
+ # list of all of your licenses will be returned.
90
+ #
91
+ # Params
92
+ #
93
+ # * key - (Optional) Search for a specific License by License Key
94
+ # * ip - (Optional) Search for a specific License by Primary IP
95
+ # * expiry - (Optional) Fetch a list of Licenses that are expiring
96
+ # Set to 1 to list all expired licenses on your account
97
+ # Set to 2 to list licenses expiring in the next 7 days
98
+ # Set to 3 to list licenses expiring in the next 15 days
99
+ # * start - (Optional) The starting point to return from
100
+ # Eg: specify 99 if you have 500 licenses and want to
101
+ # return licenses 100-500.
102
+ # * limit - (Optional) The length to limit the result set to
103
+ # Eg: specify 100 if you have 500 licenses and want to
104
+ # limit the result set to 100 items
105
+ def list_licenses(params = {})
106
+ params[:len] = params.delete(:limit) if params[:limit]
107
+ params[:ca] = :licenses
108
+ commit(params)
109
+ end
110
+
111
+ # Cancel license
112
+ #
113
+ # NOTES:
114
+ # * Either ip or key needs to be specified
115
+ # * Cancellations are not allowed on licenses expiring more than 1
116
+ # month in the future.
117
+ # * A refund is **NOT** applied when you cancel a license. You must
118
+ # claim the refund using Snuggie::NOC#refund
119
+ #
120
+ # Params
121
+ #
122
+ # * key - (Optional) The license key
123
+ # * ip - (Optional) The Primary IP of the license
124
+ def cancel_license(params = {})
125
+ params[:lickey] = params.delete(:key) if params[:key]
126
+ params[:licip] = params.delete(:ip) if params[:ip]
127
+ commit(params.merge!(:ca => :cancel, :cancel_license => 1),
128
+ :require_one => [:lickey, :licip]
129
+ )
130
+ end
131
+
132
+ # Edit IPs associated with a license
133
+ #
134
+ # Params
135
+ #
136
+ # * license_id - The license ID (**NOT** the license key)
137
+ # * ips - The list of IPs of the same VPS/Server. The first IP is
138
+ # the Primary IP. You may add up to 8 IPs
139
+ def edit_ips(params = {})
140
+ params[:'ips[]'] = params.delete(:ips)
141
+ params[:lid] = params.delete(:license_id) if params[:license_id]
142
+ commit(params.merge!(:ca => :showlicense, :editlicense => 1),
143
+ :require => [:lid, :'ips[]']
144
+ )
145
+ end
146
+
147
+ # Get details for an invoice
148
+ #
149
+ # NOTE: If invoid is 0 or not set, a list of **all** unbilled
150
+ # transactions for the current month will be returned
151
+ #
152
+ # Params
153
+ #
154
+ # * invoice_id - The invoice ID to getch details for.
155
+ def invoice_details(params = {})
156
+ params[:invoid] = params.delete(:invoice_id) if params[:invoice_id]
157
+ params[:ca] = :invoicedetails
158
+ commit(params)
159
+ end
160
+
161
+ # Get Action Logs for a license
162
+ #
163
+ # NOTE: The logs are returned in **DESCENDING ORDER**, meaning the
164
+ # latest logs will appear first.
165
+ #
166
+ # Params
167
+ #
168
+ # * key - The license key
169
+ # * limit - The number of action logs to return
170
+ def license_logs(params = {})
171
+ params[:ca] = :licenselogs
172
+ commit(params, :require => :key)
173
+ end
174
+
175
+ private
176
+
177
+ # Send a request upstream to Softaculous
178
+ #
179
+ # Params
180
+ #
181
+ # * params - a hash of parameters for the request
182
+ # * options - a hash of options, used to validate required params
183
+ def commit(params, options = {})
184
+ if options[:require]
185
+ unless require_params(params, options[:require])
186
+ raise Errors::MissingArgument
187
+ end
188
+ end
189
+
190
+ if options[:require_one]
191
+ unless require_one_of(params, options[:require_one])
192
+ raise Errors::MissingArgument
193
+ end
194
+ end
195
+
196
+ params.merge!(@credentials) unless @credentials.nil? || @credentials.empty?
197
+
198
+ uri = "#{API_URL}?#{query_string(params)}"
199
+ if res = fetch(uri)
200
+ @response = begin
201
+ PHP.unserialize(res.body)
202
+ rescue
203
+ res.body
204
+ end
205
+ end
206
+ end
207
+
208
+ # Returns true if params has all of the specified keys
209
+ #
210
+ # Params
211
+ #
212
+ # * params - hash of query parameters
213
+ # * keys - keys to search for in params
214
+ def require_params(params, keys)
215
+ Array(keys).each { |key| return false unless params[key] }
216
+ true
217
+ end
218
+
219
+ # Returns true if params has one of the specified keys
220
+ #
221
+ # Params
222
+ #
223
+ # * params - hash of query parameters
224
+ # * keys - keys to search for in params
225
+ def require_one_of(params, keys)
226
+ Array(keys).each { |key| return true if params[key] }
227
+ false
228
+ end
229
+
230
+ # Formats params into a query string for a GET request
231
+ #
232
+ # NOTE: For convenience, this converts the keys :username and
233
+ # :password to :nocname and :nocpass respectively.
234
+ #
235
+ # Params
236
+ #
237
+ # * params - hash of query parameters
238
+ def query_string(params)
239
+ params.map do |key, val|
240
+ case key.to_sym
241
+ when :username
242
+ key = :nocname
243
+ when :password
244
+ key = :nocpass
245
+ end
246
+ "#{key}=#{CGI.escape(val.to_s)}"
247
+ end.join('&')
248
+ end
249
+
250
+ # Performs a GET request on the given URI, redirects if needed
251
+ #
252
+ # See Following Redirection at
253
+ # http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTP.html
254
+ #
255
+ # Params
256
+ #
257
+ # * uri_str - the URL to fetch as a string
258
+ # * limit - number of redirects to allow
259
+ def fetch(uri_str, limit = 10)
260
+ # You should choose better exception.
261
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
262
+
263
+ uri = URI.parse(uri_str)
264
+ http = Net::HTTP.new(uri.host, uri.port)
265
+ request = Net::HTTP::Get.new(uri.request_uri)
266
+ response = http.start { |http| http.request(request) }
267
+
268
+ case response
269
+ when Net::HTTPSuccess then response
270
+ when Net::HTTPRedirection then fetch(response['location'], limit - 1)
271
+ else
272
+ response.error!
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,3 @@
1
+ module Snuggie
2
+ VERSION = Version = '0.1.0'
3
+ end
@@ -0,0 +1 @@
1
+ a:12:{s:3:"lid";i:99999;s:7:"license";s:29:"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";s:3:"bal";d:-48;s:2:"ip";s:9:"127.0.0.1";s:4:"time";i:1308062889;s:4:"rate";i:2;s:3:"amt";i:2;s:5:"added";s:2:"1M";s:6:"action";s:3:"new";s:9:"autorenew";s:3:"YES";s:5:"actid";i:99999;s:5:"error";N;}
@@ -0,0 +1 @@
1
+ a:2:{s:5:"error";N;s:17:"cancelled_license";a:15:{s:6:"apiuid";s:1:"0";s:5:"nocid";s:4:"XXXX";s:10:"servertype";s:1:"1";s:7:"license";s:29:"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";s:4:"time";s:10:"1308062889";s:7:"expires";s:8:"20110614";s:3:"env";s:0:"";s:4:"type";s:1:"1";s:9:"last_sync";s:1:"0";s:3:"lid";s:5:"99999";s:10:"soft_email";s:0:"";s:5:"panel";s:0:"";s:9:"authemail";s:18:"marty@hilldale.edu";s:6:"unique";s:0:"";s:6:"active";s:1:"1";}}
@@ -0,0 +1 @@
1
+ a:3:{s:5:"error";N;s:7:"new_ips";a:1:{i:0;s:9:"127.0.0.2";}s:3:"lid";i:99999;}
@@ -0,0 +1 @@
1
+ a:3:{s:7:"actions";a:2:{i:XXXXX;a:12:{s:5:"actid";s:5:"XXXXX";s:5:"nocid";s:4:"XXXX";s:6:"action";s:7:"advance";s:3:"lid";s:1:"0";s:6:"invoid";s:1:"0";s:4:"rate";s:5:"50.00";s:5:"added";s:0:"";s:3:"amt";s:6:"-50.00";s:4:"time";s:10:"1307554880";s:4:"date";s:8:"20110608";s:8:"refunded";s:1:"0";s:7:"license";N;}i:XXXXX;a:12:{s:5:"actid";s:5:"XXXXX";s:5:"nocid";s:4:"XXXX";s:6:"action";s:3:"new";s:3:"lid";s:5:"XXXXX";s:6:"invoid";s:1:"0";s:4:"rate";s:4:"2.00";s:5:"added";s:2:"1M";s:3:"amt";s:4:"2.00";s:4:"time";s:10:"1308062889";s:4:"date";s:8:"20110614";s:8:"refunded";s:1:"0";s:7:"license";s:29:"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";}}s:3:"amt";d:-48;s:6:"invoid";i:0;}
@@ -0,0 +1 @@
1
+ a:3:{s:7:"actions";a:1:{i:XXXXX;a:11:{s:5:"actid";s:5:"XXXXX";s:5:"nocid";s:4:"XXXX";s:6:"action";s:3:"new";s:3:"lid";s:5:"XXXXX";s:6:"invoid";s:1:"0";s:4:"rate";s:4:"2.00";s:5:"added";s:2:"1M";s:3:"amt";s:4:"2.00";s:4:"time";s:10:"1308062889";s:4:"date";s:8:"20110614";s:8:"refunded";s:1:"0";}}s:7:"license";a:15:{s:3:"lid";s:5:"XXXXX";s:7:"license";s:29:"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";s:4:"type";s:1:"1";s:6:"unique";s:0:"";s:6:"apiuid";s:1:"0";s:5:"nocid";s:4:"XXXX";s:9:"authemail";s:18:"marty@hilldale.edu";s:10:"soft_email";s:0:"";s:4:"time";s:10:"1308062889";s:7:"expires";s:8:"20110714";s:6:"active";s:1:"1";s:10:"servertype";s:1:"1";s:9:"last_sync";s:1:"0";s:5:"panel";s:0:"";s:3:"env";s:0:"";}s:5:"error";N;}
@@ -0,0 +1 @@
1
+ a:4:{s:8:"licenses";a:1:{i:99999;a:19:{s:3:"lid";s:5:"XXXXX";s:7:"license";s:29:"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";s:4:"type";s:1:"1";s:6:"unique";s:0:"";s:6:"apiuid";s:1:"0";s:5:"nocid";s:4:"XXXX";s:9:"authemail";s:18:"marty@hilldale.org";s:10:"soft_email";s:0:"";s:4:"time";s:10:"1308062889";s:7:"expires";s:8:"20110614";s:6:"active";s:1:"1";s:10:"servertype";s:1:"1";s:9:"last_sync";s:1:"0";s:5:"panel";s:0:"";s:3:"env";s:0:"";s:4:"ipid";s:5:"XXXXX";s:2:"ip";s:9:"127.0.0.1";s:7:"primary";s:1:"1";s:8:"hostname";s:12:"hilldale.org";}}s:10:"num_active";i:1;s:11:"num_results";i:1;s:5:"error";N;}
@@ -0,0 +1 @@
1
+ a:10:{s:3:"lid";s:5:"XXXXX";s:7:"license";s:29:"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";s:3:"bal";d:-50;s:4:"time";i:1308066592;s:4:"rate";s:4:"2.00";s:3:"amt";d:-2;s:5:"added";s:3:"-1M";s:6:"action";s:6:"refund";s:5:"actid";i:99999;s:5:"error";N;}
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+
3
+ context "Snuggie::Config" do
4
+ setup do
5
+ @config = Snuggie::Config.new
6
+ end
7
+
8
+ test "has attr_accessor :username" do
9
+ assert_attr_accessor @config, :username
10
+ end
11
+
12
+ test "has attr_accessor :password" do
13
+ assert_attr_accessor @config, :password
14
+ end
15
+ end
@@ -0,0 +1,279 @@
1
+ require 'test_helper'
2
+
3
+ context "Snuggie::NOC" do
4
+ TEST_CREDENTIALS = { :username => 'marty', :password => 'mcSUPERfly' }
5
+
6
+ setup do
7
+ @noc = Snuggie::NOC.new(TEST_CREDENTIALS)
8
+ end
9
+
10
+ def mock_query_url(params = {})
11
+ @noc.class::API_URL + '?' + @noc.instance_eval { query_string(params) }
12
+ end
13
+
14
+ test "::API_URL has a valid format" do
15
+ assert_valid_url @noc.class::API_URL
16
+ end
17
+
18
+ test "#initialize sets @credentials" do
19
+ credentials = @noc.instance_variable_get(:@credentials)
20
+ assert credentials.is_a?(Hash)
21
+ TEST_CREDENTIALS.each do |key, val|
22
+ assert credentials.has_key?(key)
23
+ assert_equal val, credentials[key]
24
+ end
25
+ end
26
+
27
+ test "#initialize uses Config to set @credentials" do
28
+ Snuggie.configure do |c|
29
+ c.username = 'doc'
30
+ c.password = 'clara'
31
+ end
32
+ credentials = @noc.class.new.instance_variable_get(:@credentials)
33
+ assert credentials.is_a?(Hash)
34
+ ({ :username => 'doc', :password => 'clara' }).each do |key, val|
35
+ assert_not_nil credentials[key]
36
+ assert credentials.has_key?(key)
37
+ end
38
+
39
+ Snuggie.configure do |c|
40
+ c.username = nil
41
+ c.password = nil
42
+ end
43
+ assert_equal @noc.class.new.instance_variable_get(:@credentials), Hash.new
44
+ end
45
+
46
+ test "#require_params returns true if all params are set" do
47
+ a1 = @noc.instance_eval do
48
+ require_params({ :fuel => :plutonium, :date => 1955 }, [:fuel, :date])
49
+ end
50
+ assert a1
51
+ end
52
+
53
+ test "#require_one_of returns true if one param is set" do
54
+ a1 = @noc.instance_eval do
55
+ require_one_of({ :date => 1955, :fuel => :plutonium }, [:fusion, :fuel])
56
+ end
57
+ assert a1
58
+ end
59
+
60
+ test "#query_string" do
61
+ params = {
62
+ :date => 1955,
63
+ :fuel => :plutonium,
64
+ :username => 'marty',
65
+ :password => 'mcSUPERfly'
66
+ }
67
+
68
+ query = @noc.instance_eval do
69
+ query_string(params)
70
+ end
71
+
72
+ assert_match /date=1955/, query
73
+ assert_match /fuel=plutonium/, query
74
+ assert_match /nocname=marty/, query
75
+ assert_match /nocpass=mcSUPERfly/, query
76
+
77
+ assert_no_match /username/, query
78
+ assert_no_match /password/, query
79
+ end
80
+
81
+ test "#commit requires all :require params" do
82
+ mock_request(mock_query_url)
83
+ assert_raise(Snuggie::Errors::MissingArgument) do
84
+ @noc.instance_eval do
85
+ commit({}, :require => :fuel)
86
+ end
87
+ end
88
+
89
+ params = { :fuel => :plutonium }
90
+ mock_request(mock_query_url(params))
91
+ assert_nothing_raised do
92
+ @noc.instance_eval do
93
+ commit(params, :require => :fuel)
94
+ end
95
+ end
96
+ end
97
+
98
+ test "#commit requires one of :require_one params" do
99
+ p1 = { :date => 1955 }
100
+ mock_request(mock_query_url(p1))
101
+ assert_raise(Snuggie::Errors::MissingArgument) do
102
+ @noc.instance_eval do
103
+ commit(p1, :require_one => :fuel)
104
+ end
105
+ end
106
+
107
+ p2 = { :date => 1955, :fuel => :plutonium }
108
+ mock_request(mock_query_url(p2))
109
+ assert_nothing_raised do
110
+ @noc.instance_eval do
111
+ commit(p2, :require_one => :fuel)
112
+ end
113
+ end
114
+ end
115
+
116
+ test "#commit returns a hash if PHP.unserialize works" do
117
+ p1 = { :date => 1955 }
118
+ mock_request(mock_query_url(p1), :body => PHP.serialize(:status => :success))
119
+ res = @noc.instance_eval do
120
+ commit(p1, :require => :date)
121
+ end
122
+ assert res.has_key?('status')
123
+ assert_equal 'success', res['status']
124
+ end
125
+
126
+ test "#commit returns HTTP body if PHP.unserialize fails" do
127
+ p1 = { :date => 1955 }
128
+ mock_request(mock_query_url(p1), :body => "not a PHP serialized string")
129
+ res = @noc.instance_eval do
130
+ commit(p1, :require => :date)
131
+ end
132
+ assert_equal 'not a PHP serialized string', res
133
+ end
134
+
135
+ test "#buy_license required params" do
136
+ params = {
137
+ :ip => '127.0.0.1',
138
+ :months_to_add => 1,
139
+ :servertype => :dedicated,
140
+ :authemail => 'marty@hilldale.edu',
141
+ :autorenew => true
142
+ }
143
+ mock_request(:buy_license)
144
+ assert_raise(Snuggie::Errors::MissingArgument, "requires args") do
145
+ @noc.buy_license
146
+ end
147
+
148
+ res = @noc.buy_license(params)
149
+ assert res.is_a?(Hash)
150
+ assert_equal '1M', res['added']
151
+ assert_equal 'YES', res['autorenew']
152
+ assert_equal 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', res['license']
153
+ assert_equal 1308062889, res['time']
154
+ assert_equal -48.0, res['bal']
155
+ assert_equal 2, res['rate']
156
+ assert_equal 99999, res['actid']
157
+ assert_equal '127.0.0.1', res['ip']
158
+ assert_equal 99999, res['lid']
159
+ assert_equal 2, res['amt']
160
+ end
161
+
162
+ test "#refund" do
163
+ mock_request(:refund)
164
+ assert_raise(Snuggie::Errors::MissingArgument, "requires actid") do
165
+ @noc.refund
166
+ end
167
+
168
+ res = @noc.refund :actid => 99999
169
+ assert res.is_a?(Hash)
170
+ assert_equal '-1M', res['added']
171
+ assert_equal 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', res['license']
172
+ assert_equal 1308066592, res['time']
173
+ assert_equal -50.0, res['bal']
174
+ assert_equal 'refund', res['action']
175
+ assert_equal '2.00', res['rate']
176
+ assert_equal 99999, res['actid']
177
+ assert_equal 'XXXXX', res['lid']
178
+ assert_equal -2.0, res['amt']
179
+ end
180
+
181
+ test "#list_licenses" do
182
+ mock_request(:list_licenses)
183
+ res = @noc.list_licenses
184
+ assert res.is_a?(Hash)
185
+ assert_equal 1, res['num_results']
186
+ assert_equal 1, res['num_active']
187
+ assert_not_nil res['licenses']
188
+ end
189
+
190
+ test "#cancel_license" do
191
+ mock_request(:cancel_license)
192
+ assert_raise(Snuggie::Errors::MissingArgument, "requires lickey or licip") do
193
+ @noc.cancel_license
194
+ end
195
+ # TODO: fixture/test
196
+ res = @noc.cancel_license :key => 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'
197
+ assert res.is_a?(Hash)
198
+ assert res['cancelled_license'].is_a?(Hash)
199
+ res = res['cancelled_license']
200
+ assert_equal '0', res['apiuid']
201
+ assert_equal 'XXXX', res['nocid']
202
+ assert_equal '1', res['servertype']
203
+ assert_equal 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', res['license']
204
+ assert_equal '1308062889', res['time']
205
+ assert_equal '20110614', res['expires']
206
+ assert_equal '1', res['type']
207
+ assert_equal '0', res['last_sync']
208
+ assert_equal 'marty@hilldale.edu', res['authemail']
209
+ end
210
+
211
+ test "#invoice_details unbilled" do
212
+ mock_request(:invoice_details_unbilled)
213
+ res = @noc.invoice_details
214
+ assert res.is_a?(Hash)
215
+ assert res['actions'].is_a?(Hash)
216
+ act = res['actions'][0]
217
+ assert_equal '1M', act['added']
218
+ assert_equal 'XXXX', act['nocid']
219
+ assert_equal 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', act['license']
220
+ assert_equal '0', act['refunded']
221
+ assert_equal '1308062889', act['time']
222
+ assert_equal '20110614', act['date']
223
+ assert_equal '2.00', act['rate']
224
+ assert_equal 'new', act['action']
225
+ assert_equal 'XXXXX', act['actid']
226
+ assert_equal '0', act['invoid']
227
+ assert_equal 'XXXXX', act['lid']
228
+ assert_equal '2.00', act['amt']
229
+ end
230
+
231
+ test "#license_logs" do
232
+ mock_request(:license_logs)
233
+ assert_raise(Snuggie::Errors::MissingArgument, "requires key") do
234
+ @noc.license_logs
235
+ end
236
+ res = @noc.license_logs :key => 'XXXXX-XXXXX-XXXXX-XXXXX'
237
+
238
+ assert res.is_a?(Hash)
239
+ assert res['actions'].is_a?(Array)
240
+ act = res['actions'].first
241
+ assert act.is_a?(Hash)
242
+ assert_equal '1M', act['added']
243
+ assert_equal 'XXXX', act['nocid']
244
+ assert_equal '0', act['refunded']
245
+ assert_equal '1308062889', act['time']
246
+ assert_equal '20110614', act['date']
247
+ assert_equal '2.00', act['rate']
248
+ assert_equal 'new', act['action']
249
+ assert_equal '0', act['invoid']
250
+ assert_equal 'XXXXX', act['lid']
251
+ assert_equal '2.00', act['amt']
252
+ lic = res['license']
253
+ assert lic.is_a?(Hash)
254
+ assert_equal '1', lic['servertype']
255
+ assert_equal 'XXXX', lic['nocid']
256
+ assert_equal '0', lic['apiuid']
257
+ assert_equal 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', lic['license']
258
+ assert_equal '20110714', lic['expires']
259
+ assert_equal '1308062889', lic['time']
260
+ assert_equal '0', lic['last_sync']
261
+ assert_equal '1', lic['type']
262
+ assert_equal 'XXXXX', lic['lid']
263
+ assert_equal 'marty@hilldale.edu', lic['authemail']
264
+ assert_equal '1', lic['active']
265
+ end
266
+
267
+ test "#edit_ips" do
268
+ mock_request(:edit_ips)
269
+ assert_raise(Snuggie::Errors::MissingArgument, "requires ips and lid") do
270
+ @noc.edit_ips
271
+ end
272
+ res = @noc.edit_ips :ips => '127.0.0.2', :lid => 'XXXXX'
273
+
274
+ assert res.is_a?(Hash)
275
+ assert_equal 99999, res['lid']
276
+ assert res['new_ips'].is_a?(Array)
277
+ assert_equal '127.0.0.2', res['new_ips'].first
278
+ end
279
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ context "Snuggie" do
4
+ setup do
5
+ Snuggie.config = Snuggie::Config.new
6
+ end
7
+
8
+ test "::Version has a valid format" do
9
+ assert Snuggie::Version.match(/\d+\.\d+\.\d+/)
10
+ end
11
+
12
+ test "has class attr_accessor :config" do
13
+ assert_attr_accessor Snuggie, :config
14
+ end
15
+
16
+ test "::config defaults to Config.new" do
17
+ assert Snuggie.config.is_a?(Snuggie::Config)
18
+ end
19
+
20
+ test "::configure sets config with a block" do
21
+ Snuggie.configure do |c|
22
+ c.username = 'mcfly'
23
+ c.password = 'b4ck1nt1m3'
24
+ end
25
+
26
+ assert Snuggie.configure.is_a?(Snuggie::Config)
27
+ assert_equal Snuggie.config.username, 'mcfly'
28
+ assert_equal Snuggie.config.password, 'b4ck1nt1m3'
29
+ end
30
+
31
+ end
@@ -0,0 +1,138 @@
1
+ require 'rubygems'
2
+ require 'snuggie'
3
+ require 'test/unit'
4
+ require 'fakeweb'
5
+ require 'php_serialize'
6
+
7
+ begin
8
+ require 'turn'
9
+ rescue LoadError
10
+ puts "'gem install turn' for prettier test output"
11
+ end
12
+
13
+ FakeWeb.allow_net_connect = false
14
+
15
+ # test/spec/mini 3
16
+ # Based on http://gist.github.com/25455
17
+ def context(*args, &block)
18
+ return super unless (name = args.first) && block
19
+ klass = Class.new(Test::Unit::TestCase) do
20
+ def self.test(name, &block)
21
+ name = " #{name}" unless %w[: # .].include?(name[0].chr)
22
+ define_method("test: #{self.name}#{name}", &block) if block
23
+ end
24
+ def self.setup(&block) define_method(:setup, &block) end
25
+ def self.teardown(&block) define_method(:teardown, &block) end
26
+ end
27
+ (class << klass; self end).send(:define_method, :name) { name }
28
+ (class << klass; self end).send(:define_method, :to_s) { name }
29
+ klass.class_eval &block
30
+ end
31
+
32
+ def mock_request(method, options = {})
33
+ fixture = File.expand_path("../fixtures/#{method}.txt", __FILE__)
34
+ if File.exists?(fixture)
35
+ options[:body] = File.read(fixture)
36
+ end
37
+ FakeWeb.register_uri(:get, %r|^#{Snuggie::NOC::API_URL}\?.*|, options)
38
+ end
39
+
40
+ # Asserts that the given object has an instance variable for var
41
+ #
42
+ # class Foo
43
+ # def initialize
44
+ # @bar = :bar
45
+ # end
46
+ # end
47
+ #
48
+ # assert_instance_var(Foo.new, :bar) => passes
49
+ def assert_instance_var(obj, var)
50
+ assert_not_nil obj.instance_variable_get("@#{var}")
51
+ end
52
+
53
+ # Asserts that the given object has an attr_accessor for method
54
+ #
55
+ # class Foo
56
+ # attr_accessor :bar
57
+ # end
58
+ #
59
+ # assert_attr_accesor(Foo.new, :bar) => passes
60
+ def assert_attr_accessor(obj, method)
61
+ assert_attr_reader obj, method
62
+ assert_attr_writer obj, method
63
+ end
64
+
65
+ # Asserts that the given object has an attr_reader for method
66
+ #
67
+ # class Foo
68
+ # attr_writer :bar
69
+ # end
70
+ #
71
+ # assert_attr_reader(Foo.new, :bar) => passes
72
+ def assert_attr_reader(obj, method)
73
+ assert_respond_to obj, method
74
+ assert_equal obj.send(method), obj.instance_variable_get("@#{method}")
75
+ end
76
+
77
+ # Asserts that the given object has an attr_writer for method
78
+ #
79
+ # class Foo
80
+ # attr_writer :bar
81
+ # end
82
+ #
83
+ # assert_attr_writer(Foo.new, :bar) => passes
84
+ def assert_attr_writer(obj, method)
85
+ assert_respond_to obj, "#{method}="
86
+ end
87
+
88
+ # Asserts that the given string is a valid URL
89
+ #
90
+ # assert_valid_url('http://google.com') => passes
91
+ def assert_valid_url(string)
92
+ assert string.to_s.match(/https?:\/\/[\S]+/)
93
+ end
94
+
95
+ # From https://github.com/thoughtbot/shoulda-context/blob/master/lib/shoulda/context/assertions.rb
96
+ # Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
97
+ #
98
+ # assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
99
+ def assert_same_elements(a1, a2, msg = nil)
100
+ [:select, :inject, :size].each do |m|
101
+ [a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
102
+ end
103
+
104
+ assert a1h = a1.inject({}) { |h,e| h[e] ||= a1.select { |i| i == e }.size; h }
105
+ assert a2h = a2.inject({}) { |h,e| h[e] ||= a2.select { |i| i == e }.size; h }
106
+
107
+ assert_equal(a1h, a2h, msg)
108
+ end
109
+
110
+ # Asserts that the given collection contains item x. If x is a regular expression, ensure that
111
+ # at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
112
+ #
113
+ # assert_contains(['a', '1'], /\d/) => passes
114
+ # assert_contains(['a', '1'], 'a') => passes
115
+ # assert_contains(['a', '1'], /not there/) => fails
116
+ def assert_contains(collection, x, extra_msg = "")
117
+ collection = Array(collection)
118
+ msg = "#{x.inspect} not found in #{collection.to_a.inspect} #{extra_msg}"
119
+ case x
120
+ when Regexp
121
+ assert(collection.detect { |e| e =~ x }, msg)
122
+ else
123
+ assert(collection.include?(x), msg)
124
+ end
125
+ end
126
+
127
+ # Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
128
+ # none of the elements from the collection match x.
129
+ def assert_does_not_contain(collection, x, extra_msg = "")
130
+ collection = Array(collection)
131
+ msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
132
+ case x
133
+ when Regexp
134
+ assert(!collection.detect { |e| e =~ x }, msg)
135
+ else
136
+ assert(!collection.include?(x), msg)
137
+ end
138
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snuggie
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Joshua Priddle
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-15 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: php-serialize
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 1.1.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 0.8.7
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: fakeweb
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 1.3.0
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: " Snuggie wraps the Softaculous API in a warm, loving ruby embrace.\n"
50
+ email: jpriddle@site5.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - README.markdown
57
+ files:
58
+ - Rakefile
59
+ - README.markdown
60
+ - lib/snuggie/config.rb
61
+ - lib/snuggie/errors.rb
62
+ - lib/snuggie/noc.rb
63
+ - lib/snuggie/version.rb
64
+ - lib/snuggie.rb
65
+ - test/fixtures/buy_license.txt
66
+ - test/fixtures/cancel_license.txt
67
+ - test/fixtures/edit_ips.txt
68
+ - test/fixtures/invoice_details_unbilled.txt
69
+ - test/fixtures/license_logs.txt
70
+ - test/fixtures/list_licenses.txt
71
+ - test/fixtures/refund.txt
72
+ - test/snuggie/config_test.rb
73
+ - test/snuggie/noc_test.rb
74
+ - test/snuggie_test.rb
75
+ - test/test_helper.rb
76
+ has_rdoc: true
77
+ homepage: https://github.com/site5/snuggie
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --charset=UTF-8
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.6.2
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Snuggie wraps the Softaculous API in a warm, loving ruby embrace
104
+ test_files: []
105
+