pasaporte 0.0.1

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.
Files changed (41) hide show
  1. data/History.txt +2 -0
  2. data/Manifest.txt +41 -0
  3. data/README.txt +72 -0
  4. data/Rakefile +111 -0
  5. data/TODO.txt +2 -0
  6. data/bin/pasaporte-fcgi.rb +17 -0
  7. data/lib/pasaporte/.DS_Store +0 -0
  8. data/lib/pasaporte/assets/.DS_Store +0 -0
  9. data/lib/pasaporte/assets/bgbar.png +0 -0
  10. data/lib/pasaporte/assets/lock.png +0 -0
  11. data/lib/pasaporte/assets/mainbg_green.gif +0 -0
  12. data/lib/pasaporte/assets/mainbg_red.gif +0 -0
  13. data/lib/pasaporte/assets/openid.png +0 -0
  14. data/lib/pasaporte/assets/pasaporte.css +192 -0
  15. data/lib/pasaporte/assets/pasaporte.js +10 -0
  16. data/lib/pasaporte/assets/user.png +0 -0
  17. data/lib/pasaporte/auth/cascade.rb +16 -0
  18. data/lib/pasaporte/auth/remote_web_workplace.rb +61 -0
  19. data/lib/pasaporte/auth/yaml_digest_table.rb +23 -0
  20. data/lib/pasaporte/auth/yaml_table.rb +43 -0
  21. data/lib/pasaporte/faster_openid.rb +39 -0
  22. data/lib/pasaporte/iso_countries.yml +247 -0
  23. data/lib/pasaporte/julik_state.rb +42 -0
  24. data/lib/pasaporte/markaby_ext.rb +8 -0
  25. data/lib/pasaporte/pasaporte_store.rb +60 -0
  26. data/lib/pasaporte/timezones.yml +797 -0
  27. data/lib/pasaporte.rb +1214 -0
  28. data/test/fixtures/pasaporte_approvals.yml +12 -0
  29. data/test/fixtures/pasaporte_profiles.yml +45 -0
  30. data/test/fixtures/pasaporte_throttles.yml +4 -0
  31. data/test/helper.rb +66 -0
  32. data/test/mosquito.rb +596 -0
  33. data/test/test_approval.rb +33 -0
  34. data/test/test_auth_backends.rb +59 -0
  35. data/test/test_openid.rb +363 -0
  36. data/test/test_pasaporte.rb +326 -0
  37. data/test/test_profile.rb +165 -0
  38. data/test/test_settings.rb +27 -0
  39. data/test/test_throttle.rb +70 -0
  40. data/test/testable_openid_fetcher.rb +82 -0
  41. metadata +151 -0
data/test/mosquito.rb ADDED
@@ -0,0 +1,596 @@
1
+ %w(
2
+ rubygems
3
+ test/unit
4
+ active_record
5
+ active_record/fixtures
6
+ camping
7
+ camping/session
8
+ fileutils
9
+ tempfile
10
+ stringio
11
+ ).each { |lib| require lib }
12
+
13
+ module Mosquito
14
+ VERSION = '0.1.3'
15
+
16
+ # For various methods that need to generate random text
17
+ def self.garbage(amount) #:nodoc:
18
+ fills = ("a".."z").to_a
19
+ str = (0...amount).map do
20
+ v = fills[rand(fills.length)]
21
+ (rand(2).zero? ? v.upcase : v)
22
+ end
23
+ str.join
24
+ end
25
+
26
+ # Will be raised if you try to test for something Camping does not support.
27
+ # Kind of a safeguard in the deep ocean of metaified Ruby goodness.
28
+ class SageAdvice < RuntimeError; end
29
+
30
+ # Will be raised if you try to call an absolute, canonical URL (with scheme and server).
31
+ # and the server does not match the specified request.
32
+ class NonLocalRequest < RuntimeError; end
33
+
34
+ def self.stash(something) #:nodoc:
35
+ @stashed = something
36
+ end
37
+
38
+ def self.unstash #:nodoc:
39
+ x, @stashed = @stashed, nil; x
40
+ end
41
+
42
+ module Dusty
43
+ ##
44
+ # From Jay Fields.
45
+ #
46
+ # Allows tests to be specified as a block.
47
+ #
48
+ # test "should do this and that" do
49
+ # ...
50
+ # end
51
+
52
+ def test(name, &block)
53
+ test_name = :"test_#{name.gsub(' ','_')}"
54
+ raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include? test_name.to_s
55
+ define_method test_name, &block
56
+ end
57
+ end
58
+ end
59
+
60
+ in_mem_sqlite = {:adapter => 'sqlite3', :database => ":memory:"}
61
+ ActiveRecord::Base.establish_connection(in_mem_sqlite)
62
+ ActiveRecord::Base.configurations['test'] = in_mem_sqlite.stringify_keys
63
+
64
+ ActiveRecord::Base.logger = Logger.new("test/test.log") rescue Logger.new("test.log")
65
+
66
+ # This needs to be set relative to the file where the test comes from, NOT relative to the
67
+ # mosquito itself
68
+ Test::Unit::TestCase.fixture_path = "test/fixtures/"
69
+
70
+ # Mock request is used for composing the request body and headers
71
+ class Mosquito::MockRequest
72
+ # Should be a StringIO. However, you got some assignment methods that will
73
+ # stuff it with encoded parameters for you
74
+ attr_accessor :body
75
+ attr_reader :headers
76
+
77
+ DEFAULT_HEADERS = {
78
+ 'SERVER_NAME' => 'test.host',
79
+ 'PATH_INFO' => '',
80
+ 'HTTP_ACCEPT_ENCODING' => 'gzip,deflate',
81
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.1) Gecko/20060214 Camino/1.0',
82
+ 'SCRIPT_NAME' => '/',
83
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
84
+ 'HTTP_CACHE_CONTROL' => 'max-age=0',
85
+ 'HTTP_ACCEPT_LANGUAGE' => 'en,ja;q=0.9,fr;q=0.9,de;q=0.8,es;q=0.7,it;q=0.7,nl;q=0.6,sv;q=0.5,nb;q=0.5,da;q=0.4,fi;q=0.3,pt;q=0.3,zh-Hans;q=0.2,zh-Hant;q=0.1,ko;q=0.1',
86
+ 'HTTP_HOST' => 'test.host',
87
+ 'REMOTE_ADDR' => '127.0.0.1',
88
+ 'SERVER_SOFTWARE' => 'Mongrel 0.3.12.4',
89
+ 'HTTP_KEEP_ALIVE' => '300',
90
+ 'HTTP_REFERER' => 'http://localhost/',
91
+ 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
92
+ 'HTTP_VERSION' => 'HTTP/1.1',
93
+ 'REQUEST_URI' => '/',
94
+ 'SERVER_PORT' => '80',
95
+ 'GATEWAY_INTERFACE' => 'CGI/1.2',
96
+ 'HTTP_ACCEPT' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
97
+ 'HTTP_CONNECTION' => 'keep-alive',
98
+ 'REQUEST_METHOD' => 'GET',
99
+ }
100
+
101
+
102
+ def initialize
103
+ @headers = DEFAULT_HEADERS.with_indifferent_access # :-)
104
+ @body = StringIO.new('hello Camping')
105
+ end
106
+
107
+ # Returns the hash of headers
108
+ def to_hash
109
+ @headers
110
+ end
111
+
112
+ # Gets a header
113
+ def [](key)
114
+ @headers[key]
115
+ end
116
+
117
+ # Sets a header
118
+ def []=(key, value)
119
+ @headers[key] = value
120
+ end
121
+ alias_method :set, :[]=
122
+
123
+ # Retrieve a composed query string (including the eventual "?") with URL-escaped segments
124
+ def query_string
125
+ (@query_string_with_qmark.blank? ? '' : @query_string_with_qmark)
126
+ end
127
+
128
+ # Set a composed query string, should have URL-escaped segments and include the elements after the "?"
129
+ def query_string=(nqs)
130
+ @query_string_with_qmark = nqs.gsub(/^([^\?])/, '?\1')
131
+ @headers["REQUEST_URI"] = @headers["REQUEST_URI"].split(/\?/).shift + @query_string_with_qmark
132
+ if nqs.blank?
133
+ @headers.delete "QUERY_STRING"
134
+ else
135
+ @headers["QUERY_STRING"] = nqs.gsub(/^\?/, '')
136
+ end
137
+ end
138
+
139
+ # Retrieve the domain (analogous to HTTP_HOST)
140
+ def domain
141
+ server_name || http_host
142
+ end
143
+
144
+ # Set the domain (changes both HTTP_HOST and SERVER_NAME)
145
+ def domain=(nd)
146
+ self['SERVER_NAME'] = self['HTTP_HOST'] = nd
147
+ end
148
+
149
+ # Allow getters like this:
150
+ # o.REQUEST_METHOD or o.request_method
151
+ def method_missing(method_name, *args)
152
+ triables = [method_name.to_s, method_name.to_s.upcase, "HTTP_" + method_name.to_s.upcase]
153
+ triables.map do | possible_key |
154
+ return @headers[possible_key] if @headers.has_key?(possible_key)
155
+ end
156
+ super(method_name, args)
157
+ end
158
+
159
+ # Assign a hash of parameters that should be used for the query string
160
+ def query_string_params=(new_param_hash)
161
+ self.query_string = qs_build(new_param_hash)
162
+ end
163
+
164
+ # Append a freeform segment to the query string in the request. Useful when you
165
+ # want to quickly combine the query strings.
166
+ def append_to_query_string(piece)
167
+ new_qs = '?' + [self.query_string.gsub(/^\?/, ''), piece].reject{|e| e.blank? }.join('&')
168
+ self.query_string = new_qs
169
+ end
170
+
171
+ # Assign a hash of parameters that should be used for POST. These might include
172
+ # objects that act like a file upload (with #original_filename and all)
173
+ def post_params=(new_param_hash_or_str)
174
+ # First see if this is a body payload
175
+ if !new_param_hash_or_str.kind_of?(Hash)
176
+ compose_verbatim_payload(new_param_hash_or_str)
177
+ # then check if anything in the new param hash resembles an uplaod
178
+ elsif extract_values(new_param_hash_or_str).any?{|value| value.respond_to?(:original_filename) }
179
+ compose_multipart_params(new_param_hash_or_str)
180
+ else
181
+ compose_urlencoded_params(new_param_hash_or_str)
182
+ end
183
+ end
184
+
185
+ # Generates a random 22-character MIME boundary (useful for composing multipart POSTs)
186
+ def generate_boundary
187
+ "msqto-" + Mosquito::garbage(16)
188
+ end
189
+
190
+ private
191
+ # Quickly URL-escape something
192
+ def esc(t); Camping.escape(t.to_s);end
193
+
194
+ # Extracts an array of values from a deeply-nested hash
195
+ def extract_values(hash_or_a)
196
+ returning([]) do | vals |
197
+ flatten_hash(hash_or_a) do | keys, value |
198
+ vals << value
199
+ end
200
+ end
201
+ end
202
+
203
+ # Configures the test request for a POST
204
+ def compose_urlencoded_params(new_param_hash)
205
+ self['REQUEST_METHOD'] = 'POST'
206
+ self['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
207
+ @body = StringIO.new(qs_build(new_param_hash))
208
+ end
209
+
210
+ def compose_verbatim_payload(payload)
211
+ self['REQUEST_METHOD'] = 'POST'
212
+ self['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
213
+ @body = StringIO.new(payload)
214
+ end
215
+
216
+ # Configures the test request for a multipart POST
217
+ def compose_multipart_params(new_param_hash)
218
+ # here we check if the encoded segments contain the boundary and should generate a new one
219
+ # if something is matched
220
+ boundary = "----------#{generate_boundary}"
221
+ self['REQUEST_METHOD'] = 'POST'
222
+ self['CONTENT_TYPE'] = "multipart/form-data; boundary=#{boundary}"
223
+ @body = StringIO.new(multipart_build(new_param_hash, boundary))
224
+ end
225
+
226
+ # Return a multipart value segment from a file upload handle.
227
+ def uploaded_file_segment(key, upload_io, boundary)
228
+ <<-EOF
229
+ --#{boundary}\r
230
+ Content-Disposition: form-data; name="#{key}"; filename="#{Camping.escape(upload_io.original_filename)}"\r
231
+ Content-Type: #{upload_io.content_type}\r
232
+ Content-Length: #{upload_io.size}\r
233
+ \r
234
+ #{upload_io.read}\r
235
+ EOF
236
+ end
237
+
238
+ # Return a conventional value segment from a parameter value
239
+ def conventional_segment(key, value, boundary)
240
+ <<-EOF
241
+ --#{boundary}\r
242
+ Content-Disposition: form-data; name="#{key}"\r
243
+ \r
244
+ #{value}\r
245
+ EOF
246
+ end
247
+
248
+ # Build a multipart request body that includes both uploaded files and conventional parameters.
249
+ # To have predictable results we sort the output segments (a hash passed in will not be
250
+ # iterated over in the original definition order anyway, as a good developer should know)
251
+ def multipart_build(params, boundary)
252
+ flat = []
253
+ flatten_hash(params) do | keys, value |
254
+ if keys[-1].nil? # warn the user that Camping will never see that
255
+ raise Mosquito::SageAdvice,
256
+ "Camping will only show you the last element of the array when using multipart forms"
257
+ end
258
+
259
+ flat_key = [esc(keys.shift), keys.map{|k| "[%s]" % esc(k) }].flatten.join
260
+ if value.respond_to?(:original_filename)
261
+ flat << uploaded_file_segment(flat_key, value, boundary)
262
+ else
263
+ flat << conventional_segment(flat_key, value, boundary)
264
+ end
265
+ end
266
+ flat.sort.join("")+"--#{boundary}--\r"
267
+ end
268
+
269
+ # Build a query string. The brackets are NOT encoded. Camping is peculiar in that
270
+ # in contrast to Rails it wants item=1&item=2 to make { item=>[1,2] } to make arrays. We have
271
+ # to account for that.
272
+ def qs_build (hash)
273
+ returning([]) do | qs |
274
+ flatten_hash(hash) do | keys, value |
275
+ keys.pop if keys[-1].nil? # cater for camping array handling
276
+ if value.respond_to?(:original_filename)
277
+ raise Mosquito::SageAdvice, "Sending a file using GET won't do you any good"
278
+ end
279
+
280
+ qs << [esc(keys.shift), keys.map{|k| "[%s]" % esc(k)}, '=', esc(value)].flatten.join
281
+ end
282
+ end.sort.join('&')
283
+ end
284
+
285
+ # Will accept a hash or array of any depth, collapse it into
286
+ # pairs in the form of ([first_level_k, second_level_k, ...], value)
287
+ # and yield these pairs as it goes to the supplied block. Some
288
+ # pairs might be yieled twice because arrays create repeating keys.
289
+ def flatten_hash(hash_or_a, parent_keys = [], &blk)
290
+ if hash_or_a.is_a?(Hash)
291
+ hash_or_a.each_pair do | k, v |
292
+ flatten_hash(v, parent_keys + [k], &blk)
293
+ end
294
+ elsif hash_or_a.is_a?(Array)
295
+ hash_or_a.map do | v |
296
+ blk.call(parent_keys + [nil], v)
297
+ end
298
+ else
299
+ blk.call(parent_keys, hash_or_a)
300
+ end
301
+ end
302
+ end
303
+
304
+ # Works like a wrapper for a simulated file upload. To use:
305
+ #
306
+ # uploaded = Mosquito::MockUpload.new("beach.jpg")
307
+ #
308
+ # This will create a file with the JPEG content-type and 122 bytes of purely random data, which
309
+ # can then be submitted as a part of the test request
310
+ class Mosquito::MockUpload < StringIO
311
+ attr_reader :local_path, :original_filename, :content_type, :extension
312
+ IMAGE_TYPES = {:jpg => 'image/jpeg', :png => 'image/png', :gif => 'image/gif',
313
+ :pdf => 'application/pdf' }.stringify_keys
314
+
315
+ def initialize(filename)
316
+ tempname = "tempfile_#{Time.now.to_i}"
317
+
318
+ @temp = ::Tempfile.new(tempname)
319
+ @local_path = @temp.path
320
+ @original_filename = File.basename(filename)
321
+ @extension = File.extname(@original_filename).gsub(/^\./, '').downcase
322
+ @content_type = IMAGE_TYPES[@extension] || "application/#{@extension}"
323
+
324
+ size = 100.bytes
325
+ super("Stub file %s \n%s\n" % [@original_filename, Mosquito::garbage(size)])
326
+ end
327
+
328
+ def inspect
329
+ info = " @size='#{length}' @filename='#{original_filename}' @content_type='#{content_type}'>"
330
+ super[0..-2] + info
331
+ end
332
+
333
+ end
334
+
335
+ # Stealing our assigns the evil way. This should pose no problem
336
+ # for things that happen in the controller actions, but might be tricky
337
+ # if some other service upstream munges the variables.
338
+ # This service will always get included last (innermost), so it runs regardless of
339
+ # the services upstream (such as HTTP auth) that might not call super
340
+ module Mosquito::Proboscis #:nodoc:
341
+ def service(*a)
342
+ returning(super(*a)) do
343
+ a = instance_variables.inject({}) do | assigns, ivar |
344
+ assigns[ivar.gsub(/^@/, '')] = instance_variable_get(ivar); assigns
345
+ end
346
+ Mosquito.stash(::Camping::H[a])
347
+ end
348
+ end
349
+ end
350
+
351
+ module Camping
352
+
353
+ # The basic Mosquito-wielding test case with some infrastructure
354
+ class Test < Test::Unit::TestCase
355
+ class << self; include Mosquito::Dusty; end
356
+
357
+ def test_default; end #:nodoc
358
+
359
+ # This is needed because Rails fixtures actually try to setup twice
360
+ self.use_transactional_fixtures = false
361
+ #
362
+ # # This is a removal for Rails fixture metamagic
363
+ # def self.method_added(*a); end #:nodoc:
364
+ #
365
+ # def setup #:nodoc:
366
+ # setup_with_fixtures
367
+ # end
368
+ #
369
+ # def teardown #:nodoc:
370
+ # teardown_with_fixtures
371
+ # super # This is good for people who use flexmock/mocha
372
+ # end
373
+
374
+ # The reverse of the reverse of the reverse of assert(condition)
375
+ def deny(condition, message='')
376
+ assert !condition, message
377
+ end
378
+
379
+ # http://project.ioni.st/post/217#post-217
380
+ #
381
+ # def test_new_publication
382
+ # assert_difference(Publication, :count) do
383
+ # post :create, :publication_title => ...
384
+ # # ...
385
+ # end
386
+ # end
387
+ #
388
+ # Is the number of items different?
389
+ #
390
+ # Can be used for increment and decrement.
391
+ #
392
+ def assert_difference(object, method = :count, difference = 1)
393
+ initial_value = object.send(method)
394
+ yield
395
+ assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
396
+ end
397
+
398
+ # See +assert_difference+
399
+ def assert_no_difference(object, method, &block)
400
+ assert_difference object, method, 0, &block
401
+ end
402
+ end
403
+
404
+ # Used to test the controllers and rendering. The test should be called <App>Test
405
+ # (BlogTest for the aplication called Blog). A number of helper instance variables
406
+ # will be created for you - @request, which will contain a Mosquito::MockRequest
407
+ # object, @response (contains the response with headers and body), @cookies (a hash)
408
+ # and @state (a hash). Request and response will be reset in each test.
409
+ class WebTest < Test
410
+
411
+ # Gives you access to the instance variables assigned by the controller
412
+ attr_reader :assigns
413
+
414
+ def test_default; end #:nodoc
415
+
416
+ def setup
417
+ super
418
+ @class_name_abbr = self.class.name.gsub(/^Test/, '')
419
+ @request = Mosquito::MockRequest.new
420
+ @cookies, @response, @assigns = {}, {}, {}
421
+ end
422
+
423
+ # Send a GET request to a URL
424
+ def get(url='/', vars={})
425
+ send_request url, vars, 'GET'
426
+ end
427
+
428
+ # Send a POST request to a URL. All requests except GET will allow
429
+ # setting verbatim request body as the third argument instead
430
+ # of a hash.
431
+ def post(url, post_vars={})
432
+ send_request url, post_vars, 'POST'
433
+ end
434
+
435
+ # Send a DELETE request to a URL. All requests except GET will allow
436
+ # setting verbatim request body as the third argument instead
437
+ # of a hash.
438
+ def delete(url, vars={})
439
+ send_request url, vars, 'DELETE'
440
+ end
441
+
442
+ # Send a PUT request to a URL. All requests except GET will allow
443
+ # setting verbatim request body as the third argument instead
444
+ # of a hash.
445
+ def put(url, vars={})
446
+ send_request url, vars, 'PUT'
447
+ end
448
+
449
+ # Send any request. We will try to guess what you meant - if there are uploads to be
450
+ # processed it's not going to be a GET, that's for sure.
451
+ def send_request(composite_url, post_vars, method)
452
+
453
+ if method.to_s.downcase == "get"
454
+ @request.query_string_params = post_vars
455
+ else
456
+ @request.post_params = post_vars
457
+ end
458
+
459
+ # If there is some stuff in the URL to be used as a query string, why ignore it?
460
+ relative_url, qs_from_url = relativize_url(composite_url)
461
+
462
+ @request.append_to_query_string(qs_from_url) if qs_from_url
463
+
464
+ # We do allow the user to override that one
465
+ @request['REQUEST_METHOD'] = method
466
+
467
+ @request['SCRIPT_NAME'] = '/' + @class_name_abbr.downcase
468
+ @request['PATH_INFO'] = '/' + relative_url
469
+
470
+ # We need to munge this because the PATH_INFO has changed
471
+ @request['REQUEST_URI'] = [@request.SCRIPT_NAME, @request.PATH_INFO].join('').squeeze('/')
472
+ unless @request['QUERY_STRING'].blank?
473
+ @request['REQUEST_URI'] += ('?' + @request['QUERY_STRING'])
474
+ end
475
+
476
+ if @cookies
477
+ @request['HTTP_COOKIE'] = @cookies.map {|k,v| "#{k}=#{Camping.escape(v)}" }.join('; ')
478
+ end
479
+
480
+ # Get the Camping app
481
+ app_module = Kernel.const_get(@class_name_abbr)
482
+
483
+ # Inject the proboscis if we haven't already done so
484
+ app_module.send(:include, Mosquito::Proboscis) unless app_module.ancestors.include?(Mosquito::Proboscis)
485
+
486
+ # Run the request. Do not use eval because
487
+ @response = app_module.run @request.body, @request
488
+
489
+ # Add content_type to response
490
+ eval("class << @response; def content_type; @headers['Content-Type']; end; end")
491
+
492
+ # Downgrade the disguised Mab into a string
493
+ @response.body = @response.body.to_s
494
+ @assigns = Mosquito::unstash
495
+
496
+ # We need to restore the cookies separately so that the app
497
+ # restores our session on the next request. We retrieve cookies and
498
+ # the session in their assigned form instead of parsing the headers and
499
+ # doing a deserialization cycle
500
+ @cookies = @assigns.cookies || H[{}]
501
+ @state = @assigns.state || H[{}]
502
+
503
+ if @response.headers['X-Sendfile']
504
+ @response.body = File.read(@response.headers['X-Sendfile'])
505
+ end
506
+ end
507
+
508
+ # Assert a specific response (:success, :error or a freeform error code as integer)
509
+ def assert_response(status_code)
510
+ case status_code
511
+ when :success
512
+ assert_equal 200, @response.status
513
+ when :redirect
514
+ assert_equal 302, @response.status
515
+ when :error
516
+ assert @response.status >= 500,
517
+ "Response status should have been >= 500 but was #{@response.status}"
518
+ else
519
+ assert_equal status_code, @response.status
520
+ end
521
+ end
522
+
523
+ # Check that the text in the body matches a regexp
524
+ def assert_match_body(regex, message=nil)
525
+ assert_match regex, @response.body, message
526
+ end
527
+
528
+ # Opposite of +assert_match_body+
529
+ def assert_no_match_body(regex, message=nil)
530
+ assert_no_match regex, @response.body, message
531
+ end
532
+
533
+ # Make sure that we are redirected to a certain URL. It's not needed
534
+ # to prepend the URL with a mount (instead of "/blog/latest-news" you can use "/latest-news")
535
+ #
536
+ # Checks both the response status and the url.
537
+ def assert_redirected_to(url, message=nil)
538
+ assert_response :redirect
539
+ assert_equal url, extract_redirection_url, message
540
+ end
541
+
542
+ # Assert that a cookie of name matches a certain pattern
543
+ def assert_cookie(name, pat, message=nil)
544
+ assert_match pat, @cookies[name], message
545
+ end
546
+
547
+ # Nothing is new under the sun
548
+ def follow_redirect
549
+ get extract_redirection_url
550
+ end
551
+
552
+ # Quickly gives you a handle to a file with random content
553
+ def upload(filename)
554
+ Mosquito::MockUpload.new(filename)
555
+ end
556
+
557
+ # Checks that Camping sent us a cookie to attach a session
558
+ def assert_session_started
559
+ assert_not_nil @cookies["camping_sid"],
560
+ "The session ID cookie was empty although session should have started"
561
+ end
562
+
563
+ # The reverse of +assert_session_started+
564
+ def assert_no_session
565
+ assert_nil @cookies["camping_sid"],
566
+ "A session cookie was sent although this should not happen"
567
+ end
568
+
569
+ private
570
+ def extract_redirection_url
571
+ loc = @response.headers['Location']
572
+ path_seg = @response.headers['Location'].path.gsub(%r!/#{@class_name_abbr.downcase}!, '')
573
+ loc.query ? (path_seg + "?" + loc.query).to_s : path_seg.to_s
574
+ end
575
+
576
+ def relativize_url(url)
577
+ parsed = URI.parse(url)
578
+ if !parsed.scheme.blank? && parsed.host != @request.domain
579
+ raise ::Mosquito::NonLocalRequest,
580
+ "You tried to callout to '#{parsed}' which is outside of the test domain (#{@request.domain})"
581
+ end
582
+ # Now remove the path
583
+ parsed.path.gsub!(/^\/#{@class_name_abbr.downcase}\//, '/')
584
+ [parsed.path, parsed.query]
585
+ end
586
+ end
587
+
588
+ # Used to test the models - no infrastructure will be created for running the request
589
+ class ModelTest < Test
590
+ def test_default; end #:nodoc
591
+ end
592
+
593
+ # Deprecated but humane
594
+ UnitTest = ModelTest
595
+ FunctionalTest = WebTest
596
+ end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ class TestApproval < Camping::ModelTest
4
+ TATIVILLE_WIKI = 'http://tativille.fr/wiki/'
5
+ fixtures :pasaporte_profiles
6
+
7
+ def setup
8
+ super; @hulot = Profile.find(1)
9
+ end
10
+
11
+ def test_associations_defined
12
+ assert_kind_of Enumerable, @hulot.approvals, "An association for approvals should exist"
13
+ assert_kind_of NilClass, Approval.new.profile
14
+ end
15
+
16
+ def test_presence_validations
17
+ approval = Approval.new
18
+ deny approval.valid?
19
+ approval.profile = @hulot
20
+
21
+ deny approval.valid?
22
+ approval.trust_root = TATIVILLE_WIKI
23
+ assert approval.valid?, "Now the approval is valid"
24
+ end
25
+
26
+ def test_uniqueness_validations
27
+ approval = Approval.create :profile => @hulot, :trust_root => TATIVILLE_WIKI
28
+ deny approval.new_record?
29
+ assert_raise(ActiveRecord::RecordInvalid) do
30
+ Approval.create! :profile => @hulot, :trust_root => TATIVILLE_WIKI
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ silence_warnings do
4
+ require 'pasaporte/auth/yaml_table'
5
+ require 'pasaporte/auth/yaml_digest_table'
6
+ end
7
+
8
+ class TestAuthBackends < Camping::ModelTest
9
+ STORE = File.dirname(Pasaporte::PATH) + '/pasaporte'
10
+ YAML_T = STORE + '/users_per_domain.yml'
11
+
12
+ def test_yaml_table
13
+ h = {}
14
+ h["test.foo"] = [{"login" => 'schmutz', "pass" => "xx"}]
15
+ h["test.bar"] = [{"login" => 'schtolz', "pass" => "xx"}]
16
+ File.open(YAML_T, 'w') { | f | f << h.to_yaml }
17
+
18
+ auth = Pasaporte::Auth::YamlTable.new
19
+ deny auth.call("foo", "bar", "xxx.com")
20
+ deny auth.call("schmutz", "xx", "test.bar")
21
+
22
+ assert auth.call("schmutz", "xx", "test.foo")
23
+ assert auth.call("schtolz", "xx", "test.bar")
24
+ end
25
+
26
+ # def test_yaml_table_with_implicit_reload
27
+ # h = {}
28
+ # h["test.foo"] = [{"login" => 'schmutz', "pass" => "xx"}]
29
+ # File.open(YAML_T, 'w') { | f | f << h.to_yaml }
30
+ #
31
+ # auth = Pasaporte::Auth::YamlTable.new
32
+ #
33
+ # assert auth.call("schmutz", "xx", "test.foo"), "Schmutz is in the table"
34
+ # deny auth.call("schtolz", "xx", "test.foo"), "This guy is not listed in the table yet"
35
+ #
36
+ # h["test.foo"] << {"login" => 'schtolz', "pass" => "xx"}
37
+ # File.open(YAML_T, 'w') { | f | f << h.to_yaml }
38
+ #
39
+ # assert auth.call("schtolz", "xx", "test.foo"), "Schtolz is now in"
40
+ # end
41
+
42
+ def test_yaml_digest_table
43
+ h = {}
44
+ h["test.foo"] = [{"login" => 'schmutz', "pass_md5" => _md5("xyz")}]
45
+ h["test.bar"] = [{"login" => 'schtolz', "pass_md5" => _md5("watoi")}]
46
+ File.open(YAML_T, 'w') { | f | f << h.to_yaml }
47
+
48
+ auth = Pasaporte::Auth::YamlDigestTable.new
49
+ end
50
+
51
+ def teardown
52
+ FileUtils.rm_rf(YAML_T) rescue Errno::ENOENT
53
+ super
54
+ end
55
+
56
+ def _md5(x)
57
+ Digest::MD5.hexdigest(x).to_s
58
+ end
59
+ end