arrow 1.0.7
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.
- data/ChangeLog +1590 -0
- data/LICENSE +28 -0
- data/README +75 -0
- data/Rakefile +366 -0
- data/Rakefile.local +63 -0
- data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
- data/data/arrow/applets/args.rb +50 -0
- data/data/arrow/applets/config.rb +55 -0
- data/data/arrow/applets/error.rb +63 -0
- data/data/arrow/applets/files.rb +46 -0
- data/data/arrow/applets/inspect.rb +46 -0
- data/data/arrow/applets/nosuchapplet.rb +31 -0
- data/data/arrow/applets/status.rb +92 -0
- data/data/arrow/applets/test.rb +133 -0
- data/data/arrow/applets/tutorial/counter.rb +96 -0
- data/data/arrow/applets/tutorial/dingus.rb +67 -0
- data/data/arrow/applets/tutorial/hello.rb +34 -0
- data/data/arrow/applets/tutorial/hello2.rb +73 -0
- data/data/arrow/applets/tutorial/imgtext.rb +90 -0
- data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
- data/data/arrow/applets/tutorial/index.rb +36 -0
- data/data/arrow/applets/tutorial/logo.rb +98 -0
- data/data/arrow/applets/tutorial/memcache.rb +61 -0
- data/data/arrow/applets/tutorial/missing.rb +37 -0
- data/data/arrow/applets/tutorial/protected.rb +100 -0
- data/data/arrow/applets/tutorial/redirector.rb +52 -0
- data/data/arrow/applets/tutorial/rndimages.rb +159 -0
- data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
- data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
- data/data/arrow/applets/tutorial/superhello.rb +72 -0
- data/data/arrow/applets/tutorial/timeclock.rb +78 -0
- data/data/arrow/applets/view-applet.rb +123 -0
- data/data/arrow/applets/view-template.rb +85 -0
- data/data/arrow/applets/wiki.rb +274 -0
- data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
- data/data/arrow/templates/applet-status.tmpl +153 -0
- data/data/arrow/templates/args-display.tmpl +120 -0
- data/data/arrow/templates/config/display-table.tmpl +36 -0
- data/data/arrow/templates/config/display.tmpl +36 -0
- data/data/arrow/templates/counter-deleted.tmpl +33 -0
- data/data/arrow/templates/counter.tmpl +59 -0
- data/data/arrow/templates/dingus.tmpl +55 -0
- data/data/arrow/templates/enumtable.tmpl +8 -0
- data/data/arrow/templates/error-display.tmpl +92 -0
- data/data/arrow/templates/filemap.tmpl +89 -0
- data/data/arrow/templates/hello-world-src.tmpl +34 -0
- data/data/arrow/templates/hello-world.tmpl +60 -0
- data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
- data/data/arrow/templates/imgtext/form.tmpl +70 -0
- data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
- data/data/arrow/templates/imgtext/reload.tmpl +55 -0
- data/data/arrow/templates/inspect/display.tmpl +80 -0
- data/data/arrow/templates/loginform.tmpl +64 -0
- data/data/arrow/templates/logout.tmpl +32 -0
- data/data/arrow/templates/memcache/display.tmpl +41 -0
- data/data/arrow/templates/navbar.incl +27 -0
- data/data/arrow/templates/nosuchapplet.tmpl +32 -0
- data/data/arrow/templates/printsource.tmpl +35 -0
- data/data/arrow/templates/protected.tmpl +36 -0
- data/data/arrow/templates/rndimages.tmpl +38 -0
- data/data/arrow/templates/service-response.tmpl +13 -0
- data/data/arrow/templates/sharenotes/display.tmpl +38 -0
- data/data/arrow/templates/status.tmpl +120 -0
- data/data/arrow/templates/templateviewer.tmpl +43 -0
- data/data/arrow/templates/test/harness.tmpl +57 -0
- data/data/arrow/templates/test/list.tmpl +48 -0
- data/data/arrow/templates/test/problem.tmpl +42 -0
- data/data/arrow/templates/tutorial/index.tmpl +37 -0
- data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
- data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
- data/data/arrow/templates/view-applet.tmpl +40 -0
- data/data/arrow/templates/view-template.tmpl +83 -0
- data/data/arrow/templates/wiki/formerror.tmpl +47 -0
- data/data/arrow/templates/wiki/markup_help.incl +6 -0
- data/data/arrow/templates/wiki/new.tmpl +56 -0
- data/data/arrow/templates/wiki/new_system.tmpl +122 -0
- data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
- data/data/arrow/templates/wiki/show.tmpl +34 -0
- data/docs/manual/layouts/default.page +43 -0
- data/docs/manual/lib/api-filter.rb +81 -0
- data/docs/manual/lib/editorial-filter.rb +64 -0
- data/docs/manual/lib/examples-filter.rb +244 -0
- data/docs/manual/lib/links-filter.rb +117 -0
- data/lib/apache/fakerequest.rb +448 -0
- data/lib/apache/logger.rb +33 -0
- data/lib/arrow.rb +51 -0
- data/lib/arrow/acceptparam.rb +207 -0
- data/lib/arrow/applet.rb +725 -0
- data/lib/arrow/appletmixins.rb +218 -0
- data/lib/arrow/appletregistry.rb +590 -0
- data/lib/arrow/applettestcase.rb +503 -0
- data/lib/arrow/broker.rb +255 -0
- data/lib/arrow/cache.rb +176 -0
- data/lib/arrow/config-loaders/yaml.rb +75 -0
- data/lib/arrow/config.rb +615 -0
- data/lib/arrow/constants.rb +24 -0
- data/lib/arrow/cookie.rb +359 -0
- data/lib/arrow/cookieset.rb +108 -0
- data/lib/arrow/dispatcher.rb +368 -0
- data/lib/arrow/dispatcherloader.rb +50 -0
- data/lib/arrow/exceptions.rb +61 -0
- data/lib/arrow/fallbackhandler.rb +48 -0
- data/lib/arrow/formvalidator.rb +631 -0
- data/lib/arrow/htmltokenizer.rb +343 -0
- data/lib/arrow/logger.rb +488 -0
- data/lib/arrow/logger/apacheoutputter.rb +69 -0
- data/lib/arrow/logger/arrayoutputter.rb +63 -0
- data/lib/arrow/logger/coloroutputter.rb +111 -0
- data/lib/arrow/logger/fileoutputter.rb +96 -0
- data/lib/arrow/logger/htmloutputter.rb +54 -0
- data/lib/arrow/logger/outputter.rb +123 -0
- data/lib/arrow/mixins.rb +425 -0
- data/lib/arrow/monkeypatches.rb +94 -0
- data/lib/arrow/object.rb +117 -0
- data/lib/arrow/path.rb +196 -0
- data/lib/arrow/service.rb +447 -0
- data/lib/arrow/session.rb +289 -0
- data/lib/arrow/session/dbstore.rb +100 -0
- data/lib/arrow/session/filelock.rb +160 -0
- data/lib/arrow/session/filestore.rb +132 -0
- data/lib/arrow/session/id.rb +98 -0
- data/lib/arrow/session/lock.rb +253 -0
- data/lib/arrow/session/md5id.rb +42 -0
- data/lib/arrow/session/nulllock.rb +42 -0
- data/lib/arrow/session/posixlock.rb +166 -0
- data/lib/arrow/session/sha1id.rb +54 -0
- data/lib/arrow/session/store.rb +366 -0
- data/lib/arrow/session/usertrackid.rb +52 -0
- data/lib/arrow/spechelpers.rb +73 -0
- data/lib/arrow/template.rb +713 -0
- data/lib/arrow/template/attr.rb +31 -0
- data/lib/arrow/template/call.rb +31 -0
- data/lib/arrow/template/comment.rb +33 -0
- data/lib/arrow/template/container.rb +118 -0
- data/lib/arrow/template/else.rb +41 -0
- data/lib/arrow/template/elsif.rb +44 -0
- data/lib/arrow/template/escape.rb +53 -0
- data/lib/arrow/template/export.rb +87 -0
- data/lib/arrow/template/for.rb +145 -0
- data/lib/arrow/template/if.rb +78 -0
- data/lib/arrow/template/import.rb +119 -0
- data/lib/arrow/template/include.rb +206 -0
- data/lib/arrow/template/iterator.rb +208 -0
- data/lib/arrow/template/nodes.rb +734 -0
- data/lib/arrow/template/parser.rb +571 -0
- data/lib/arrow/template/prettyprint.rb +53 -0
- data/lib/arrow/template/render.rb +191 -0
- data/lib/arrow/template/selectlist.rb +94 -0
- data/lib/arrow/template/set.rb +87 -0
- data/lib/arrow/template/timedelta.rb +81 -0
- data/lib/arrow/template/unless.rb +78 -0
- data/lib/arrow/template/urlencode.rb +51 -0
- data/lib/arrow/template/yield.rb +139 -0
- data/lib/arrow/templatefactory.rb +125 -0
- data/lib/arrow/testcase.rb +567 -0
- data/lib/arrow/transaction.rb +608 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +114 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +282 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +278 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/spec/arrow/acceptparam_spec.rb +157 -0
- data/spec/arrow/applet_spec.rb +575 -0
- data/spec/arrow/appletmixins_spec.rb +409 -0
- data/spec/arrow/appletregistry_spec.rb +294 -0
- data/spec/arrow/broker_spec.rb +153 -0
- data/spec/arrow/config_spec.rb +224 -0
- data/spec/arrow/cookieset_spec.rb +164 -0
- data/spec/arrow/dispatcher_spec.rb +137 -0
- data/spec/arrow/dispatcherloader_spec.rb +65 -0
- data/spec/arrow/formvalidator_spec.rb +781 -0
- data/spec/arrow/logger_spec.rb +346 -0
- data/spec/arrow/mixins_spec.rb +120 -0
- data/spec/arrow/service_spec.rb +645 -0
- data/spec/arrow/session_spec.rb +121 -0
- data/spec/arrow/template/iterator_spec.rb +222 -0
- data/spec/arrow/templatefactory_spec.rb +185 -0
- data/spec/arrow/transaction_spec.rb +319 -0
- data/spec/arrow_spec.rb +37 -0
- data/spec/lib/appletmatchers.rb +281 -0
- data/spec/lib/constants.rb +77 -0
- data/spec/lib/helpers.rb +41 -0
- data/spec/lib/matchers.rb +44 -0
- data/tests/cookie.tests.rb +310 -0
- data/tests/path.tests.rb +157 -0
- data/tests/session.tests.rb +111 -0
- data/tests/session_id.tests.rb +82 -0
- data/tests/session_lock.tests.rb +191 -0
- data/tests/session_store.tests.rb +53 -0
- data/tests/template.tests.rb +1360 -0
- metadata +339 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
BEGIN {
|
|
4
|
+
require 'pathname'
|
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
|
6
|
+
|
|
7
|
+
libdir = basedir + "lib"
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
require 'rubygems'
|
|
13
|
+
require 'spec'
|
|
14
|
+
require 'tempfile'
|
|
15
|
+
require 'tmpdir'
|
|
16
|
+
require 'pathname'
|
|
17
|
+
require 'apache/fakerequest'
|
|
18
|
+
require 'arrow'
|
|
19
|
+
require 'arrow/dispatcher'
|
|
20
|
+
require 'arrow/config-loaders/yaml'
|
|
21
|
+
|
|
22
|
+
require 'spec/lib/matchers'
|
|
23
|
+
require 'spec/lib/constants'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
include Arrow::TestConstants
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#####################################################################
|
|
30
|
+
### C O N T E X T S
|
|
31
|
+
#####################################################################
|
|
32
|
+
|
|
33
|
+
describe Arrow::Dispatcher do
|
|
34
|
+
include Arrow::TimeMatchers
|
|
35
|
+
|
|
36
|
+
before(:all) do
|
|
37
|
+
@tmpfile = Tempfile.new( 'test.conf', '.' )
|
|
38
|
+
TEST_CONFIG.dup.write( @tmpfile.path )
|
|
39
|
+
@tmpfile.close
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
after( :all ) do
|
|
43
|
+
@tmpfile.delete
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
after(:each) do
|
|
48
|
+
Pathname.glob( "%s/arrow-fatal*" % Dir.tmpdir ).each do |f|
|
|
49
|
+
f.unlink
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Specs
|
|
55
|
+
|
|
56
|
+
it "raises an error when its factory method is called with " +
|
|
57
|
+
"something other than a String or Hash" do
|
|
58
|
+
lambda {
|
|
59
|
+
Arrow::Dispatcher.create( :something )
|
|
60
|
+
}.should raise_error( ArgumentError, /invalid config hash/i )
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "writes a crashlog to TMPDIR when create fails" do
|
|
64
|
+
crashlog = Pathname.new( Dir.tmpdir + "/arrow-fatal.log.#{$$}" )
|
|
65
|
+
crashlog.unlink if crashlog.exist?
|
|
66
|
+
|
|
67
|
+
begin
|
|
68
|
+
Arrow::Dispatcher.create( :something )
|
|
69
|
+
rescue ArgumentError
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
crashlog.exist?.should == true
|
|
73
|
+
crashlog.ctime.should be_after( Time.now - 60 )
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "assumes a string configspec is a default configfile" do
|
|
77
|
+
dispatcher = Arrow::Dispatcher.create( @tmpfile.path )
|
|
78
|
+
Arrow::Dispatcher.instance.should equal( dispatcher )
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "reads dispatcher configs from an object's #read method if it has one" do
|
|
82
|
+
config_obj = mock( "configuration object", :null_object => true )
|
|
83
|
+
config_obj.should_receive( :respond_to? ).with( :read ).and_return( true )
|
|
84
|
+
config_obj.should_receive( :read ).and_return( YAML.dump({}) )
|
|
85
|
+
|
|
86
|
+
Arrow::Dispatcher.create_from_hosts_file( config_obj )
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
describe "instance" do
|
|
91
|
+
it "has an associated name" do
|
|
92
|
+
@config = Arrow::Config.new( TEST_CONFIG_HASH )
|
|
93
|
+
@dispatcher = Arrow::Dispatcher.new( "test", @config )
|
|
94
|
+
@dispatcher.name.should == "test"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe " running under $SAFE = 1" do
|
|
98
|
+
before(:all) do
|
|
99
|
+
@configfile = Tempfile.new( 'test.conf', '.' )
|
|
100
|
+
@configfile.print( YAML.dump(TEST_CONFIG_HASH) )
|
|
101
|
+
@configfile.close
|
|
102
|
+
|
|
103
|
+
hosts_config = YAML.dump({
|
|
104
|
+
:testing => @configfile.path,
|
|
105
|
+
})
|
|
106
|
+
@host_configio = StringIO.new( hosts_config )
|
|
107
|
+
@host_configio.taint
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
after(:all) do
|
|
111
|
+
@configfile.delete
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Fake actually loading the config file
|
|
115
|
+
before(:each) do
|
|
116
|
+
@host_configio.rewind
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should be able to create dispatchers from a tainted hosts file" do
|
|
120
|
+
$SAFE = 1
|
|
121
|
+
rval = nil
|
|
122
|
+
|
|
123
|
+
lambda {
|
|
124
|
+
rval = Arrow::Dispatcher.create_from_hosts_file( @host_configio )
|
|
125
|
+
}.should_not raise_error()
|
|
126
|
+
|
|
127
|
+
rval.should be_an_instance_of( Hash )
|
|
128
|
+
rval.should have(2).keys
|
|
129
|
+
rval.keys.should include( :testing )
|
|
130
|
+
rval.keys.should include( File.expand_path(@configfile.path) )
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# vim: set nosta noet ts=4 sw=4:
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
BEGIN {
|
|
4
|
+
require 'pathname'
|
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
|
6
|
+
|
|
7
|
+
libdir = basedir + "lib"
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
require 'spec'
|
|
13
|
+
require 'spec/lib/helpers'
|
|
14
|
+
|
|
15
|
+
require 'apache/fakerequest'
|
|
16
|
+
|
|
17
|
+
require 'arrow'
|
|
18
|
+
require 'arrow/dispatcherloader'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#####################################################################
|
|
22
|
+
### C O N T E X T S
|
|
23
|
+
#####################################################################
|
|
24
|
+
|
|
25
|
+
describe Arrow::DispatcherLoader do
|
|
26
|
+
include Arrow::SpecHelpers
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
before( :all ) do
|
|
30
|
+
setup_logging( :crit )
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
after( :all ) do
|
|
34
|
+
reset_logging()
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "creates Arrow::Dispatchers from a registered configfile on child_init" do
|
|
38
|
+
req = Apache::Request.new
|
|
39
|
+
Arrow::Dispatcher.should_receive( :create_from_hosts_file ).with( 'hosts.yml' )
|
|
40
|
+
Arrow::DispatcherLoader.new( 'hosts.yml' ).child_init( req ).should == Apache::OK
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "handles errors while loading dispachers by logging to a tempfile and to Apache's log" do
|
|
44
|
+
req = Apache::Request.new
|
|
45
|
+
Arrow::Dispatcher.should_receive( :create_from_hosts_file ).with( 'hosts.yml' ).
|
|
46
|
+
and_raise( RuntimeError.new("something bad happened") )
|
|
47
|
+
|
|
48
|
+
logpath = mock( "error logfile Pathname" )
|
|
49
|
+
io = mock( "error logfile IO" )
|
|
50
|
+
|
|
51
|
+
Pathname.should_receive( :new ).with( instance_of(String) ).
|
|
52
|
+
and_return( logpath )
|
|
53
|
+
logpath.should_receive( :+ ).with( 'arrow-dispatcher-failure.log' ).and_return( logpath )
|
|
54
|
+
logpath.should_receive( :open ).with( IO::WRONLY|IO::TRUNC|IO::CREAT ).and_yield( io )
|
|
55
|
+
io.should_receive( :puts ).with( /something bad happened/ )
|
|
56
|
+
io.should_receive( :flush )
|
|
57
|
+
|
|
58
|
+
expect {
|
|
59
|
+
Arrow::DispatcherLoader.new( 'hosts.yml' ).child_init( req )
|
|
60
|
+
}.to raise_error( RuntimeError, /something bad happened/ )
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
BEGIN {
|
|
4
|
+
require 'pathname'
|
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
|
6
|
+
|
|
7
|
+
libdir = basedir + "lib"
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
require 'spec'
|
|
14
|
+
require 'apache/fakerequest'
|
|
15
|
+
require 'arrow'
|
|
16
|
+
require 'spec/lib/helpers'
|
|
17
|
+
require 'arrow/formvalidator'
|
|
18
|
+
require 'date'
|
|
19
|
+
rescue LoadError
|
|
20
|
+
unless Object.const_defined?( :Gem )
|
|
21
|
+
require 'rubygems'
|
|
22
|
+
retry
|
|
23
|
+
end
|
|
24
|
+
raise
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
#####################################################################
|
|
29
|
+
### C O N T E X T S
|
|
30
|
+
#####################################################################
|
|
31
|
+
TestProfile = {
|
|
32
|
+
:required => [ :required ],
|
|
33
|
+
:optional => %w{
|
|
34
|
+
optional number int_constraint bool_constraint email_constraint
|
|
35
|
+
host_constraint regexp_w_captures regexp_w_one_capture
|
|
36
|
+
alpha_constraint alphanumeric_constraint printable_constraint
|
|
37
|
+
proc_constraint uri_constraint
|
|
38
|
+
},
|
|
39
|
+
:constraints => {
|
|
40
|
+
:number => /^\d+$/,
|
|
41
|
+
:regexp_w_captures => /(\w+)(\S+)?/,
|
|
42
|
+
:regexp_w_one_capture => /(\w+)/,
|
|
43
|
+
:int_constraint => :integer,
|
|
44
|
+
:bool_constraint => :boolean,
|
|
45
|
+
:email_constraint => :email,
|
|
46
|
+
:uri_constraint => :uri,
|
|
47
|
+
:host_constraint => :hostname,
|
|
48
|
+
:alpha_constraint => :alpha,
|
|
49
|
+
:alphanumeric_constraint => :alphanumeric,
|
|
50
|
+
:printable_constraint => :printable,
|
|
51
|
+
:proc_constraint => Proc.new {|d| Date.parse(d) rescue nil },
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
describe Arrow::FormValidator do
|
|
57
|
+
include Arrow::SpecHelpers
|
|
58
|
+
|
|
59
|
+
before( :all ) do
|
|
60
|
+
setup_logging( :crit )
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
before(:each) do
|
|
64
|
+
@validator = Arrow::FormValidator.new( TestProfile )
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
after( :all ) do
|
|
68
|
+
reset_logging()
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Test index operator interface
|
|
74
|
+
it "should provide read and write access to valid args via the index operator" do
|
|
75
|
+
rval = nil
|
|
76
|
+
|
|
77
|
+
@validator.validate( {'required' => "1"} )
|
|
78
|
+
@validator[:required].should == "1"
|
|
79
|
+
|
|
80
|
+
@validator[:required] = "bar"
|
|
81
|
+
@validator["required"].should == "bar"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
it "should untaint valid args if told to do so" do
|
|
86
|
+
rval = nil
|
|
87
|
+
tainted_one = "1"
|
|
88
|
+
tainted_one.taint
|
|
89
|
+
|
|
90
|
+
@validator.validate( {'required' => 1, 'number' => tainted_one},
|
|
91
|
+
:untaint_all_constraints => true )
|
|
92
|
+
|
|
93
|
+
Arrow::Logger.global.notice "Validator: %p" % [@validator]
|
|
94
|
+
|
|
95
|
+
@validator[:number].should == "1"
|
|
96
|
+
@validator[:number].tainted?.should be_false()
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
it "should untaint field names" do
|
|
101
|
+
rval = nil
|
|
102
|
+
tainted_one = "1"
|
|
103
|
+
tainted_one.taint
|
|
104
|
+
|
|
105
|
+
@validator.validate( {'required' => 1, 'number' => tainted_one},
|
|
106
|
+
:untaint_all_constraints => true )
|
|
107
|
+
|
|
108
|
+
Arrow::Logger.global.notice "Validator: %p" % [@validator]
|
|
109
|
+
|
|
110
|
+
@validator[:number].should == "1"
|
|
111
|
+
@validator[:number].tainted?.should be_false()
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
it "should return the capture from a regexp constraint if it has only one" do
|
|
116
|
+
rval = nil
|
|
117
|
+
params = { 'required' => 1, 'regexp_w_one_capture' => " ygdrassil " }
|
|
118
|
+
|
|
119
|
+
@validator.validate( params, :untaint_all_constraints => true )
|
|
120
|
+
|
|
121
|
+
Arrow::Logger.global.notice "Validator: %p" % [@validator]
|
|
122
|
+
|
|
123
|
+
@validator[:regexp_w_one_capture].should == 'ygdrassil'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "should return the captures from a regexp constraint as an array if it has more than one" do
|
|
127
|
+
rval = nil
|
|
128
|
+
params = { 'required' => 1, 'regexp_w_captures' => " the1tree(!) " }
|
|
129
|
+
|
|
130
|
+
@validator.validate( params, :untaint_all_constraints => true )
|
|
131
|
+
|
|
132
|
+
Arrow::Logger.global.notice "Validator: %p" % [@validator]
|
|
133
|
+
|
|
134
|
+
@validator[:regexp_w_captures].should == ['the1tree', '(!)']
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "should return the captures from a regexp constraint as an array " +
|
|
138
|
+
"even if an optional capture doesn't match anything" do
|
|
139
|
+
rval = nil
|
|
140
|
+
params = { 'required' => 1, 'regexp_w_captures' => " the1tree " }
|
|
141
|
+
|
|
142
|
+
@validator.validate( params, :untaint_all_constraints => true )
|
|
143
|
+
|
|
144
|
+
Arrow::Logger.global.notice "Validator: %p" % [@validator]
|
|
145
|
+
|
|
146
|
+
@validator[:regexp_w_captures].should == ['the1tree', nil]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "knows the names of fields that were required but missing from the parameters" do
|
|
150
|
+
@validator.validate( {} )
|
|
151
|
+
|
|
152
|
+
@validator.should have_errors()
|
|
153
|
+
@validator.should_not be_okay()
|
|
154
|
+
|
|
155
|
+
@validator.missing.should have(1).members
|
|
156
|
+
@validator.missing.should == ['required']
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "knows the names of fields that did not meet their constraints" do
|
|
160
|
+
params = {'number' => 'rhinoceros'}
|
|
161
|
+
@validator.validate( params )
|
|
162
|
+
|
|
163
|
+
@validator.should have_errors()
|
|
164
|
+
@validator.should_not be_okay()
|
|
165
|
+
|
|
166
|
+
@validator.invalid.should have(1).keys
|
|
167
|
+
@validator.invalid.keys.should == ['number']
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "can return a combined list of all problem parameters, which includes " +
|
|
171
|
+
" both missing and invalid fields" do
|
|
172
|
+
params = {'number' => 'rhinoceros'}
|
|
173
|
+
@validator.validate( params )
|
|
174
|
+
|
|
175
|
+
@validator.should have_errors()
|
|
176
|
+
@validator.should_not be_okay()
|
|
177
|
+
|
|
178
|
+
@validator.error_fields.should have(2).members
|
|
179
|
+
@validator.error_fields.should include('number')
|
|
180
|
+
@validator.error_fields.should include('required')
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it "can return human descriptions of validation errors" do
|
|
184
|
+
params = {'number' => 'rhinoceros', 'unknown' => "1"}
|
|
185
|
+
@validator.validate( params )
|
|
186
|
+
|
|
187
|
+
@validator.error_messages.should have(2).members
|
|
188
|
+
@validator.error_messages.should include("Missing value for 'Required'")
|
|
189
|
+
@validator.error_messages.should include("Invalid value for 'Number'")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "can include unknown fields in its human descriptions of validation errors" do
|
|
193
|
+
params = {'number' => 'rhinoceros', 'unknown' => "1"}
|
|
194
|
+
@validator.validate( params )
|
|
195
|
+
|
|
196
|
+
@validator.error_messages(true).should have(3).members
|
|
197
|
+
@validator.error_messages(true).should include("Missing value for 'Required'")
|
|
198
|
+
@validator.error_messages(true).should include("Invalid value for 'Number'")
|
|
199
|
+
@validator.error_messages(true).should include("Unknown parameter 'Unknown'")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "can use provided descriptions of parameters when constructing human " +
|
|
203
|
+
"validation error messages" do
|
|
204
|
+
descs = {
|
|
205
|
+
:number => "Numeral",
|
|
206
|
+
:required => "Test Name",
|
|
207
|
+
}
|
|
208
|
+
params = {'number' => 'rhinoceros', 'unknown' => "1"}
|
|
209
|
+
@validator.validate( params, :descriptions => descs )
|
|
210
|
+
|
|
211
|
+
@validator.error_messages.should have(2).members
|
|
212
|
+
@validator.error_messages.should include("Missing value for 'Test Name'")
|
|
213
|
+
@validator.error_messages.should include("Invalid value for 'Numeral'")
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
it "capitalizes the names of simple fields for descriptions" do
|
|
218
|
+
@validator.get_description( "required" ).should == 'Required'
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
it "splits apart underbarred field names into capitalized words for descriptions" do
|
|
222
|
+
@validator.get_description( "rodent_size" ).should == 'Rodent Size'
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "uses the key for descriptions of hash fields" do
|
|
226
|
+
@validator.get_description( "rodent[size]" ).should == 'Size'
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "uses separate capitalized words for descriptions of hash fields with underbarred keys " do
|
|
230
|
+
@validator.get_description( "castle[baron_id]" ).should == 'Baron Id'
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it "should be able to coalesce simple hash fields into a hash of validated values" do
|
|
234
|
+
@validator.validate( {'rodent[size]' => 'unusual'}, :optional => ['rodent[size]'] )
|
|
235
|
+
|
|
236
|
+
@validator.valid.should == {'rodent' => {'size' => 'unusual'}}
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "should be able to coalesce complex hash fields into a nested hash of validated values" do
|
|
240
|
+
profile = {
|
|
241
|
+
:optional => [
|
|
242
|
+
'recipe[ingredient][name]',
|
|
243
|
+
'recipe[ingredient][cost]',
|
|
244
|
+
'recipe[yield]'
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
args = {
|
|
248
|
+
'recipe[ingredient][name]' => 'nutmeg',
|
|
249
|
+
'recipe[ingredient][cost]' => '$0.18',
|
|
250
|
+
'recipe[yield]' => '2 loaves',
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@validator.validate( args, profile )
|
|
254
|
+
@validator.valid.should == {
|
|
255
|
+
'recipe' => {
|
|
256
|
+
'ingredient' => { 'name' => 'nutmeg', 'cost' => '$0.18' },
|
|
257
|
+
'yield' => '2 loaves'
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it "should untaint both keys and values in complex hash fields if untainting is turned on" do
|
|
263
|
+
profile = {
|
|
264
|
+
:required => [
|
|
265
|
+
'recipe[ingredient][rarity]',
|
|
266
|
+
],
|
|
267
|
+
:optional => [
|
|
268
|
+
'recipe[ingredient][name]',
|
|
269
|
+
'recipe[ingredient][cost]',
|
|
270
|
+
'recipe[yield]'
|
|
271
|
+
],
|
|
272
|
+
:constraints => {
|
|
273
|
+
'recipe[ingredient][rarity]' => /^([\w\-]+)$/,
|
|
274
|
+
},
|
|
275
|
+
:untaint_all_constraints => true,
|
|
276
|
+
}
|
|
277
|
+
args = {
|
|
278
|
+
'recipe[ingredient][rarity]'.taint => 'super-rare'.taint,
|
|
279
|
+
'recipe[ingredient][name]'.taint => 'nutmeg'.taint,
|
|
280
|
+
'recipe[ingredient][cost]'.taint => '$0.18'.taint,
|
|
281
|
+
'recipe[yield]'.taint => '2 loaves'.taint,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@validator.validate( args, profile )
|
|
285
|
+
|
|
286
|
+
@validator.valid.should == {
|
|
287
|
+
'recipe' => {
|
|
288
|
+
'ingredient' => { 'name' => 'nutmeg', 'cost' => '$0.18', 'rarity' => 'super-rare' },
|
|
289
|
+
'yield' => '2 loaves'
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@validator.valid.keys.all? {|key| key.should_not be_tainted() }
|
|
294
|
+
@validator.valid.values.all? {|key| key.should_not be_tainted() }
|
|
295
|
+
@validator.valid['recipe'].keys.all? {|key| key.should_not be_tainted() }
|
|
296
|
+
@validator.valid['recipe']['ingredient'].keys.all? {|key| key.should_not be_tainted() }
|
|
297
|
+
@validator.valid['recipe']['yield'].should_not be_tainted()
|
|
298
|
+
@validator.valid['recipe']['ingredient']['rarity'].should_not be_tainted()
|
|
299
|
+
@validator.valid['recipe']['ingredient']['name'].should_not be_tainted()
|
|
300
|
+
@validator.valid['recipe']['ingredient']['cost'].should_not be_tainted()
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it "accepts the value 'true' for fields with boolean constraints" do
|
|
304
|
+
params = {'required' => '1', 'bool_constraint' => 'true'}
|
|
305
|
+
|
|
306
|
+
@validator.validate( params )
|
|
307
|
+
|
|
308
|
+
@validator.should be_okay()
|
|
309
|
+
@validator.should_not have_errors()
|
|
310
|
+
|
|
311
|
+
@validator[:bool_constraint].should be_true()
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it "accepts the value 't' for fields with boolean constraints" do
|
|
315
|
+
params = {'required' => '1', 'bool_constraint' => 't'}
|
|
316
|
+
|
|
317
|
+
@validator.validate( params )
|
|
318
|
+
|
|
319
|
+
@validator.should be_okay()
|
|
320
|
+
@validator.should_not have_errors()
|
|
321
|
+
|
|
322
|
+
@validator[:bool_constraint].should be_true()
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
it "accepts the value 'yes' for fields with boolean constraints" do
|
|
326
|
+
params = {'required' => '1', 'bool_constraint' => 'yes'}
|
|
327
|
+
|
|
328
|
+
@validator.validate( params )
|
|
329
|
+
|
|
330
|
+
@validator.should be_okay()
|
|
331
|
+
@validator.should_not have_errors()
|
|
332
|
+
|
|
333
|
+
@validator[:bool_constraint].should be_true()
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
it "accepts the value 'y' for fields with boolean constraints" do
|
|
337
|
+
params = {'required' => '1', 'bool_constraint' => 'y'}
|
|
338
|
+
|
|
339
|
+
@validator.validate( params )
|
|
340
|
+
|
|
341
|
+
@validator.should be_okay()
|
|
342
|
+
@validator.should_not have_errors()
|
|
343
|
+
|
|
344
|
+
@validator[:bool_constraint].should be_true()
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it "accepts the value '1' for fields with boolean constraints" do
|
|
348
|
+
params = {'required' => '1', 'bool_constraint' => '1'}
|
|
349
|
+
|
|
350
|
+
@validator.validate( params )
|
|
351
|
+
|
|
352
|
+
@validator.should be_okay()
|
|
353
|
+
@validator.should_not have_errors()
|
|
354
|
+
|
|
355
|
+
@validator[:bool_constraint].should be_true()
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "accepts the value 'false' for fields with boolean constraints" do
|
|
359
|
+
params = {'required' => '1', 'bool_constraint' => 'false'}
|
|
360
|
+
|
|
361
|
+
@validator.validate( params )
|
|
362
|
+
|
|
363
|
+
@validator.should be_okay()
|
|
364
|
+
@validator.should_not have_errors()
|
|
365
|
+
|
|
366
|
+
@validator[:bool_constraint].should be_false()
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
it "accepts the value 'f' for fields with boolean constraints" do
|
|
370
|
+
params = {'required' => '1', 'bool_constraint' => 'f'}
|
|
371
|
+
|
|
372
|
+
@validator.validate( params )
|
|
373
|
+
|
|
374
|
+
@validator.should be_okay()
|
|
375
|
+
@validator.should_not have_errors()
|
|
376
|
+
|
|
377
|
+
@validator[:bool_constraint].should be_false()
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
it "accepts the value 'no' for fields with boolean constraints" do
|
|
381
|
+
params = {'required' => '1', 'bool_constraint' => 'no'}
|
|
382
|
+
|
|
383
|
+
@validator.validate( params )
|
|
384
|
+
|
|
385
|
+
@validator.should be_okay()
|
|
386
|
+
@validator.should_not have_errors()
|
|
387
|
+
|
|
388
|
+
@validator[:bool_constraint].should be_false()
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
it "accepts the value 'n' for fields with boolean constraints" do
|
|
392
|
+
params = {'required' => '1', 'bool_constraint' => 'n'}
|
|
393
|
+
|
|
394
|
+
@validator.validate( params )
|
|
395
|
+
|
|
396
|
+
@validator.should be_okay()
|
|
397
|
+
@validator.should_not have_errors()
|
|
398
|
+
|
|
399
|
+
@validator[:bool_constraint].should be_false()
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
it "accepts the value '0' for fields with boolean constraints" do
|
|
403
|
+
params = {'required' => '1', 'bool_constraint' => '0'}
|
|
404
|
+
|
|
405
|
+
@validator.validate( params )
|
|
406
|
+
|
|
407
|
+
@validator.should be_okay()
|
|
408
|
+
@validator.should_not have_errors()
|
|
409
|
+
|
|
410
|
+
@validator[:bool_constraint].should be_false()
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
it "rejects non-boolean parameters for fields with boolean constraints" do
|
|
414
|
+
params = {'required' => '1', 'bool_constraint' => 'peanut'}
|
|
415
|
+
|
|
416
|
+
@validator.validate( params )
|
|
417
|
+
|
|
418
|
+
@validator.should_not be_okay()
|
|
419
|
+
@validator.should have_errors()
|
|
420
|
+
|
|
421
|
+
@validator[:bool_constraint].should be_nil()
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it "accepts simple integers for fields with integer constraints" do
|
|
425
|
+
params = {'required' => '1', 'int_constraint' => '11'}
|
|
426
|
+
|
|
427
|
+
@validator.validate( params )
|
|
428
|
+
|
|
429
|
+
@validator.should be_okay()
|
|
430
|
+
@validator.should_not have_errors()
|
|
431
|
+
|
|
432
|
+
@validator[:int_constraint].should == 11
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
it "accepts '0' for fields with integer constraints" do
|
|
436
|
+
params = {'required' => '1', 'int_constraint' => '0'}
|
|
437
|
+
|
|
438
|
+
@validator.validate( params )
|
|
439
|
+
|
|
440
|
+
@validator.should be_okay()
|
|
441
|
+
@validator.should_not have_errors()
|
|
442
|
+
|
|
443
|
+
@validator[:int_constraint].should == 0
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
it "accepts negative integers for fields with integer constraints" do
|
|
447
|
+
params = {'required' => '1', 'int_constraint' => '-407'}
|
|
448
|
+
|
|
449
|
+
@validator.validate( params )
|
|
450
|
+
|
|
451
|
+
@validator.should be_okay()
|
|
452
|
+
@validator.should_not have_errors()
|
|
453
|
+
|
|
454
|
+
@validator[:int_constraint].should == -407
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
it "rejects non-integers for fields with integer constraints" do
|
|
458
|
+
params = {'required' => '1', 'int_constraint' => '11.1'}
|
|
459
|
+
|
|
460
|
+
@validator.validate( params )
|
|
461
|
+
|
|
462
|
+
@validator.should_not be_okay()
|
|
463
|
+
@validator.should have_errors()
|
|
464
|
+
|
|
465
|
+
@validator[:int_constraint].should be_nil()
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
it "rejects integer values with other cruft in them for fields with integer constraints" do
|
|
469
|
+
params = {'required' => '1', 'int_constraint' => '88licks'}
|
|
470
|
+
|
|
471
|
+
@validator.validate( params )
|
|
472
|
+
|
|
473
|
+
@validator.should_not be_okay()
|
|
474
|
+
@validator.should have_errors()
|
|
475
|
+
|
|
476
|
+
@validator[:int_constraint].should be_nil()
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
ValidURIs = %w{
|
|
480
|
+
http://127.0.0.1
|
|
481
|
+
http://127.0.0.1/
|
|
482
|
+
http://[127.0.0.1]/
|
|
483
|
+
http://ruby-lang.org/
|
|
484
|
+
http://www.rocketboom.com/vlog/rb_08_feb_01
|
|
485
|
+
http://del.icio.us/search/?fr=del_icio_us&p=ruby+arrow&type=all
|
|
486
|
+
http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8080/index.html
|
|
487
|
+
http://[1080:0:0:0:8:800:200C:417A]/index.html
|
|
488
|
+
http://[3ffe:2a00:100:7031::1]
|
|
489
|
+
http://[1080::8:800:200C:417A]/foo
|
|
490
|
+
http://[::192.9.5.5]/ipng
|
|
491
|
+
http://[::FFFF:129.144.52.38]:3474/index.html
|
|
492
|
+
http://[2010:836B:4179::836B:4179]
|
|
493
|
+
|
|
494
|
+
https://mail.google.com/
|
|
495
|
+
https://127.0.0.1/
|
|
496
|
+
https://r4.com:8080/
|
|
497
|
+
|
|
498
|
+
ftp://ftp.ruby-lang.org/pub/ruby/1.0/ruby-0.49.tar.gz
|
|
499
|
+
ftp://crashoverride:god@gibson.ellingsonmineral.com/root/.workspace/.garbage.
|
|
500
|
+
|
|
501
|
+
ldap:/o=University%20of%20Michigan,c=US
|
|
502
|
+
ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
|
|
503
|
+
ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress
|
|
504
|
+
ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)
|
|
505
|
+
ldap://ldap.itd.umich.edu/c=GB?objectClass?one
|
|
506
|
+
ldap://ldap.question.com/o=Question%3f,c=US?mail
|
|
507
|
+
ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04)
|
|
508
|
+
ldap:/??sub??bindname=cn=Manager%2co=Foo
|
|
509
|
+
ldap:/??sub??!bindname=cn=Manager%2co=Foo
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
ValidURIs.each do |uri_string|
|
|
513
|
+
it "accepts #{uri_string} for fields with URI constraints" do
|
|
514
|
+
params = {'required' => '1', 'uri_constraint' => uri_string}
|
|
515
|
+
|
|
516
|
+
@validator.validate( params )
|
|
517
|
+
|
|
518
|
+
@validator.should be_okay()
|
|
519
|
+
@validator.should_not have_errors()
|
|
520
|
+
|
|
521
|
+
@validator[:uri_constraint].should be_a_kind_of( URI::Generic )
|
|
522
|
+
@validator[:uri_constraint].to_s.should == uri_string
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# :FIXME: I don't know LDAP uris very well, so I'm not sure how they're likely to
|
|
527
|
+
# be invalidly-occurring in the wild
|
|
528
|
+
InvalidURIs = %W{
|
|
529
|
+
glark:
|
|
530
|
+
|
|
531
|
+
http:
|
|
532
|
+
http://
|
|
533
|
+
http://_com/vlog/rb_08_feb_01
|
|
534
|
+
http://del.icio.us/search/\x20\x14\x18
|
|
535
|
+
http://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/index.html
|
|
536
|
+
http://1080:0:0:0:8:800:200C:417A/index.html
|
|
537
|
+
http://3ffe:2a00:100:7031::1
|
|
538
|
+
http://1080::8:800:200C:417A/foo
|
|
539
|
+
http://::192.9.5.5/ipng
|
|
540
|
+
http://::FFFF:129.144.52.38:80/index.html
|
|
541
|
+
http://2010:836B:4179::836B:4179
|
|
542
|
+
|
|
543
|
+
https:
|
|
544
|
+
https://user:pass@/
|
|
545
|
+
https://r4.com:nonnumericport/
|
|
546
|
+
|
|
547
|
+
ftp:
|
|
548
|
+
ftp:ruby-0.49.tar.gz
|
|
549
|
+
ftp://crashoverride:god@/root/.workspace/.garbage.
|
|
550
|
+
|
|
551
|
+
ldap:
|
|
552
|
+
ldap:/o=University\x20of\x20Michigan,c=US
|
|
553
|
+
ldap://ldap.itd.umich.edu/o=University+\x00of+Michigan
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
InvalidURIs.each do |uri_string|
|
|
557
|
+
it "rejects #{uri_string} for fields with URI constraints" do
|
|
558
|
+
params = {'required' => '1', 'uri_constraint' => uri_string}
|
|
559
|
+
|
|
560
|
+
# lambda {
|
|
561
|
+
@validator.validate( params )
|
|
562
|
+
# }.should_not raise_error()
|
|
563
|
+
|
|
564
|
+
@validator.should_not be_okay()
|
|
565
|
+
@validator.should have_errors()
|
|
566
|
+
|
|
567
|
+
@validator[:uri_constraint].should be_nil()
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
it "accepts simple RFC822 addresses for fields with email constraints" do
|
|
572
|
+
params = {'required' => '1', 'email_constraint' => 'jrandom@hacker.ie'}
|
|
573
|
+
|
|
574
|
+
@validator.validate( params )
|
|
575
|
+
|
|
576
|
+
@validator.should be_okay()
|
|
577
|
+
@validator.should_not have_errors()
|
|
578
|
+
|
|
579
|
+
@validator[:email_constraint].should == 'jrandom@hacker.ie'
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
it "accepts hyphenated domains in RFC822 addresses for fields with email constraints" do
|
|
583
|
+
params = {'required' => '1', 'email_constraint' => 'jrandom@just-another-hacquer.fr'}
|
|
584
|
+
|
|
585
|
+
@validator.validate( params )
|
|
586
|
+
|
|
587
|
+
@validator.should be_okay()
|
|
588
|
+
@validator.should_not have_errors()
|
|
589
|
+
|
|
590
|
+
@validator[:email_constraint].should == 'jrandom@just-another-hacquer.fr'
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
ComplexAddresses = [
|
|
594
|
+
'ruby+hacker@random-example.org',
|
|
595
|
+
'"ruby hacker"@ph8675309.org',
|
|
596
|
+
'jrandom@[ruby hacquer].com',
|
|
597
|
+
'abcdefghijklmnopqrstuvwxyz@abcdefghijklmnopqrstuvwxyz',
|
|
598
|
+
]
|
|
599
|
+
ComplexAddresses.each do |addy|
|
|
600
|
+
it "accepts #{addy} for fields with email constraints" do
|
|
601
|
+
params = {'required' => '1', 'email_constraint' => addy}
|
|
602
|
+
|
|
603
|
+
@validator.validate( params )
|
|
604
|
+
|
|
605
|
+
@validator.should be_okay()
|
|
606
|
+
@validator.should_not have_errors()
|
|
607
|
+
|
|
608
|
+
@validator[:email_constraint].should == addy
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
BogusAddresses = [
|
|
614
|
+
'jrandom@hacquer com',
|
|
615
|
+
'jrandom@ruby hacquer.com',
|
|
616
|
+
'j random@rubyhacquer.com',
|
|
617
|
+
'j random@ruby|hacquer.com',
|
|
618
|
+
'j:random@rubyhacquer.com',
|
|
619
|
+
]
|
|
620
|
+
BogusAddresses.each do |addy|
|
|
621
|
+
it "rejects #{addy} for fields with email constraints" do
|
|
622
|
+
params = {'required' => '1', 'email_constraint' => addy}
|
|
623
|
+
|
|
624
|
+
@validator.validate( params )
|
|
625
|
+
|
|
626
|
+
@validator.should_not be_okay()
|
|
627
|
+
@validator.should have_errors()
|
|
628
|
+
|
|
629
|
+
@validator[:email_constraint].should be_nil()
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it "accepts simple hosts for fields with host constraints" do
|
|
634
|
+
params = {'required' => '1', 'host_constraint' => 'deveiate.org'}
|
|
635
|
+
|
|
636
|
+
@validator.validate( params )
|
|
637
|
+
|
|
638
|
+
@validator.should be_okay()
|
|
639
|
+
@validator.should_not have_errors()
|
|
640
|
+
|
|
641
|
+
@validator[:host_constraint].should == 'deveiate.org'
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
it "accepts hyphenated hosts for fields with host constraints" do
|
|
645
|
+
params = {'required' => '1', 'host_constraint' => 'your-characters-can-fly.kr'}
|
|
646
|
+
|
|
647
|
+
@validator.validate( params )
|
|
648
|
+
|
|
649
|
+
@validator.should be_okay()
|
|
650
|
+
@validator.should_not have_errors()
|
|
651
|
+
|
|
652
|
+
@validator[:host_constraint].should == 'your-characters-can-fly.kr'
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
BogusHosts = [
|
|
656
|
+
'.',
|
|
657
|
+
'glah ',
|
|
658
|
+
'glah[lock]',
|
|
659
|
+
'glah.be$',
|
|
660
|
+
'indus«tree».com',
|
|
661
|
+
]
|
|
662
|
+
|
|
663
|
+
BogusHosts.each do |hostname|
|
|
664
|
+
it "rejects #{hostname} for fields with host constraints" do
|
|
665
|
+
params = {'required' => '1', 'host_constraint' => hostname}
|
|
666
|
+
|
|
667
|
+
@validator.validate( params )
|
|
668
|
+
|
|
669
|
+
@validator.should_not be_okay()
|
|
670
|
+
@validator.should have_errors()
|
|
671
|
+
|
|
672
|
+
@validator[:host_constraint].should be_nil()
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
it "accepts alpha characters for fields with alpha constraints" do
|
|
677
|
+
params = {'required' => '1', 'alpha_constraint' => 'abelincoln'}
|
|
678
|
+
|
|
679
|
+
@validator.validate( params )
|
|
680
|
+
|
|
681
|
+
@validator.should be_okay()
|
|
682
|
+
@validator.should_not have_errors()
|
|
683
|
+
|
|
684
|
+
@validator[:alpha_constraint].should == 'abelincoln'
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
it "rejects non-alpha characters for fields with alpha constraints" do
|
|
688
|
+
params = {'required' => '1', 'alpha_constraint' => 'duck45'}
|
|
689
|
+
|
|
690
|
+
@validator.validate( params )
|
|
691
|
+
|
|
692
|
+
@validator.should_not be_okay()
|
|
693
|
+
@validator.should have_errors()
|
|
694
|
+
|
|
695
|
+
@validator[:alpha_constraint].should be_nil()
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
### 'alphanumeric'
|
|
699
|
+
it "accepts alphanumeric characters for fields with alphanumeric constraints" do
|
|
700
|
+
params = {'required' => '1', 'alphanumeric_constraint' => 'zombieabe11'}
|
|
701
|
+
|
|
702
|
+
@validator.validate( params )
|
|
703
|
+
|
|
704
|
+
@validator.should be_okay()
|
|
705
|
+
@validator.should_not have_errors()
|
|
706
|
+
|
|
707
|
+
@validator[:alphanumeric_constraint].should == 'zombieabe11'
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
it "rejects non-alphanumeric characters for fields with alphanumeric constraints" do
|
|
711
|
+
params = {'required' => '1', 'alphanumeric_constraint' => 'duck!ling'}
|
|
712
|
+
|
|
713
|
+
@validator.validate( params )
|
|
714
|
+
|
|
715
|
+
@validator.should_not be_okay()
|
|
716
|
+
@validator.should have_errors()
|
|
717
|
+
|
|
718
|
+
@validator[:alphanumeric_constraint].should be_nil()
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
### 'printable'
|
|
722
|
+
it "accepts printable characters for fields with 'printable' constraints" do
|
|
723
|
+
test_content = <<-EOF
|
|
724
|
+
I saw you with some kind of medical apparatus strapped to your
|
|
725
|
+
spine. It was all glass and metal, a great crystaline hypodermic
|
|
726
|
+
spider, carrying you into the aether with a humming, crackling sound.
|
|
727
|
+
EOF
|
|
728
|
+
|
|
729
|
+
params = {
|
|
730
|
+
'required' => '1',
|
|
731
|
+
'printable_constraint' => test_content
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
@validator.validate( params )
|
|
735
|
+
|
|
736
|
+
@validator.should be_okay()
|
|
737
|
+
@validator.should_not have_errors()
|
|
738
|
+
|
|
739
|
+
@validator[:printable_constraint].should == test_content
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
it "rejects non-printable characters for fields with 'printable' constraints" do
|
|
743
|
+
params = {'required' => '1', 'printable_constraint' => %{\0Something cold\0}}
|
|
744
|
+
|
|
745
|
+
@validator.validate( params )
|
|
746
|
+
|
|
747
|
+
@validator.should_not be_okay()
|
|
748
|
+
@validator.should have_errors()
|
|
749
|
+
|
|
750
|
+
@validator[:printable_constraint].should be_nil()
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
it "accepts parameters for fields with Proc constraints if the Proc " +
|
|
755
|
+
"returns a true value" do
|
|
756
|
+
test_date = '2007-07-17'
|
|
757
|
+
params = {'required' => '1', 'proc_constraint' => test_date}
|
|
758
|
+
|
|
759
|
+
@validator.validate( params )
|
|
760
|
+
|
|
761
|
+
@validator.should be_okay()
|
|
762
|
+
@validator.should_not have_errors()
|
|
763
|
+
|
|
764
|
+
@validator[:proc_constraint].should == Date.parse( test_date )
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
it "rejects parameters for fields with Proc constraints if the Proc " +
|
|
768
|
+
"returns a false value" do
|
|
769
|
+
|
|
770
|
+
params = {'required' => '1', 'proc_constraint' => %{::::}}
|
|
771
|
+
|
|
772
|
+
@validator.validate( params )
|
|
773
|
+
|
|
774
|
+
@validator.should_not be_okay()
|
|
775
|
+
@validator.should have_errors()
|
|
776
|
+
|
|
777
|
+
@validator[:proc_constraint].should be_nil()
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
end
|
|
781
|
+
|