runo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +120 -0
  3. data/bin/runo +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +141 -0
  7. data/lib/_parser.rb +243 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +213 -0
  10. data/lib/_storage/file.rb +200 -0
  11. data/lib/_storage/sequel.rb +174 -0
  12. data/lib/_storage/temp.rb +73 -0
  13. data/lib/_widget/action_create.rb +23 -0
  14. data/lib/_widget/action_login.rb +22 -0
  15. data/lib/_widget/action_signup.rb +16 -0
  16. data/lib/_widget/action_update.rb +16 -0
  17. data/lib/_widget/crumb.rb +24 -0
  18. data/lib/_widget/done.rb +16 -0
  19. data/lib/_widget/login.rb +25 -0
  20. data/lib/_widget/me.rb +31 -0
  21. data/lib/_widget/message.rb +51 -0
  22. data/lib/_widget/navi.rb +88 -0
  23. data/lib/_widget/submit.rb +49 -0
  24. data/lib/_widget/view_ym.rb +77 -0
  25. data/lib/_workflow/_workflow.rb +89 -0
  26. data/lib/_workflow/attachment.rb +50 -0
  27. data/lib/_workflow/blog.rb +28 -0
  28. data/lib/_workflow/contact.rb +23 -0
  29. data/lib/_workflow/forum.rb +26 -0
  30. data/lib/_workflow/register.rb +39 -0
  31. data/lib/meta/_meta.rb +20 -0
  32. data/lib/meta/group.rb +19 -0
  33. data/lib/meta/id.rb +59 -0
  34. data/lib/meta/owner.rb +21 -0
  35. data/lib/meta/timestamp.rb +118 -0
  36. data/lib/runo.rb +396 -0
  37. data/lib/scalar/checkbox.rb +68 -0
  38. data/lib/scalar/file.rb +144 -0
  39. data/lib/scalar/img.rb +112 -0
  40. data/lib/scalar/password.rb +58 -0
  41. data/lib/scalar/radio.rb +47 -0
  42. data/lib/scalar/select.rb +47 -0
  43. data/lib/scalar/text.rb +38 -0
  44. data/lib/scalar/textarea.rb +35 -0
  45. data/lib/scalar/textarea_pre.rb +14 -0
  46. data/lib/scalar/textarea_wiki.rb +173 -0
  47. data/lib/set/_set.rb +195 -0
  48. data/lib/set/dynamic.rb +177 -0
  49. data/lib/set/static.rb +102 -0
  50. data/lib/set/static_folder.rb +96 -0
  51. data/locale/en/index.po +242 -0
  52. data/locale/index.pot +243 -0
  53. data/locale/ja/index.po +242 -0
  54. data/locale/lazy_parser.rb +54 -0
  55. data/skel/config.ru +27 -0
  56. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  57. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank.yaml +12 -0
  59. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  60. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  61. data/skel/skin/_users/00000000_root.yaml +11 -0
  62. data/skel/skin/_users/css/users.css +21 -0
  63. data/skel/skin/_users/css/users.less +25 -0
  64. data/skel/skin/_users/done.html +42 -0
  65. data/skel/skin/_users/index.html +46 -0
  66. data/skel/skin/_users/index.yaml +3 -0
  67. data/skel/skin/_users/summary.html +40 -0
  68. data/skel/skin/css/base.css +93 -0
  69. data/skel/skin/css/base.less +139 -0
  70. data/skel/skin/css/coax.css +199 -0
  71. data/skel/skin/css/coax.less +244 -0
  72. data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
  73. data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
  74. data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
  75. data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
  76. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
  79. data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
  80. data/skel/skin/examples/blog/frank/index.yaml +4 -0
  81. data/skel/skin/examples/blog/index.html +51 -0
  82. data/skel/skin/examples/blog/rss.xml +18 -0
  83. data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
  84. data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
  85. data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
  86. data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
  87. data/skel/skin/examples/contact/index.html +47 -0
  88. data/skel/skin/examples/contact/js/contact.js +13 -0
  89. data/skel/skin/examples/contact/summary.html +54 -0
  90. data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
  91. data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
  92. data/skel/skin/examples/forum/index.html +68 -0
  93. data/skel/skin/examples/forum/summary.html +47 -0
  94. data/skel/skin/examples/index.html +75 -0
  95. data/skel/skin/index.html +41 -0
  96. data/skel/skin/js/base.js +50 -0
  97. data/t/locale/de/index.po +19 -0
  98. data/t/locale/en-GB/index.po +25 -0
  99. data/t/locale/ja/index.po +30 -0
  100. data/t/skin/_users/00000000_test.yaml +3 -0
  101. data/t/skin/_users/index.html +13 -0
  102. data/t/skin/foo/20091120_0001.yaml +7 -0
  103. data/t/skin/foo/bar/20091120_0001.yaml +5 -0
  104. data/t/skin/foo/bar/index.yaml +5 -0
  105. data/t/skin/foo/baz/css/baz.css +1 -0
  106. data/t/skin/foo/css/foo.css +1 -0
  107. data/t/skin/foo/index.html +14 -0
  108. data/t/skin/foo/index.yaml +7 -0
  109. data/t/skin/foo/not_css/foo.css +1 -0
  110. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  111. data/t/skin/index.yaml +3 -0
  112. data/t/skin/t_attachment/index.html +13 -0
  113. data/t/skin/t_contact/done.html +6 -0
  114. data/t/skin/t_contact/index.html +9 -0
  115. data/t/skin/t_file/index.html +16 -0
  116. data/t/skin/t_img/index.html +14 -0
  117. data/t/skin/t_img/test.jpg +0 -0
  118. data/t/skin/t_select/index.html +9 -0
  119. data/t/skin/t_store/index.html +9 -0
  120. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  121. data/t/skin/t_summary/create.html +9 -0
  122. data/t/skin/t_summary/index.html +9 -0
  123. data/t/skin/t_summary/summary.html +9 -0
  124. data/t/t.rb +27 -0
  125. data/t/test_checkbox.rb +273 -0
  126. data/t/test_field.rb +330 -0
  127. data/t/test_file.rb +900 -0
  128. data/t/test_id.rb +215 -0
  129. data/t/test_img.rb +328 -0
  130. data/t/test_meta.rb +57 -0
  131. data/t/test_parser.rb +1266 -0
  132. data/t/test_password.rb +188 -0
  133. data/t/test_radio.rb +226 -0
  134. data/t/test_role.rb +249 -0
  135. data/t/test_runo.rb +742 -0
  136. data/t/test_runo_call.rb +1286 -0
  137. data/t/test_runo_i18n.rb +318 -0
  138. data/t/test_select.rb +182 -0
  139. data/t/test_set_complex.rb +527 -0
  140. data/t/test_set_dynamic.rb +1504 -0
  141. data/t/test_set_folder.rb +515 -0
  142. data/t/test_set_permit.rb +246 -0
  143. data/t/test_set_static.rb +445 -0
  144. data/t/test_storage.rb +915 -0
  145. data/t/test_text.rb +125 -0
  146. data/t/test_textarea.rb +138 -0
  147. data/t/test_timestamp.rb +473 -0
  148. data/t/test_workflow.rb +367 -0
  149. metadata +345 -0
@@ -0,0 +1,1286 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require "#{::File.dirname __FILE__}/t"
7
+
8
+ class TC_Runo_Call < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @runo = Runo.new
12
+ end
13
+
14
+ def teardown
15
+ end
16
+
17
+ def test_get
18
+ Runo.client = nil
19
+ res = Rack::MockRequest.new(@runo).get(
20
+ 'http://example.com/foo/'
21
+ )
22
+ assert_match(
23
+ /<html/,
24
+ res.body,
25
+ 'Runo#call() should return the folder.get if the base field is a SD'
26
+ )
27
+ end
28
+
29
+ def test_get_non_exist
30
+ Runo.client = nil
31
+ res = Rack::MockRequest.new(@runo).get(
32
+ 'http://example.com/foo/non/exist/'
33
+ )
34
+ assert_equal(
35
+ 404,
36
+ res.status,
37
+ 'Runo#call() should return 404 if the given path is non-existent'
38
+ )
39
+ end
40
+
41
+ def test_get_partial
42
+ Runo.client = nil
43
+ res = Rack::MockRequest.new(@runo).get(
44
+ 'http://example.com/foo/20091120_0001/name/'
45
+ )
46
+ assert_equal(
47
+ 'FZ',
48
+ res.body,
49
+ 'Runo#call() should return the base.get unless the base field is a SD'
50
+ )
51
+ end
52
+
53
+ def test_get_sub
54
+ Runo.client = nil
55
+ res = Rack::MockRequest.new(@runo).get(
56
+ 'http://example.com/foo/'
57
+ )
58
+ assert_match(
59
+ %r{<li><a>qux</a></li>},
60
+ res.body,
61
+ "Runo#call() should return sets other than 'main' as well"
62
+ )
63
+
64
+ res = Rack::MockRequest.new(@runo).get(
65
+ 'http://example.com/foo/sub/d=2010/'
66
+ )
67
+ assert_match(
68
+ %r{<li><a>qux</a></li>},
69
+ res.body,
70
+ "Runo#call() should pass args for a sd other than 'main' as well"
71
+ )
72
+
73
+ res = Rack::MockRequest.new(@runo).get(
74
+ 'http://example.com/foo/sub/d=2009/'
75
+ )
76
+ assert_no_match(
77
+ %r{<li><a>qux</a></li>},
78
+ res.body,
79
+ "Runo#call() should pass args for a sd other than 'main' as well"
80
+ )
81
+ end
82
+
83
+ def test_get_contact
84
+ Runo.client = nil
85
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.clear
86
+
87
+ res = Rack::MockRequest.new(@runo).get(
88
+ 'http://example.com/1234567890.0123/t_contact/'
89
+ )
90
+ assert_equal(
91
+ 200,
92
+ res.status,
93
+ 'Runo#call to contact by nobody should return status 200'
94
+ )
95
+ assert_equal(
96
+ <<_html,
97
+ <html>
98
+ <head><base href="http:///t_contact/" /><title></title></head>
99
+ <body>
100
+ <h1></h1>
101
+ <form id="form_main" method="post" enctype="multipart/form-data" action="/t_contact/1234567890.0123/update.html">
102
+ <input name="_token" type="hidden" value="#{Runo.token}" />
103
+ <ul id="main" class="runo-contact">
104
+ <li><a><span class="text"><input type="text" name="_001-name" value="foo" size="32" /></span></a>: <span class="text"><input type="text" name="_001-comment" value="bar!" size="64" /></span></li>
105
+ </ul>
106
+ <div class="submit">
107
+ <input name=".status-public" type="submit" value="create" />
108
+ </div>
109
+ </form>
110
+ </body>
111
+ </html>
112
+ _html
113
+ res.body,
114
+ 'Runo#call to contact by nobody should return :create'
115
+ )
116
+ end
117
+
118
+ def test_get_contact_forbidden
119
+ Runo.client = nil
120
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.build(
121
+ '20100425_1234' => {'name' => 'cz', 'comment' => 'howdy.'}
122
+ )
123
+
124
+ res = Rack::MockRequest.new(@runo).get(
125
+ 'http://example.com/t_contact/20100425/1234/index.html'
126
+ )
127
+ assert_no_match(
128
+ /howdy/,
129
+ res.body,
130
+ 'getting an contact by nobody should not return any contents'
131
+ )
132
+ res = Rack::MockRequest.new(@runo).get(
133
+ 'http://example.com/t_contact/20100425_1234/'
134
+ )
135
+ assert_no_match(
136
+ /howdy/,
137
+ res.body,
138
+ 'peeking an contact by nobody should not return any contents'
139
+ )
140
+ res = Rack::MockRequest.new(@runo).get(
141
+ 'http://example.com/t_contact/20100425_1234/comment/'
142
+ )
143
+ assert_no_match(
144
+ /howdy/,
145
+ res.body,
146
+ 'peeking an contact by nobody should not return any contents'
147
+ )
148
+
149
+ res = Rack::MockRequest.new(@runo).get(
150
+ 'http://example.com/t_contact/20100425/1234/done.html'
151
+ )
152
+ assert_no_match(
153
+ /howdy/,
154
+ res.body,
155
+ 'getting an contact by nobody should not return any contents'
156
+ )
157
+ res = Rack::MockRequest.new(@runo).get(
158
+ 'http://example.com/t_contact/20100425_1234/done.html'
159
+ )
160
+ assert_no_match(
161
+ /howdy/,
162
+ res.body,
163
+ 'peeking an contact by nobody should not return any contents'
164
+ )
165
+ res = Rack::MockRequest.new(@runo).get(
166
+ 'http://example.com/t_contact/20100425_1234/comment/done.html'
167
+ )
168
+ assert_no_match(
169
+ /howdy/,
170
+ res.body,
171
+ 'peeking an contact by nobody should not return any contents'
172
+ )
173
+
174
+ res = Rack::MockRequest.new(@runo).get(
175
+ 'http://example.com/t_contact/20100425/1234/preview_create.html'
176
+ )
177
+ assert_no_match(
178
+ /howdy/,
179
+ res.body,
180
+ 'getting an contact by nobody should not return any contents'
181
+ )
182
+ res = Rack::MockRequest.new(@runo).get(
183
+ 'http://example.com/t_contact/20100425_1234/preview_create.html'
184
+ )
185
+ assert_no_match(
186
+ /howdy/,
187
+ res.body,
188
+ 'peeking an contact by nobody should not return any contents'
189
+ )
190
+ res = Rack::MockRequest.new(@runo).get(
191
+ 'http://example.com/t_contact/20100425_1234/comment/preview_create.html'
192
+ )
193
+ assert_no_match(
194
+ /howdy/,
195
+ res.body,
196
+ 'peeking an contact by nobody should not return any contents'
197
+ )
198
+ end
199
+
200
+ def test_get_summary
201
+ Runo.client = nil
202
+ res = Rack::MockRequest.new(@runo).get(
203
+ 'http://example.com/t_summary/p=1/'
204
+ )
205
+ assert_equal(
206
+ <<'_html',
207
+ <html>
208
+ <head><base href="http:///t_summary/" /><title>summary</title></head>
209
+ <body>
210
+ <h1>summary</h1>
211
+ <table id="main" class="runo-blog">
212
+ <tr><td><a href="/t_summary/20100326/1/read_detail.html">frank</a></td><td>hi.</td></tr>
213
+ </table>
214
+ </body>
215
+ </html>
216
+ _html
217
+ res.body,
218
+ 'Runo#call() should use [:tmpl][:summary] when available and appropriate'
219
+ )
220
+
221
+ res = Rack::MockRequest.new(@runo).get(
222
+ 'http://example.com/t_summary/p=1/read_detail.html'
223
+ )
224
+ assert_equal(
225
+ <<'_html',
226
+ <html>
227
+ <head><base href="http:///t_summary/" /><title>index</title></head>
228
+ <body>
229
+ <h1>index</h1>
230
+ <ul id="main" class="runo-blog">
231
+ <li><a>frank</a>: hi.</li>
232
+ </ul>
233
+ </body>
234
+ </html>
235
+ _html
236
+ res.body,
237
+ 'Runo#call() should use [:tmpl] when the action is :read -> :detail'
238
+ )
239
+
240
+ Runo.client = 'root'
241
+ res = Rack::MockRequest.new(@runo).get(
242
+ "http://example.com/t_summary/p=1/update.html"
243
+ )
244
+ tid = res.body[%r{/(#{Runo::REX::TID})/}, 1]
245
+ assert_equal(
246
+ <<"_html",
247
+ <html>
248
+ <head><base href="http:///t_summary/" /><title>index</title></head>
249
+ <body>
250
+ <h1>index</h1>
251
+ <form id="form_main" method="post" enctype="multipart/form-data" action="/t_summary/#{tid}/update.html">
252
+ <input name="_token" type="hidden" value="#{Runo.token}" />
253
+ <ul id="main" class="runo-blog">
254
+ <li><a><span class="text"><input type="text" name="20100326_0001-name" value="frank" size="32" /></span></a>: <span class="text"><input type="text" name="20100326_0001-comment" value="hi." size="64" /></span></li>
255
+ </ul>
256
+ <div class="submit">
257
+ <input name=".status-public" type="submit" value="update" />
258
+ <input name=".action-preview_delete" type="submit" value="delete..." />
259
+ </div>
260
+ </form>
261
+ </body>
262
+ </html>
263
+ _html
264
+ res.body,
265
+ 'Runo#call() should use [:tmpl] unless the action is :read'
266
+ )
267
+ end
268
+
269
+ def test_get_static
270
+ Runo.client = 'root'
271
+ res = Rack::MockRequest.new(@runo).get(
272
+ 'http://example.com/foo/css/foo.css'
273
+ )
274
+ assert_equal(
275
+ 200,
276
+ res.status,
277
+ 'Runo#call should return a static file if the given path is under css/, js/, etc.'
278
+ )
279
+ assert_equal(
280
+ 'text/css',
281
+ res.headers['Content-Type'],
282
+ 'Runo#call should return a static file if the given path is under css/, js/, etc.'
283
+ )
284
+ assert_equal(
285
+ "#foo {bar: baz;}\n",
286
+ res.body,
287
+ 'Runo#call should return a static file if the given path is under css/, js/, etc.'
288
+ )
289
+ end
290
+
291
+ def test_get_static_not_css
292
+ Runo.client = 'root'
293
+ res = Rack::MockRequest.new(@runo).get(
294
+ 'http://example.com/foo/not_css/foo.css'
295
+ )
296
+ assert_not_equal(
297
+ "#foo {bar: baz;}\n",
298
+ res.body,
299
+ 'Runo#call should not return a static file if the step is not exactly css/, js/, etc.'
300
+ )
301
+ end
302
+
303
+ def test_get_static_non_exist
304
+ Runo.client = 'root'
305
+ res = Rack::MockRequest.new(@runo).get(
306
+ 'http://example.com/foo/css/non-exist.css'
307
+ )
308
+ assert_equal(
309
+ 404,
310
+ res.status,
311
+ 'Runo#call should return 404 if the static file is not found'
312
+ )
313
+ end
314
+
315
+ def test_get_static_ambiguous
316
+ Runo.client = 'root'
317
+ res = Rack::MockRequest.new(@runo).get(
318
+ 'http://example.com/foo/css/0123456789.1234/_001/img/bar.jpg'
319
+ )
320
+ assert_equal(
321
+ 'Not Found', # from Runo#call
322
+ res.body,
323
+ 'Runo#call should not look for a static file if the path includes a tid'
324
+ )
325
+
326
+ res = Rack::MockRequest.new(@runo).get(
327
+ 'http://example.com/foo/css/20100613_1234/img/bar.jpg'
328
+ )
329
+ assert_equal(
330
+ 'Not Found', # from Runo#call
331
+ res.body,
332
+ 'Runo#call should not look for a static file if the path includes an id'
333
+ )
334
+
335
+ res = Rack::MockRequest.new(@runo).get(
336
+ 'http://example.com/foo/css/0123456789.1234/20100613_1234/img/bar.jpg'
337
+ )
338
+ assert_equal(
339
+ 'Not Found', # from Runo#call
340
+ res.body,
341
+ 'Runo#call should not look for a static file if the path includes an id'
342
+ )
343
+
344
+ res = Rack::MockRequest.new(@runo).get(
345
+ 'http://example.com/foo/css/20100613_1234/bar/id=img/'
346
+ )
347
+ assert_equal(
348
+ 'Not Found', # from Runo#call
349
+ res.body,
350
+ 'Runo#call should not look for a static file if the path includes an id'
351
+ )
352
+ end
353
+
354
+ def test_get_static_forbidden
355
+ Runo.client = 'root'
356
+ res = Rack::MockRequest.new(@runo).get(
357
+ 'http://example.com/_users/00000000_test.yaml'
358
+ )
359
+ assert_no_match(
360
+ /^password/,
361
+ res.body,
362
+ 'Runo#call should not return meta files nor raw data'
363
+ )
364
+
365
+ res = Rack::MockRequest.new(@runo).get(
366
+ 'http://example.com/_users/css/../00000000_test.yaml'
367
+ )
368
+ assert_no_match(
369
+ /password/,
370
+ res.body,
371
+ 'Runo#call should not allow bad paths'
372
+ )
373
+ end
374
+
375
+ def test_get_static_cascade
376
+ Runo.client = 'root'
377
+
378
+ res = Rack::MockRequest.new(@runo).get(
379
+ 'http://example.com/foo/bar/css/foo.css'
380
+ )
381
+ assert_equal(
382
+ "#foo {bar: baz;}\n",
383
+ res.body,
384
+ 'Runo#call should look the acnestor dirs for the static directory'
385
+ )
386
+
387
+ res = Rack::MockRequest.new(@runo).get(
388
+ 'http://example.com/foo/bar/css/non_exist.css'
389
+ )
390
+ assert_equal(
391
+ 404,
392
+ res.status,
393
+ 'Runo#call should return 404 if the file is not found in the nearest static dir'
394
+ )
395
+
396
+ res = Rack::MockRequest.new(@runo).get(
397
+ 'http://example.com/foo/baz/css/foo.css'
398
+ )
399
+ assert_equal(
400
+ 404,
401
+ res.status,
402
+ 'Runo#call should not look the ancestor dirs if the file is not found in the nearest dir'
403
+ )
404
+
405
+ res = Rack::MockRequest.new(@runo).get(
406
+ 'http://example.com/foo/bar/js/non_exist.css'
407
+ )
408
+ assert_equal(
409
+ 404,
410
+ res.status,
411
+ 'Runo#call should return 404 if the static dir is not found in any ancestor dirs'
412
+ )
413
+ end
414
+
415
+ def test_post_simple_create
416
+ Runo.client = 'root'
417
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
418
+
419
+ res = Rack::MockRequest.new(@runo).post(
420
+ 'http://example.com/t_store/main/update.html',
421
+ {
422
+ :input => "_1-name=fz&_1-comment=hi.&.status-public=create&_token=#{Runo.token}"
423
+ }
424
+ )
425
+ assert_equal(
426
+ 303,
427
+ res.status,
428
+ 'Runo#call with post method should return status 303'
429
+ )
430
+ assert_match(
431
+ Runo::REX::PATH_ID,
432
+ res.headers['Location'],
433
+ 'Runo#call with post method should return a proper location'
434
+ )
435
+
436
+ res.headers['Location'] =~ Runo::REX::PATH_ID
437
+ new_id = sprintf('%.8d_%.4d', $1, $2)
438
+
439
+ val = Runo::Set::Static::Folder.root.item('t_store', 'main', new_id).val
440
+ assert_instance_of(
441
+ ::Hash,
442
+ val,
443
+ 'Runo#call with post method should store the item in the storage'
444
+ )
445
+ val.delete '_timestamp'
446
+ assert_equal(
447
+ {'_owner' => 'root', 'name' => 'fz', 'comment' => 'hi.'},
448
+ val,
449
+ 'Runo#call with post method should store the item in the storage'
450
+ )
451
+ end
452
+
453
+ def test_post_invalid_create
454
+ Runo.client = 'root'
455
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
456
+
457
+ res = Rack::MockRequest.new(@runo).post(
458
+ "http://example.com/t_store/main/update.html",
459
+ {
460
+ :input => "_1-name=tooooooooooooloooooooooooooooooooooooooooong&_1-comment=hi.&.status-public=create&_token=#{Runo.token}"
461
+ }
462
+ )
463
+ tid = res.body[%r{/(#{Runo::REX::TID})/}, 1]
464
+
465
+ assert_equal(
466
+ 422,
467
+ res.status,
468
+ 'Runo#post with an invalid new item should be an error'
469
+ )
470
+ assert_instance_of(
471
+ Runo::Set::Dynamic,
472
+ Runo.transaction[tid],
473
+ 'the suspended SD should be kept in Runo.transaction'
474
+ )
475
+ end
476
+
477
+ def test_post_empty_create
478
+ Runo.client = 'root'
479
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
480
+
481
+ res = Rack::MockRequest.new(@runo).post(
482
+ 'http://example.com/t_store/main/update.html',
483
+ {
484
+ :input => "_1-name=&_1-comment=&.status-public=create&_token=#{Runo.token}"
485
+ }
486
+ )
487
+ assert_equal(
488
+ 422,
489
+ res.status,
490
+ 'Runo#post with an empty new item should be an error'
491
+ )
492
+ end
493
+
494
+ def test_post_empty_update
495
+ Runo.client = 'root'
496
+
497
+ res = Rack::MockRequest.new(@runo).post(
498
+ 'http://example.com/t_store/main/update.html',
499
+ {
500
+ :input => "_1-name=don&_1-comment=brown.&.status-public=create&_token=#{Runo.token}"
501
+ }
502
+ )
503
+ res.headers['Location'] =~ Runo::REX::PATH_ID
504
+ new_id = sprintf('%.8d_%.4d', $1, $2)
505
+
506
+ res = Rack::MockRequest.new(@runo).post(
507
+ 'http://example.com/t_store/main/update.html',
508
+ {
509
+ :input => "#{new_id}-name=don&.status-public=update&_token=#{Runo.token}"
510
+ }
511
+ )
512
+ assert_equal(
513
+ 303,
514
+ res.status,
515
+ 'Runo#post without any update on the item should not raise an error'
516
+ )
517
+
518
+ res = Rack::MockRequest.new(@runo).get(
519
+ res.headers['Location']
520
+ )
521
+ assert_no_match(
522
+ /message/,
523
+ res.body,
524
+ 'Runo#post without any update on the item should not set any messages'
525
+ )
526
+ end
527
+
528
+ def test_post_back_and_forth
529
+ Runo.client = 'root'
530
+
531
+ # post from a form without status
532
+ res = Rack::MockRequest.new(@runo).post(
533
+ 'http://example.com/t_store/main/1234567890.9999/update.html',
534
+ {
535
+ :input => "_1-comment=brown."
536
+ }
537
+ )
538
+
539
+ # back to the form, post again with status
540
+ res = Rack::MockRequest.new(@runo).post(
541
+ 'http://example.com/t_store/main/1234567890.9999/update.html',
542
+ {
543
+ :input => "_1-name=don&_1-comment=brown.&.status-public=create&_token=#{Runo.token}"
544
+ }
545
+ )
546
+ assert_equal(
547
+ 303,
548
+ res.status,
549
+ 'Runo#post twice from the same form should work if the previous post is without status'
550
+ )
551
+
552
+ # back to the form one more time, post again with status
553
+ res = Rack::MockRequest.new(@runo).post(
554
+ 'http://example.com/t_store/main/1234567890.9999/update.html',
555
+ {
556
+ :input => "_1-name=roy&_1-comment=brown.&.status-public=create&_token=#{Runo.token}"
557
+ }
558
+ )
559
+ assert_equal(
560
+ 422,
561
+ res.status,
562
+ 'Runo#post twice from the same form should not work if the previous post is with status'
563
+ )
564
+ assert_equal(
565
+ 'transaction expired',
566
+ res.body,
567
+ 'Runo#post twice from the same form should not work if the previous post is with status'
568
+ )
569
+ end
570
+
571
+ def test_post_with_invalid_token
572
+ Runo.client = 'root'
573
+ res = Rack::MockRequest.new(@runo).post(
574
+ 'http://example.com/t_store/main/update.html',
575
+ {
576
+ :input => "_1-name=fz&_1-comment=hi.&.status-public=create&_token=invalid"
577
+ }
578
+ )
579
+ assert_equal(
580
+ 403,
581
+ res.status,
582
+ 'Runo#call without a valid token should return status 403'
583
+ )
584
+ assert_equal(
585
+ 'invalid token',
586
+ res.body,
587
+ 'Runo#call without a valid token should return status 403'
588
+ )
589
+ end
590
+
591
+ def test_post_with_attachment
592
+ Runo.client = 'root'
593
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main').storage.clear
594
+
595
+ # post an attachment
596
+ res = Rack::MockRequest.new(@runo).post(
597
+ 'http://example.com/t_attachment/main/update.html',
598
+ {
599
+ :input => "_012-files-_1-file=wow.jpg&_012-files-_1.action-create=create&_token=#{Runo.token}"
600
+ }
601
+ )
602
+ assert_match(
603
+ 'update.html',
604
+ res.headers['Location'],
605
+ 'Runo#call without the root status should always return :update'
606
+ )
607
+ assert_equal(
608
+ {},
609
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main').val,
610
+ 'Runo#call without the root status should not update the persistent storage'
611
+ )
612
+
613
+ tid = res.headers['Location'][Runo::REX::TID]
614
+ assert_instance_of(
615
+ Runo::Set::Dynamic,
616
+ Runo.transaction[tid],
617
+ 'the suspended SD should be kept in Runo.transaction'
618
+ )
619
+
620
+ # post the item
621
+ res = Rack::MockRequest.new(@runo).post(
622
+ "http://example.com/#{tid}/update.html",
623
+ {
624
+ :input => "_012-comment=hello.&.status-public=create&_token=#{Runo.token}"
625
+ }
626
+ )
627
+ assert_no_match(
628
+ /update\.html/,
629
+ res.headers['Location'],
630
+ 'Runo#call with the root status should commit the transaction'
631
+ )
632
+ assert_not_equal(
633
+ {},
634
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main').val,
635
+ 'Runo#call with the root status should update the persistent storage'
636
+ )
637
+
638
+ res.headers['Location'] =~ Runo::REX::PATH_ID
639
+ new_id = sprintf('%.8d_%.4d', $1, $2)
640
+ new_item = Runo::Set::Static::Folder.root.item('t_attachment', 'main', new_id)
641
+ assert_not_equal(
642
+ {},
643
+ new_item.val,
644
+ 'Runo#call with the root status should commit all the pending items'
645
+ )
646
+ assert_equal(
647
+ 'hello.',
648
+ new_item.val['comment'],
649
+ 'Runo#call with the root status should commit the root items'
650
+ )
651
+ assert_equal(
652
+ {'file' => 'wow.jpg'},
653
+ new_item.val['files'].values.first,
654
+ 'Runo#call with the root status should commit the descendant items'
655
+ )
656
+
657
+ # post a reply
658
+ res = Rack::MockRequest.new(@runo).post(
659
+ "http://example.com/t_attachment/main/#{new_id}/replies/update.html",
660
+ {
661
+ :input => "_001-reply=wow.&.status-public=create&_token=#{Runo.token}"
662
+ }
663
+ )
664
+ assert_equal(303, res.status)
665
+ assert_match(
666
+ %r{/#{Runo::REX::TID}/t_attachment/#{new_id}/replies/read_detail.html},
667
+ res.headers['Location'],
668
+ 'Runo#call with a sub-app status should commit the root item'
669
+ )
670
+ assert_not_equal(
671
+ {},
672
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main', new_id, 'replies').val,
673
+ 'Runo#call with a sub-app status should update the persistent storage'
674
+ )
675
+ end
676
+
677
+ def test_post_only_attachment
678
+ Runo.client = 'root'
679
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main').storage.clear
680
+
681
+ # post an item
682
+ res = Rack::MockRequest.new(@runo).post(
683
+ "http://example.com/t_attachment/main/update.html",
684
+ {
685
+ :input => "_012-comment=abc&.status-public=create"
686
+ }
687
+ )
688
+ res.headers['Location'] =~ Runo::REX::PATH_ID
689
+ new_id = sprintf('%.8d_%.4d', $1, $2)
690
+
691
+ # post an attachment
692
+ tid = '1234567890.1234'
693
+ res = Rack::MockRequest.new(@runo).post(
694
+ "http://example.com/#{tid}/t_attachment/update.html",
695
+ {
696
+ :input => "#{new_id}-files-_1-file=boo.jpg&#{new_id}-files-_1.action-create=create&_token=#{Runo.token}"
697
+ }
698
+ )
699
+ attachment_id = Runo.transaction[tid].item(new_id, 'files').val.keys.first
700
+ res = Rack::MockRequest.new(@runo).post(
701
+ "http://example.com/#{tid}/update.html",
702
+ {
703
+ :input => "#{new_id}-files-#{attachment_id}-file=boo.jpg&#{new_id}-files-_1-file=&.status-public=create&_token=#{Runo.token}"
704
+ }
705
+ )
706
+
707
+ assert_not_nil(
708
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main', new_id).val['files'],
709
+ 'Runo#call should treat the post with only an attachement nicely'
710
+ )
711
+ end
712
+
713
+ def test_post_with_invalid_attachment
714
+ Runo.client = 'root'
715
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main').storage.clear
716
+
717
+ # post an invalid empty attachment
718
+ res = Rack::MockRequest.new(@runo).post(
719
+ 'http://example.com/t_attachment/main/update.html',
720
+ {
721
+ :input => "_012-files-_1-file=&_012-files-_1.action-create=create&_token=#{Runo.token}"
722
+ }
723
+ )
724
+ tid = res.headers['Location'][Runo::REX::TID]
725
+
726
+ # post the root item
727
+ res = Rack::MockRequest.new(@runo).post(
728
+ "http://example.com/#{tid}/update.html",
729
+ {
730
+ :input => "_012-comment=hello.&.status-public=create&_token=#{Runo.token}"
731
+ }
732
+ )
733
+ assert_equal(
734
+ 303,
735
+ res.status,
736
+ 'Runo#call with the root status should ignore the invalid empty attachment'
737
+ )
738
+ assert_not_equal(
739
+ {},
740
+ Runo::Set::Static::Folder.root.item('t_attachment', 'main').val,
741
+ 'Runo#call with the root status should ignore the invalid empty attachment'
742
+ )
743
+
744
+ # post an invalid non-empty attachment
745
+ res = Rack::MockRequest.new(@runo).post(
746
+ 'http://example.com/t_attachment/main/update.html',
747
+ {
748
+ :input => "_012-files-_1-file=tooloooooooooooong&_012-files-_1.action-create=create&_token=#{Runo.token}"
749
+ }
750
+ )
751
+ tid = res.headers['Location'][Runo::REX::TID]
752
+
753
+ # post the root item
754
+ res = Rack::MockRequest.new(@runo).post(
755
+ "http://example.com/#{tid}/update.html",
756
+ {
757
+ :input => "_012-comment=hello.&.status-public=create&_token=#{Runo.token}"
758
+ }
759
+ )
760
+ assert_equal(
761
+ 422,
762
+ res.status,
763
+ 'Runo#call with the root status should not ignore the invalid non-empty attachment'
764
+ )
765
+ end
766
+
767
+ def test_post_preview_update
768
+ Runo.client = 'root'
769
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
770
+ base_uri = ''
771
+
772
+ res = Rack::MockRequest.new(@runo).post(
773
+ "http://#{base_uri}/t_store/main/update.html",
774
+ {
775
+ :input => ".action-preview_update=submit&_1-name=fz&_1-comment=howdy.&.status-public=create"
776
+ }
777
+ )
778
+ tid = res.headers['Location'][Runo::REX::TID]
779
+
780
+ assert_equal(
781
+ 303,
782
+ res.status,
783
+ 'Runo#call with :preview action should return status 303 upon success'
784
+ )
785
+ assert_equal(
786
+ "http://#{base_uri}/#{tid}/id=_1/preview_update.html",
787
+ res.headers['Location'],
788
+ 'Runo#call with :preview action should return a proper location'
789
+ )
790
+ assert_instance_of(
791
+ Runo::Set::Dynamic,
792
+ Runo.transaction[tid],
793
+ 'the suspended SD should be kept in Runo.transaction'
794
+ )
795
+
796
+ res = Rack::MockRequest.new(@runo).get(
797
+ "http://#{base_uri}/#{tid}/id=_1/preview_update.html"
798
+ )
799
+ assert_equal(
800
+ <<"_html",
801
+ <html>
802
+ <head><base href="http://#{base_uri}/t_store/" /><title></title></head>
803
+ <body>
804
+ <h1></h1>
805
+ <ul class="message notice">
806
+ <li>please confirm.</li>
807
+ </ul>
808
+ <form id="form_main" method="post" enctype="multipart/form-data" action="/#{tid}/update.html">
809
+ <input name="_token" type="hidden" value="#{Runo.token}" />
810
+ <ul id="main" class="runo-blog">
811
+ <li><a>fz</a>: howdy.<input type="hidden" name="_1.action" value="create" /></li>
812
+ </ul>
813
+ <div class="submit">
814
+ <input name=".status-public" type="submit" value="create" />
815
+ </div>
816
+ </form>
817
+ </body>
818
+ </html>
819
+ _html
820
+ res.body,
821
+ 'Runo#call with :preview action should set a proper transaction upon success'
822
+ )
823
+
824
+ res = Rack::MockRequest.new(@runo).post(
825
+ "http://example.com/#{tid}/update.html",
826
+ {
827
+ :input => "_1.action=create&.status-public=create&_token=#{Runo.token}"
828
+ }
829
+ )
830
+ assert_equal(
831
+ 303,
832
+ res.status,
833
+ 'Runo#call with post method should return status 303'
834
+ )
835
+ assert_match(
836
+ Runo::REX::PATH_ID,
837
+ res.headers['Location'],
838
+ 'Runo#call with post method should return a proper location'
839
+ )
840
+
841
+ res.headers['Location'] =~ Runo::REX::PATH_ID
842
+ new_id = sprintf('%.8d_%.4d', $1, $2)
843
+
844
+ val = Runo::Set::Static::Folder.root.item('t_store', 'main', new_id).val
845
+ assert_instance_of(
846
+ ::Hash,
847
+ val,
848
+ 'Runo#call with post method should store the item in the storage'
849
+ )
850
+ val.delete '_timestamp'
851
+ assert_equal(
852
+ {'_owner' => 'root', 'name' => 'fz', 'comment' => 'howdy.'},
853
+ val,
854
+ 'Runo#call with post method should store the item in the storage'
855
+ )
856
+ end
857
+
858
+ def test_post_preview_invalid
859
+ Runo.client = 'root'
860
+
861
+ res = Rack::MockRequest.new(@runo).post(
862
+ 'http://example.com/t_store/main/update.html',
863
+ {
864
+ :input => ".action-preview_update=submit&_1-name=verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrylong&_1-comment=howdy.&.status-public=create"
865
+ }
866
+ )
867
+ assert_equal(
868
+ 422,
869
+ res.status,
870
+ 'Runo#call with :preview action & malformed input should return status 422'
871
+ )
872
+ assert_match(
873
+ /malformed input\./,
874
+ res.body,
875
+ 'Runo#call with :preview action & malformed input should return :update'
876
+ )
877
+
878
+ tid = res.body[%r{/(#{Runo::REX::TID})/}, 1]
879
+ assert_instance_of(
880
+ Runo::Set::Dynamic,
881
+ Runo.transaction[tid],
882
+ 'the suspended SD should be kept in Runo.transaction'
883
+ )
884
+ end
885
+
886
+ def test_post_contact
887
+ Runo.client = nil
888
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.clear
889
+
890
+ res = Rack::MockRequest.new(@runo).post(
891
+ 'http://example.com/t_contact/main/update.html',
892
+ {
893
+ :input => "_1-name=fz&_1-comment=hi.&.status-public=create&_token=#{Runo.token}"
894
+ }
895
+ )
896
+ assert_equal(
897
+ 303,
898
+ res.status,
899
+ 'Runo#call with post method should return status 303'
900
+ )
901
+ assert_no_match(
902
+ Runo::REX::PATH_ID,
903
+ res.headers['Location'],
904
+ 'Runo#call should not tell the item location when the workflow is contact'
905
+ )
906
+
907
+ res = Rack::MockRequest.new(@runo).get(
908
+ res.headers['Location'],
909
+ {}
910
+ )
911
+ assert_match(
912
+ /thank you!/,
913
+ res.body,
914
+ 'Runo#call should refer to (action).html if available'
915
+ )
916
+ assert_no_match(
917
+ /message/,
918
+ res.body,
919
+ 'Runo#call should not include messages for action :done'
920
+ )
921
+ assert_no_match(
922
+ /login/,
923
+ res.body,
924
+ 'Runo#call should always allow action :done'
925
+ )
926
+ end
927
+
928
+ def test_post_contact_forbidden
929
+ Runo.client = nil
930
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.build(
931
+ '20100425_1234' => {'_owner' => 'nobody', 'name' => 'cz', 'comment' => 'howdy.'}
932
+ )
933
+
934
+ res = Rack::MockRequest.new(@runo).post(
935
+ 'http://example.com/t_contact/main/update.html',
936
+ {
937
+ :input => "20100425_1234-comment=modified&20100425_1234.action=create&.status-public=create"
938
+ }
939
+ )
940
+ assert_equal(
941
+ 403,
942
+ res.status,
943
+ 'Runo#call should not allow nobody to update an existing contact'
944
+ )
945
+ assert_equal(
946
+ {'20100425_1234' => {'_owner' => 'nobody', 'name' => 'cz', 'comment' => 'howdy.'}},
947
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.val,
948
+ 'Runo#call should not allow nobody to update an existing contact'
949
+ )
950
+
951
+ res = Rack::MockRequest.new(@runo).post(
952
+ 'http://example.com/t_contact/main/create.html',
953
+ {
954
+ :input => "20100425_1234-comment=modified&20100425_1234.action=create&.status-public=create"
955
+ }
956
+ )
957
+ assert_equal(
958
+ 403,
959
+ res.status,
960
+ 'Runo#call should not allow nobody to update an existing contact'
961
+ )
962
+ assert_equal(
963
+ {'20100425_1234' => {'_owner' => 'nobody', 'name' => 'cz', 'comment' => 'howdy.'}},
964
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.val,
965
+ 'Runo#call should not allow nobody to update an existing contact'
966
+ )
967
+
968
+ res = Rack::MockRequest.new(@runo).post(
969
+ 'http://example.com/t_contact/main/20100425_1234/create.html',
970
+ {
971
+ :input => "comment=modified&.action=create&.status-public=create"
972
+ }
973
+ )
974
+ assert_equal(
975
+ 403,
976
+ res.status,
977
+ 'Runo#call should not allow nobody to update an existing contact'
978
+ )
979
+ assert_equal(
980
+ {'20100425_1234' => {'_owner' => 'nobody', 'name' => 'cz', 'comment' => 'howdy.'}},
981
+ Runo::Set::Static::Folder.root.item('t_contact', 'main').storage.val,
982
+ 'Runo#call should not allow nobody to update an existing contact'
983
+ )
984
+ end
985
+
986
+ def test_post_wrong_action
987
+ Runo.client = nil
988
+ res = Rack::MockRequest.new(@runo).post(
989
+ 'http://example.com/t_store/main/read.html',
990
+ {
991
+ :input => "_1-name=fz&_1-comment=hi.&.status-public=create"
992
+ }
993
+ )
994
+ assert_equal(
995
+ 403,
996
+ res.status,
997
+ 'post with an action other than :update should be regarded as :update'
998
+ )
999
+
1000
+ Runo.client = 'root'
1001
+ res = Rack::MockRequest.new(@runo).post(
1002
+ 'http://example.com/t_store/main/read.html',
1003
+ {
1004
+ :input => "_1-name=fz&_1-comment=hi.&.status-public=create&_token=#{Runo.token}"
1005
+ }
1006
+ )
1007
+ assert_equal(
1008
+ 303,
1009
+ res.status,
1010
+ 'post with an action other than :update should be regarded as :update'
1011
+ )
1012
+ end
1013
+
1014
+ def test_post_login
1015
+ Runo.client = nil
1016
+ res = Rack::MockRequest.new(@runo).post(
1017
+ "http://example.com/foo/20100222/1/login.html",
1018
+ {
1019
+ :input => "id=test&pw=test&dest_action=update"
1020
+ }
1021
+ )
1022
+ assert_equal(
1023
+ 'test',
1024
+ Runo.client,
1025
+ 'Runo#call with :login action should set Runo.client upon success'
1026
+ )
1027
+ assert_equal(
1028
+ 303,
1029
+ res.status,
1030
+ 'Runo#call with :login action should return status 303'
1031
+ )
1032
+ assert_match(
1033
+ %r{/foo/20100222/1/update.html},
1034
+ res.headers['Location'],
1035
+ 'Runo#call with :login action should return a proper location'
1036
+ )
1037
+ end
1038
+
1039
+ def test_post_login_with_wrong_pw
1040
+ Runo.client = nil
1041
+ res = Rack::MockRequest.new(@runo).post(
1042
+ "http://example.com/foo/20100222/1/login.html",
1043
+ {
1044
+ :input => "id=test&pw=wrong&dest_action=update"
1045
+ }
1046
+ )
1047
+ assert_equal(
1048
+ 'nobody',
1049
+ Runo.client,
1050
+ 'Runo#call with :login action should not set Runo.client upon failure'
1051
+ )
1052
+ assert_equal(
1053
+ 422,
1054
+ res.status,
1055
+ 'Runo#call with :login action should return status 422 upon failure'
1056
+ )
1057
+ end
1058
+
1059
+ def test_post_logout
1060
+ Runo.client = 'frank'
1061
+ res = Rack::MockRequest.new(@runo).post(
1062
+ "http://example.com/foo/20100222/1/logout.html?_token=#{Runo.token}",
1063
+ {}
1064
+ )
1065
+ assert_equal(
1066
+ 'nobody',
1067
+ Runo.client,
1068
+ 'Runo#call with :logout action should unset Runo.client'
1069
+ )
1070
+ assert_equal(
1071
+ 303,
1072
+ res.status,
1073
+ 'Runo#call with :logout action should return status 303'
1074
+ )
1075
+ assert_match(
1076
+ %r{/foo/20100222/1/index.html},
1077
+ res.headers['Location'],
1078
+ 'Runo#call with :logout action should return a proper location'
1079
+ )
1080
+ end
1081
+
1082
+ def test_post_logout_with_invalid_token
1083
+ Runo.client = 'frank'
1084
+ res = Rack::MockRequest.new(@runo).post(
1085
+ "http://example.com/foo/20100222/1/logout.html?_token=invalid",
1086
+ {}
1087
+ )
1088
+ assert_equal(
1089
+ 403,
1090
+ res.status,
1091
+ 'Runo#call without a valid token should return status 403'
1092
+ )
1093
+ assert_equal(
1094
+ 'invalid token',
1095
+ res.body,
1096
+ 'Runo#call without a valid token should return status 403'
1097
+ )
1098
+ assert_equal(
1099
+ 'frank',
1100
+ Runo.client,
1101
+ 'Runo#call without a valid token should return status 403'
1102
+ )
1103
+ end
1104
+
1105
+ def test_get_logout
1106
+ Runo.client = 'frank'
1107
+ res = Rack::MockRequest.new(@runo).get(
1108
+ "http://example.com/foo/20100222/1/logout.html?_token=#{Runo.token}",
1109
+ {}
1110
+ )
1111
+ assert_equal(
1112
+ 'nobody',
1113
+ Runo.client,
1114
+ 'Runo#call with :logout action should work via both get and post'
1115
+ )
1116
+ assert_match(
1117
+ %r{/foo/20100222/1/index.html},
1118
+ res.headers['Location'],
1119
+ 'Runo#call with :logout action should work via both get and post'
1120
+ )
1121
+ end
1122
+
1123
+ def test_message_notice
1124
+ Runo.client = 'root'
1125
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
1126
+
1127
+ res = Rack::MockRequest.new(@runo).post(
1128
+ 'http://example.com/t_store/main/update.html',
1129
+ {
1130
+ :input => "_2-name=fz&_2-comment=hi.&.status-public=create&_token=#{Runo.token}"
1131
+ }
1132
+ )
1133
+ assert_match(
1134
+ %r{#{Runo::REX::TID}/t_store/},
1135
+ res.headers['Location'],
1136
+ 'Runo#call should return both the base path and tid at :done'
1137
+ )
1138
+
1139
+ tid = res.headers['Location'][Runo::REX::TID]
1140
+ new_id = res.headers['Location'][Runo::REX::PATH_ID]
1141
+
1142
+ res = Rack::MockRequest.new(@runo).get(
1143
+ res.headers['Location']
1144
+ )
1145
+ assert_match(
1146
+ /created 1 entry\./,
1147
+ res.body,
1148
+ 'Runo#call should include the current message'
1149
+ )
1150
+
1151
+ res = Rack::MockRequest.new(@runo).get(
1152
+ "http://example.com/#{tid}/#{new_id}index.html"
1153
+ )
1154
+ assert_no_match(
1155
+ /created 1 entry\./,
1156
+ res.body,
1157
+ 'Runo#call should not include the message twice'
1158
+ )
1159
+
1160
+ res.headers['Location'] =~ Runo::REX::PATH_ID
1161
+ new_id = sprintf('%.8d_%.4d', $1, $2)
1162
+ res = Rack::MockRequest.new(@runo).post(
1163
+ 'http://example.com/t_store/main/update.html',
1164
+ {
1165
+ :input => "#{new_id}-comment=howdy.&.status-public=update&_token=#{Runo.token}"
1166
+ }
1167
+ )
1168
+ res = Rack::MockRequest.new(@runo).get(
1169
+ res.headers['Location']
1170
+ )
1171
+ assert_match(
1172
+ /updated 1 entry\./,
1173
+ res.body,
1174
+ 'Runo#call should include a message according to the action'
1175
+ )
1176
+
1177
+ res = Rack::MockRequest.new(@runo).post(
1178
+ 'http://example.com/t_store/main/update.html',
1179
+ {
1180
+ :input => "#{new_id}.action=delete&.status-public=delete&_token=#{Runo.token}"
1181
+ }
1182
+ )
1183
+ res = Rack::MockRequest.new(@runo).get(
1184
+ res.headers['Location']
1185
+ )
1186
+ assert_match(
1187
+ /deleted 1 entry\./,
1188
+ res.body,
1189
+ 'Runo#call should include a message according to the action'
1190
+ )
1191
+ end
1192
+
1193
+ def test_message_notice_plural
1194
+ Runo.client = 'root'
1195
+
1196
+ res = Rack::MockRequest.new(@runo).post(
1197
+ 'http://example.com/t_attachment/main/update.html',
1198
+ {
1199
+ :input => "_1-comment=foo&_2-comment=bar&.status-public=create&_token=#{Runo.token}"
1200
+ }
1201
+ )
1202
+ res = Rack::MockRequest.new(@runo).get(
1203
+ res.headers['Location']
1204
+ )
1205
+ assert_match(
1206
+ /created 2 tArticles\./,
1207
+ res.body,
1208
+ 'the message should be plural if more than one item have results.'
1209
+ )
1210
+ end
1211
+
1212
+ def test_message_alert
1213
+ Runo.client = 'nobody'
1214
+
1215
+ res = Rack::MockRequest.new(@runo).get(
1216
+ "http://example.com/foo/20091120/1/update.html"
1217
+ )
1218
+ assert_match(
1219
+ /please login\./,
1220
+ res.body,
1221
+ 'Runo#call should include the current message'
1222
+ )
1223
+ end
1224
+
1225
+ def test_message_error
1226
+ Runo.client = 'root'
1227
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
1228
+
1229
+ res = Rack::MockRequest.new(@runo).post(
1230
+ 'http://example.com/t_store/main/update.html',
1231
+ {
1232
+ :input => "_2-name=verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrylongname&.status-public=create&_token=#{Runo.token}"
1233
+ }
1234
+ )
1235
+ assert_match(
1236
+ /malformed input\./,
1237
+ res.body,
1238
+ 'Runo#call should include the current message'
1239
+ )
1240
+ end
1241
+
1242
+ def test_message_i18n
1243
+ Runo.client = 'root'
1244
+ Runo::Set::Static::Folder.root.item('t_store', 'main').storage.clear
1245
+
1246
+ res = Rack::MockRequest.new(@runo).post(
1247
+ 'http://example.com/t_store/main/update.html',
1248
+ {
1249
+ :input => "_3-name=&.status-public=create&_token=#{Runo.token}",
1250
+ 'HTTP_ACCEPT_LANGUAGE' => 'en, de',
1251
+ }
1252
+ )
1253
+ assert_match(
1254
+ /malformed input/,
1255
+ res.body,
1256
+ "Runo::I18n.find_msg should return at least an empty hash for 'en' as HTTP_ACCEPT_LANGUAGE."
1257
+ )
1258
+
1259
+ res = Rack::MockRequest.new(@runo).post(
1260
+ 'http://example.com/t_store/main/update.html',
1261
+ {
1262
+ :input => "_3-name=&.status-public=create&_token=#{Runo.token}",
1263
+ 'HTTP_ACCEPT_LANGUAGE' => 'en-US, de',
1264
+ }
1265
+ )
1266
+ assert_match(
1267
+ /malformed input/,
1268
+ res.body,
1269
+ "Runo::I18n.find_msg should return at least an empty hash for 'en' as HTTP_ACCEPT_LANGUAGE."
1270
+ )
1271
+
1272
+ res = Rack::MockRequest.new(@runo).post(
1273
+ 'http://example.com/t_store/main/update.html',
1274
+ {
1275
+ :input => "_3-name=&.status-public=create&_token=#{Runo.token}",
1276
+ 'HTTP_ACCEPT_LANGUAGE' => 'de, en',
1277
+ }
1278
+ )
1279
+ assert_match(
1280
+ /Fehlerhafte Eingabe/,
1281
+ res.body,
1282
+ 'Set::Dynamic#_g_message should be i18nized according to HTTP_ACCEPT_LANGUAGE.'
1283
+ )
1284
+ end
1285
+
1286
+ end