pasaporte 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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