strelka 0.0.1pre4 → 0.0.1.pre129

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