snuggie 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+