strelka 0.0.1pre4 → 0.0.1.pre129

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/History.rdoc +1 -1
  2. data/IDEAS.rdoc +62 -0
  3. data/Manifest.txt +38 -7
  4. data/README.rdoc +124 -5
  5. data/Rakefile +22 -6
  6. data/bin/leash +102 -157
  7. data/contrib/hoetemplate/.autotest.erb +23 -0
  8. data/contrib/hoetemplate/History.rdoc.erb +4 -0
  9. data/contrib/hoetemplate/Manifest.txt.erb +8 -0
  10. data/contrib/hoetemplate/README.rdoc.erb +17 -0
  11. data/contrib/hoetemplate/Rakefile.erb +24 -0
  12. data/contrib/hoetemplate/data/file_name/apps/file_name_app +36 -0
  13. data/contrib/hoetemplate/data/file_name/templates/layout.tmpl.erb +13 -0
  14. data/contrib/hoetemplate/data/file_name/templates/top.tmpl.erb +8 -0
  15. data/contrib/hoetemplate/lib/file_name.rb.erb +18 -0
  16. data/contrib/hoetemplate/spec/file_name_spec.rb.erb +21 -0
  17. data/data/strelka/apps/hello-world +30 -0
  18. data/lib/strelka/app/defaultrouter.rb +49 -30
  19. data/lib/strelka/app/errors.rb +121 -0
  20. data/lib/strelka/app/exclusiverouter.rb +40 -0
  21. data/lib/strelka/app/filters.rb +18 -7
  22. data/lib/strelka/app/negotiation.rb +122 -0
  23. data/lib/strelka/app/parameters.rb +171 -14
  24. data/lib/strelka/app/paramvalidator.rb +751 -0
  25. data/lib/strelka/app/plugins.rb +66 -46
  26. data/lib/strelka/app/restresources.rb +499 -0
  27. data/lib/strelka/app/router.rb +73 -0
  28. data/lib/strelka/app/routing.rb +140 -18
  29. data/lib/strelka/app/templating.rb +12 -3
  30. data/lib/strelka/app.rb +174 -24
  31. data/lib/strelka/constants.rb +0 -20
  32. data/lib/strelka/exceptions.rb +29 -0
  33. data/lib/strelka/httprequest/acceptparams.rb +377 -0
  34. data/lib/strelka/httprequest/negotiation.rb +257 -0
  35. data/lib/strelka/httprequest.rb +155 -7
  36. data/lib/strelka/httpresponse/negotiation.rb +579 -0
  37. data/lib/strelka/httpresponse.rb +140 -0
  38. data/lib/strelka/logging.rb +4 -1
  39. data/lib/strelka/mixins.rb +53 -0
  40. data/lib/strelka.rb +22 -1
  41. data/spec/data/error.tmpl +1 -0
  42. data/spec/lib/constants.rb +0 -1
  43. data/spec/lib/helpers.rb +21 -0
  44. data/spec/strelka/app/defaultrouter_spec.rb +41 -35
  45. data/spec/strelka/app/errors_spec.rb +212 -0
  46. data/spec/strelka/app/exclusiverouter_spec.rb +220 -0
  47. data/spec/strelka/app/filters_spec.rb +196 -0
  48. data/spec/strelka/app/negotiation_spec.rb +73 -0
  49. data/spec/strelka/app/parameters_spec.rb +149 -0
  50. data/spec/strelka/app/paramvalidator_spec.rb +1059 -0
  51. data/spec/strelka/app/plugins_spec.rb +26 -19
  52. data/spec/strelka/app/restresources_spec.rb +393 -0
  53. data/spec/strelka/app/router_spec.rb +63 -0
  54. data/spec/strelka/app/routing_spec.rb +183 -9
  55. data/spec/strelka/app/templating_spec.rb +1 -2
  56. data/spec/strelka/app_spec.rb +265 -32
  57. data/spec/strelka/exceptions_spec.rb +53 -0
  58. data/spec/strelka/httprequest/acceptparams_spec.rb +282 -0
  59. data/spec/strelka/httprequest/negotiation_spec.rb +246 -0
  60. data/spec/strelka/httprequest_spec.rb +204 -14
  61. data/spec/strelka/httpresponse/negotiation_spec.rb +464 -0
  62. data/spec/strelka/httpresponse_spec.rb +114 -0
  63. data/spec/strelka/mixins_spec.rb +99 -0
  64. data.tar.gz.sig +1 -0
  65. metadata +175 -79
  66. metadata.gz.sig +2 -0
  67. data/IDEAS.textile +0 -174
  68. data/data/strelka/apps/strelka-admin +0 -65
  69. data/data/strelka/apps/strelka-setup +0 -26
  70. data/data/strelka/bootstrap-config.rb +0 -34
  71. data/data/strelka/templates/admin/console.tmpl +0 -21
  72. data/data/strelka/templates/layout.tmpl +0 -30
  73. data/lib/strelka/process.rb +0 -19
@@ -7,6 +7,7 @@ BEGIN {
7
7
  $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
8
8
  }
9
9
 
10
+ require 'uri'
10
11
  require 'rspec'
11
12
  require 'spec/lib/helpers'
12
13
  require 'strelka/httprequest'
@@ -27,27 +28,216 @@ describe Strelka::HTTPRequest do
27
28
  reset_logging()
28
29
  end
29
30
 
31
+ context "instance" do
30
32
 
31
- before( :each ) do
32
- @req = @request_factory.get( '/directory/userinfo/ged' )
33
- end
33
+ before( :each ) do
34
+ @req = @request_factory.get( '/directory/userinfo/ged' )
35
+ end
34
36
 
35
- it "knows what the request's parsed URI is" do
36
- @req.uri.should be_a( URI )
37
- @req.uri.path.should == '/directory/userinfo/ged'
38
- @req.uri.query.should be_nil()
39
- end
37
+ it "knows what the request's parsed URI is" do
38
+ @req.uri.should be_a( URI )
39
+ @req.uri.to_s.should == 'http://localhost:8080/directory/userinfo/ged'
40
+ end
41
+
42
+ it "knows what Mongrel2 route it followed" do
43
+ @req.pattern.should == '/directory'
44
+ end
45
+
46
+ it "knows what the URI of the route handling the request is" do
47
+ @req.base_uri.should be_a( URI )
48
+ @req.base_uri.to_s.should == 'http://localhost:8080/directory'
49
+ end
50
+
51
+ it "knows what the path of the request past its route is" do
52
+ @req.app_path.should == '/userinfo/ged'
53
+ end
54
+
55
+ it "knows what HTTP verb the request used" do
56
+ @req.verb.should == :GET
57
+ end
58
+
59
+ it "can get and set notes for communication between plugins" do
60
+ @req.notes.should be_a( Hash )
61
+ @req.notes[:routing].should be_a( Hash )
62
+ @req.notes[:routing][:route].should be_a( Hash )
63
+ end
40
64
 
41
- it "knows what Mongrel2 route it followed" do
42
- @req.pattern.should == '/directory'
43
65
  end
44
66
 
45
- it "knows what the path of the request past its route is" do
46
- @req.app_path.should == '/userinfo/ged'
67
+
68
+ context "instance with a query string" do
69
+
70
+ before( :each ) do
71
+ @req = @request_factory.get( '/directory/userinfo/ged?limit=10;offset=20' )
72
+ end
73
+
74
+ it "knows what the request's parsed URI is" do
75
+ @req.uri.should be_a( URI )
76
+ @req.uri.to_s.should == 'http://localhost:8080/directory/userinfo/ged?limit=10;offset=20'
77
+ end
78
+
79
+ it "knows what Mongrel2 route it followed" do
80
+ @req.pattern.should == '/directory'
81
+ end
82
+
83
+ it "knows what the URI of the route handling the request is" do
84
+ @req.base_uri.should be_a( URI )
85
+ @req.base_uri.to_s.should == 'http://localhost:8080/directory'
86
+ end
87
+
88
+ it "knows what the path of the request past its route is" do
89
+ @req.app_path.should == '/userinfo/ged'
90
+ @req.app_path.should == '/userinfo/ged' # make sure the slice is non-destructive
91
+ end
92
+
93
+ it "knows what HTTP verb the request used" do
94
+ @req.verb.should == :GET
95
+ end
96
+
47
97
  end
48
98
 
49
- it "knows what HTTP verb the request used" do
50
- @req.verb.should == :GET
99
+
100
+ describe "request-parameter parsing" do
101
+
102
+ context "a GET request" do
103
+ it "has an empty params Hash if the request doesn't have a query string " do
104
+ req = @request_factory.get( '/directory/path' )
105
+ req.params.should == {}
106
+ end
107
+
108
+ it "has a params Hash with the key/value pair in it if the query string has " +
109
+ "one key/value pair" do
110
+ req = @request_factory.get( '/directory/path?foo=bar' )
111
+ req.params.should == {'foo' => 'bar'}
112
+ end
113
+
114
+ it "has a params Hash with the key/value pairs in it if the query string has " +
115
+ "two pairs seperated with a an ampersand" do
116
+ req = @request_factory.get( '/directory/path?foo=bar&chunky=pork' )
117
+ req.params.should == {
118
+ 'foo' => 'bar',
119
+ 'chunky' => 'pork',
120
+ }
121
+ end
122
+
123
+ it "has a params Hash with the key/value pairs in it if the query string has " +
124
+ "two pairs with a semi-colon separator" do
125
+ req = @request_factory.get( '/directory/path?potato=gun;legume=bazooka' )
126
+ req.params.should == {
127
+ 'potato' => 'gun',
128
+ 'legume' => 'bazooka',
129
+ }
130
+ end
131
+
132
+ it "has a params Hash with an Array of values if the query string has two values " +
133
+ "for the same key" do
134
+ req = @request_factory.get( '/directory/path?foo=bar&foo=baz' )
135
+ req.params.should == {
136
+ 'foo' => ['bar', 'baz'],
137
+ }
138
+ end
139
+
140
+ it "has a params Hash with one Array of values and a scalar value if the query " +
141
+ "string has three values and two keys" do
142
+ req = @request_factory.get( '/directory/path?foo=bar&foo=pork;mirror=sequel' )
143
+ req.params.should == {
144
+ 'foo' => ['bar', 'pork'],
145
+ 'mirror' => 'sequel',
146
+ }
147
+ end
148
+ end
149
+
150
+ context "a POST request with a 'application/x-www-form-urlencoded' body" do
151
+
152
+ before( :each ) do
153
+ @req = @request_factory.post( '/directory/path', '',
154
+ 'Content-type' => 'application/x-www-form-urlencoded' )
155
+ end
156
+
157
+ it "returns an empty Hash for an empty body" do
158
+ @req.body = ''
159
+ @req.params.should == {}
160
+ end
161
+
162
+ it "has a params Hash with the key/value pair in it if the form data has " +
163
+ "one key/value pair" do
164
+ @req.body = 'foo=bar'
165
+ @req.params.should == {'foo' => 'bar'}
166
+ end
167
+
168
+ it "has a params Hash with the key/value pairs in it if the form data has " +
169
+ "two pairs seperated with a an ampersand" do
170
+ @req.body = 'foo=bar&chunky=pork'
171
+ @req.params.should == {
172
+ 'foo' => 'bar',
173
+ 'chunky' => 'pork',
174
+ }
175
+ end
176
+
177
+ it "has a params Hash with the key/value pairs in it if the form data has " +
178
+ "two pairs with a semi-colon separator" do
179
+ @req.body = 'potato=gun;legume=bazooka'
180
+ @req.params.should == {
181
+ 'potato' => 'gun',
182
+ 'legume' => 'bazooka',
183
+ }
184
+ end
185
+
186
+ it "has a params Hash with an Array of values if the form data has two values " +
187
+ "for the same key" do
188
+ @req.body = 'foo=bar&foo=baz'
189
+ @req.params.should == { 'foo' => ['bar', 'baz'] }
190
+ end
191
+
192
+ it "has a params Hash with one Array of values and a scalar value if the form " +
193
+ "data has three values and two keys" do
194
+ @req.body = 'foo=bar&foo=pork;mirror=sequel'
195
+ @req.params.should == {
196
+ 'foo' => ['bar', 'pork'],
197
+ 'mirror' => 'sequel',
198
+ }
199
+ end
200
+ end
201
+
202
+ context "a POST request with a 'multipart/form-data' body" do
203
+
204
+ before( :each ) do
205
+ @req = @request_factory.post( '/directory/path', '',
206
+ 'Content-type' => 'multipart/form-data' )
207
+ end
208
+
209
+ it "returns nil for an empty body" do
210
+ pending "multipart/form-data support" do
211
+ @req.body = ''
212
+ @req.params.should be_nil()
213
+ end
214
+ end
215
+
216
+ end
217
+
218
+
219
+ context "a POST request with a 'application/json' body" do
220
+ before( :each ) do
221
+ @req = @request_factory.post( '/directory/path', '',
222
+ 'Content-type' => 'application/json' )
223
+ end
224
+
225
+ it "returns nil for an empty body" do
226
+ @req.body = ''
227
+ @req.params.should be_nil()
228
+ end
229
+
230
+ it "has the JSON data as the params if it has a body with JSON object in it" do
231
+ data = {
232
+ 'animal' => 'ducky',
233
+ 'adjectives' => ['fluffy', 'puddle-ey'],
234
+ }
235
+ @req.body = Yajl.dump( data )
236
+ @req.params.should == data
237
+ end
238
+
239
+ end
240
+
51
241
  end
52
242
 
53
243
  end
@@ -0,0 +1,464 @@
1
+ #!/usr/bin/env ruby
2
+ #encoding: utf-8
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
7
+
8
+ libdir = basedir + "lib"
9
+
10
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
11
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
12
+ }
13
+
14
+ require 'rspec'
15
+
16
+ require 'spec/lib/helpers'
17
+
18
+ require 'strelka'
19
+ require 'strelka/httprequest/negotiation'
20
+ require 'strelka/httpresponse/negotiation'
21
+
22
+
23
+ #####################################################################
24
+ ### C O N T E X T S
25
+ #####################################################################
26
+
27
+ describe Strelka::HTTPResponse::Negotiation do
28
+
29
+ before( :all ) do
30
+ setup_logging( :fatal )
31
+ @request_factory = Mongrel2::RequestFactory.new( route: '/service/user' )
32
+ end
33
+
34
+ after( :all ) do
35
+ reset_logging()
36
+ end
37
+
38
+
39
+ before( :each ) do
40
+ @app = Class.new( Strelka::App ) { plugins :negotiation }
41
+ @req = @request_factory.get( '/service/user/estark' )
42
+ @res = @req.response
43
+ end
44
+
45
+
46
+ describe "content-alternative callback methods" do
47
+
48
+ it "can provide blocks for bodies of several different mediatypes" do
49
+ @req.headers.accept = 'application/x-yaml, application/json; q=0.7, text/xml; q=0.2'
50
+
51
+ @res.for( 'application/json' ) { %{["a JSON dump"]} }
52
+ @res.for( 'application/x-yaml' ) { "---\na: YAML dump\n\n" }
53
+
54
+ @res.negotiated_body.should == "---\na: YAML dump\n\n"
55
+ @res.content_type.should == "application/x-yaml"
56
+ @res.header_data.should =~ /accept(?!-)/i
57
+ end
58
+
59
+ it "can provide a single block for bodies of several different mediatypes" do
60
+ @req.headers.accept = 'application/x-yaml; q=0.7, application/json; q=0.9'
61
+
62
+ @res.for( 'application/json', 'application/x-yaml' ) do
63
+ { uuid: 'fc85e35b-c9c3-4675-a882-25bf98d11e1b', name: "Harlot's Garden" }
64
+ end
65
+
66
+ @res.negotiated_body.should == "{\"uuid\":\"fc85e35b-c9c3-4675-a882-25bf98d11e1b\"," +
67
+ "\"name\":\"Harlot's Garden\"}"
68
+ @res.content_type.should == "application/json"
69
+ @res.header_data.should =~ /accept(?!-)/i
70
+ end
71
+
72
+ it "can provide a block for bodies of several different symbolic mediatypes" do
73
+ @req.headers.accept = 'application/x-yaml; q=0.7, application/json; q=0.9'
74
+
75
+ @res.for( :json, :yaml ) do
76
+ { uuid: 'fc85e35b-c9c3-4675-a882-25bf98d11e1b', name: "Harlot's Garden" }
77
+ end
78
+
79
+ @res.negotiated_body.should == "{\"uuid\":\"fc85e35b-c9c3-4675-a882-25bf98d11e1b\"," +
80
+ "\"name\":\"Harlot's Garden\"}"
81
+ @res.content_type.should == "application/json"
82
+ @res.header_data.should =~ /accept(?!-)/i
83
+ end
84
+
85
+ end
86
+
87
+
88
+ describe "automatic content transcoding" do
89
+
90
+ it "transcodes String entity bodies if the charset is not acceptable" do
91
+ @req.headers.accept_charset = 'koi8-r, koi8-u;q=0.9, utf-8;q=0.8'
92
+
93
+ @res.body = File.read( __FILE__, encoding: 'iso-8859-5' )
94
+ @res.content_type = 'text/plain'
95
+
96
+ @res.negotiated_body.encoding.should == Encoding::KOI8_R
97
+ @res.header_data.should =~ /accept-charset(?!-)/i
98
+ end
99
+
100
+ it "transcodes String entity bodies if the charset is not acceptable" do
101
+ @req.headers.accept_charset = 'utf-8'
102
+
103
+ @res.body = File.read( __FILE__, encoding: 'iso-8859-5' )
104
+ @res.content_type = 'application/json'
105
+
106
+ @res.negotiated_body.encoding.should == Encoding::UTF_8
107
+ @res.header_data.should =~ /accept-charset(?!-)/i
108
+ end
109
+
110
+ it "transcodes File entity bodies if the charset is not acceptable" do
111
+ pending "implementation of IO transcoding" do
112
+ @req.headers.accept_charset = 'koi8-r, koi8-u;q=0.9, utf-8;q=0.8'
113
+
114
+ @res.body = File.open( __FILE__, 'r:iso-8859-5' )
115
+ @res.content_type = 'text/plain'
116
+
117
+ @res.negotiated_body.encoding.should == Encoding::KOI8_R
118
+ @res.header_data.should =~ /accept-charset(?!-)/i
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+
125
+ describe "language alternative callback methods" do
126
+
127
+ it "can provide blocks for alternative bodies of several different languages" do
128
+ @req.headers.accept = 'text/plain'
129
+ @req.headers.accept_language = 'de'
130
+
131
+ @res.puts( "the English body" )
132
+ @res.languages << :en
133
+ @res.content_type = 'text/plain'
134
+ @res.for_language( :de ) { "German translation" }
135
+ @res.for_language( :sl ) { "Slovenian translation" }
136
+
137
+ @res.negotiated_body.should == "German translation"
138
+ @res.languages.should == ["de"]
139
+ @res.header_data.should =~ /accept-language/i
140
+ end
141
+
142
+ it "can provide blocks for bodies of several different languages without setting a " +
143
+ "default entity body" do
144
+ @req.headers.accept = 'text/plain'
145
+ @req.headers.accept_language = 'de, en-gb;q=0.9, en;q=0.7'
146
+
147
+ @res.content_type = 'text/plain'
148
+ @res.for_language( :en ) { "English translation" }
149
+ @res.for_language( :de ) { "German translation" }
150
+ @res.for_language( :sl ) { "Slovenian translation" }
151
+
152
+ @res.negotiated_body.should == "German translation"
153
+ @res.languages.should == ["de"]
154
+ @res.header_data.should =~ /accept-language/i
155
+ end
156
+
157
+ it "can provide a single block for bodies of several different languages" do
158
+ @req.headers.accept_language = 'fr;q=0.9, de;q=0.7, en;q=0.7, pt'
159
+ translations = {
160
+ :pt => "Portuguese translation",
161
+ :fr => "French translation",
162
+ :de => "German translation",
163
+ :en => "English translation",
164
+ }
165
+
166
+ @res.content_type = 'text/plain'
167
+ @res.for_language( translations.keys ) do |lang|
168
+ translations[ lang.to_sym ]
169
+ end
170
+
171
+ @res.negotiated_body.should == "Portuguese translation"
172
+ @res.languages.should == ["pt"]
173
+ @res.header_data.should =~ /accept-language/i
174
+ end
175
+
176
+ it "calls the first block for requests with no accept-language header" do
177
+ @req.headers.delete( :accept_language )
178
+ @req.headers.accept = 'text/plain'
179
+
180
+ @res.content_type = 'text/plain'
181
+ @res.for_language( :en ) { "English translation" }
182
+ @res.for_language( :de ) { "German translation" }
183
+ @res.for_language( :sl ) { "Slovenian translation" }
184
+
185
+ @res.negotiated_body.should == "English translation"
186
+ @res.languages.should == ["en"]
187
+ @res.header_data.should =~ /accept-language/i
188
+ end
189
+ end
190
+
191
+
192
+ describe "content coding alternative callback methods" do
193
+
194
+ it "can provide blocks for content coding" do
195
+ @req.headers.accept = 'text/plain'
196
+ @req.headers.accept_encoding = 'gzip'
197
+
198
+ @res << "the text body"
199
+ @res.content_type = 'text/plain'
200
+ @res.for_encoding( :deflate ) { @res.body + " (deflated)" }
201
+ @res.for_encoding( :gzip ) { @res.body + " (gzipped)" }
202
+
203
+ @res.negotiated_body.should == "the text body (gzipped)"
204
+ @res.encodings.should include( "gzip" )
205
+ @res.header_data.should =~ /accept-encoding/i
206
+ @res.header_data.should_not =~ /identity/i
207
+ end
208
+
209
+ it "chooses the content coding with the highest qvalue" do
210
+ @req.headers.accept = 'text/plain'
211
+ @req.headers.accept_encoding = 'gzip;q=0.7, deflate'
212
+
213
+ @res << "the text body"
214
+ @res.content_type = 'text/plain'
215
+ @res.for_encoding( :deflate ) { @res.body + " (deflated)" }
216
+ @res.for_encoding( :gzip ) { @res.body + " (gzipped)" }
217
+
218
+ @res.negotiated_body.should == "the text body (deflated)"
219
+ @res.encodings.should include( "deflate" )
220
+ @res.header_data.should =~ /accept-encoding/i
221
+ @res.header_data.should_not =~ /identity/i
222
+ end
223
+
224
+ end
225
+
226
+
227
+ describe "content-type acceptance predicates" do
228
+
229
+ it "knows that it is acceptable if its content_type is in the list of accepted types " +
230
+ "in its request" do
231
+ @req.headers.accept = 'application/x-yaml, application/json; q=0.7'
232
+ @res.content_type = 'application/json'
233
+
234
+ @res.should have_acceptable_content_type()
235
+ end
236
+
237
+ it "knows that it is acceptable if its request doesn't have accepted types" do
238
+ @req.headers.delete( :accept )
239
+ @res.content_type = 'application/x-ruby-marshalled'
240
+
241
+ @res.should have_acceptable_content_type()
242
+ end
243
+
244
+ it "knows that it is acceptable if it doesn't have an originating request" do
245
+ res = Strelka::HTTPResponse.new( 'appid', 88 )
246
+ res.extend( Strelka::HTTPResponse::Negotiation )
247
+ res.content_type = 'application/x-ruby-marshalled'
248
+
249
+ res.should have_acceptable_content_type()
250
+ end
251
+
252
+ it "knows that it is not acceptable if its content_type isn't in the list of " +
253
+ "accepted types in its request" do
254
+ @req.headers.accept = 'application/x-yaml, application/json; q=0.7'
255
+ @res.content_type = 'application/x-ruby-marshalled'
256
+
257
+ @res.should_not have_acceptable_content_type()
258
+ end
259
+
260
+ end
261
+
262
+
263
+ describe "charset acceptance predicates" do
264
+
265
+ it "knows that it is acceptable if its explicit charset is in the list of accepted " +
266
+ "charsets in its request" do
267
+ @req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
268
+ @res.charset = 'iso-8859-5'
269
+
270
+ @res.should have_acceptable_charset()
271
+ end
272
+
273
+ it "knows that it is acceptable if its request doesn't have accepted types" do
274
+ @req.headers.delete( :accept_charset )
275
+ @res.charset = 'koi8-u'
276
+ @res.should have_acceptable_charset()
277
+ end
278
+
279
+ it "knows that it is acceptable if it doesn't have an originating request" do
280
+ res = Strelka::HTTPResponse.new( 'appid', 88 )
281
+ res.charset = 'iso8859-15'
282
+
283
+ res.should have_acceptable_charset()
284
+ end
285
+
286
+ it "knows that it is acceptable if its explicit charset is set to ascii-8bit" do
287
+ @req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
288
+ @res.content_type = 'image/jpeg'
289
+ @res.charset = Encoding::ASCII_8BIT
290
+
291
+ @res.should have_acceptable_charset()
292
+ end
293
+
294
+ it "knows that it is acceptable if no charset can be derived, but the list of " +
295
+ "acceptable charsets includes ISO8859-1" do
296
+ @req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
297
+ @res.content_type = 'text/plain'
298
+ @res.body = "some stuff".force_encoding( Encoding::ASCII_8BIT )
299
+
300
+ @res.should have_acceptable_charset()
301
+ end
302
+
303
+ it "knows that it is not acceptable if no charset can be derived, the content is a " +
304
+ "text subtype, and the list of acceptable charsets doesn't include ISO8859-1" do
305
+ @req.headers.accept_charset = 'iso-8859-15, utf-8;q=0.8'
306
+ @res.content_type = 'text/plain'
307
+ @res.body = "some stuff".force_encoding( Encoding::ASCII_8BIT )
308
+
309
+ @res.should_not have_acceptable_charset()
310
+ end
311
+
312
+ it "knows that it is not acceptable if its explicit charset isn't in the list of " +
313
+ "accepted charsets in its request" do
314
+ @req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
315
+ @res.charset = 'sjis'
316
+
317
+ @res.should_not have_acceptable_charset()
318
+ end
319
+
320
+ it "knows that it is not acceptable if the charset in its content-type header isn't in " +
321
+ "the list of accepted charsets in its request" do
322
+ @req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
323
+ @res.content_type = 'text/plain; charset=sjis'
324
+
325
+ @res.should_not have_acceptable_charset()
326
+ end
327
+
328
+ it "knows that it is not acceptable if the charset derived from its entity body isn't in " +
329
+ "the list of accepted charsets in its request" do
330
+ @req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
331
+ @res.content_type = 'text/plain'
332
+ @res.body = File.open( __FILE__, 'r:iso8859-5' )
333
+
334
+ @res.should_not have_acceptable_charset()
335
+ end
336
+
337
+ end
338
+
339
+
340
+ describe "language acceptance predicates" do
341
+
342
+ it "knows that it is acceptable if it has a single language that's in the list of " +
343
+ "languages accepted by its originating request" do
344
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
345
+ @res.languages << 'ja'
346
+
347
+ @res.should have_acceptable_language()
348
+ end
349
+
350
+ it "knows that it is acceptable if all of its multiple languages are in the list of " +
351
+ "languages accepted by its originating request" do
352
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
353
+ @res.languages << 'ja' << 'en-us'
354
+
355
+ @res.should have_acceptable_language()
356
+ end
357
+
358
+ # I'm not sure if this is what RFC1616 means. It might be that *all* of its languages
359
+ # have to be in the accept-language: list.
360
+ it "knows that it is acceptable if one of its multiple languages is in the " +
361
+ "list of languages accepted by its originating request" do
362
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
363
+ @res.languages << 'pt' << 'en'
364
+
365
+ @res.should have_acceptable_language()
366
+ end
367
+
368
+ it "knows that it is acceptable if it has a body but doesn't have a language set" do
369
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
370
+ @res.languages.clear
371
+ @res.puts( "Some content in an unspecified language." )
372
+
373
+ @res.should have_acceptable_language()
374
+ end
375
+
376
+ it "knows that it is acceptable if it has no body yet" do
377
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
378
+ @res.languages.clear
379
+
380
+ @res.should have_acceptable_language()
381
+ end
382
+
383
+ it "knows that it is acceptable if it doesn't have an originating request" do
384
+ res = Strelka::HTTPResponse.new( 'appid', 88 )
385
+ res.extend( Strelka::HTTPResponse::Negotiation )
386
+ res.languages << 'kh'
387
+
388
+ res.should have_acceptable_language()
389
+ end
390
+
391
+ it "knows that it is not acceptable if it has a single language that isn't in the " +
392
+ "list of languages accepted by its originating request" do
393
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
394
+ @res.languages << 'pt'
395
+
396
+ @res.should_not have_acceptable_language()
397
+ end
398
+
399
+ it "knows that it is not acceptable if it has multiple languages, none of which are " +
400
+ "in the list of languages accepted by its originating request" do
401
+ @req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
402
+ @res.languages << 'pt-br' << 'fr-ca'
403
+
404
+ @res.should_not have_acceptable_language()
405
+ end
406
+
407
+ end
408
+
409
+
410
+ describe "encoding acceptance predicates" do
411
+
412
+ it "knows that it is acceptable if its content coding is in the list of accepted " +
413
+ "codings in its originating request" do
414
+ @req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0.5, *;q=0'
415
+ @res.encodings << 'gzip'
416
+
417
+ @res.should have_acceptable_encoding()
418
+ end
419
+
420
+ it "knows that it is acceptable if all of its content codings are in the list of accepted " +
421
+ "codings in its originating request" do
422
+ @req.headers.accept_encoding = 'gzip;q=1.0, frobnify;q=0.9, identity; q=0.5, *;q=0'
423
+ @res.encodings << 'gzip' << 'frobnify'
424
+
425
+ @res.should have_acceptable_encoding()
426
+ end
427
+
428
+ it "knows that it is not acceptable if one of its content codings is not in the list " +
429
+ "of accepted codings in its originating request" do
430
+ @req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0.5, *;q=0'
431
+ @res.encodings << 'gzip' << 'frobnify'
432
+
433
+ @res.should_not have_acceptable_encoding()
434
+ end
435
+
436
+ it "knows that it is not acceptable if it doesn't have any explicit content codings " +
437
+ "and 'identity' is explicitly not accepted in its originating request" do
438
+ @req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0'
439
+ @res.encodings.clear
440
+
441
+ @res.should_not have_acceptable_encoding()
442
+ end
443
+
444
+ it "knows that it is not acceptable if it doesn't have any explicit content codings " +
445
+ "and 'identity' is explicitly not accepted in its originating request" do
446
+ @req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0'
447
+ @res.encodings.clear
448
+
449
+ @res.should_not have_acceptable_encoding()
450
+ end
451
+
452
+ it "knows that it is not acceptable if it doesn't have any explicit content codings, " +
453
+ "the wildcard content-coding is disallowed, and 'identity' is not explicitly accepted" do
454
+ @req.headers.accept_encoding = 'gzip;q=1.0, *;q=0'
455
+ @res.encodings.clear
456
+
457
+ @res.should_not have_acceptable_encoding()
458
+ end
459
+
460
+ end
461
+
462
+
463
+ end
464
+