update Changes
[catagits/Web-Simple.git] / t / dispatch_parser.t
CommitLineData
920d6222 1use strict;
2use warnings FATAL => 'all';
3
4use Test::More qw(no_plan);
5
6use Web::Simple::DispatchParser;
7
8my $dp = Web::Simple::DispatchParser->new;
9
10my $get = $dp->parse_dispatch_specification('GET');
11
12is_deeply(
13 [ $get->({ REQUEST_METHOD => 'GET' }) ],
14 [ {} ],
15 'GET matches'
16);
17
18is_deeply(
19 [ $get->({ REQUEST_METHOD => 'POST' }) ],
20 [],
21 'POST does not match'
22);
23
24ok(
25 !eval { $dp->parse_dispatch_specification('GET POST'); 1; },
26 "Don't yet allow two methods"
27);
28
29my $html = $dp->parse_dispatch_specification('.html');
30
31is_deeply(
32 [ $html->({ PATH_INFO => '/foo/bar.html' }) ],
33 [ { PATH_INFO => '/foo/bar' } ],
34 '.html matches'
35);
36
37is_deeply(
38 [ $html->({ PATH_INFO => '/foo/bar.xml' }) ],
39 [],
40 '.xml does not match .html'
41);
42
c6ea9542 43my $any_ext = $dp->parse_dispatch_specification('.*');
44
45is_deeply(
46 [ $any_ext->({ PATH_INFO => '/foo/bar.html' }) ],
47 [ { PATH_INFO => '/foo/bar' }, 'html' ],
48 '.html matches .* and extension returned'
49);
50
51is_deeply(
52 [ $any_ext->({ PATH_INFO => '/foo/bar' }) ],
53 [],
54 'no extension does not match .*'
55);
56
57
920d6222 58my $slash = $dp->parse_dispatch_specification('/');
59
60is_deeply(
61 [ $slash->({ PATH_INFO => '/' }) ],
62 [ {} ],
63 '/ matches /'
64);
65
66is_deeply(
67 [ $slash->({ PATH_INFO => '/foo' }) ],
68 [ ],
69 '/foo does not match /'
70);
71
72my $post = $dp->parse_dispatch_specification('/post/*');
73
74is_deeply(
75 [ $post->({ PATH_INFO => '/post/one' }) ],
76 [ {}, 'one' ],
77 '/post/one parses out one'
78);
79
80is_deeply(
81 [ $post->({ PATH_INFO => '/post/one/' }) ],
82 [],
83 '/post/one/ does not match'
84);
85
9e4713ab 86my $combi = $dp->parse_dispatch_specification('GET+/post/*');
920d6222 87
88is_deeply(
89 [ $combi->({ PATH_INFO => '/post/one', REQUEST_METHOD => 'GET' }) ],
90 [ {}, 'one' ],
91 '/post/one parses out one'
92);
93
94is_deeply(
95 [ $combi->({ PATH_INFO => '/post/one/', REQUEST_METHOD => 'GET' }) ],
96 [],
97 '/post/one/ does not match'
98);
99
100is_deeply(
101 [ $combi->({ PATH_INFO => '/post/one', REQUEST_METHOD => 'POST' }) ],
102 [],
103 'POST /post/one does not match'
104);
c6ea9542 105
106my $or = $dp->parse_dispatch_specification('GET|POST');
107
108foreach my $meth (qw(GET POST)) {
109
110 is_deeply(
111 [ $or->({ REQUEST_METHOD => $meth }) ],
112 [ {} ],
113 'GET|POST matches method '.$meth
114 );
115}
116
117is_deeply(
118 [ $or->({ REQUEST_METHOD => 'PUT' }) ],
119 [],
120 'GET|POST does not match PUT'
121);
da9b9236 122
123$or = $dp->parse_dispatch_specification('GET|POST|DELETE');
124
125foreach my $meth (qw(GET POST DELETE)) {
126
127 is_deeply(
128 [ $or->({ REQUEST_METHOD => $meth }) ],
129 [ {} ],
130 'GET|POST|DELETE matches method '.$meth
131 );
132}
133
134is_deeply(
135 [ $or->({ REQUEST_METHOD => 'PUT' }) ],
136 [],
137 'GET|POST|DELETE does not match PUT'
138);
b0420ad6 139
140my $nest = $dp->parse_dispatch_specification('(GET+/foo)|POST');
141
142is_deeply(
143 [ $nest->({ PATH_INFO => '/foo', REQUEST_METHOD => 'GET' }) ],
144 [ {} ],
145 '(GET+/foo)|POST matches GET /foo'
146);
147
148is_deeply(
149 [ $nest->({ PATH_INFO => '/bar', REQUEST_METHOD => 'GET' }) ],
150 [],
151 '(GET+/foo)|POST does not match GET /bar'
152);
153
154is_deeply(
155 [ $nest->({ PATH_INFO => '/bar', REQUEST_METHOD => 'POST' }) ],
156 [ {} ],
157 '(GET+/foo)|POST matches POST /bar'
158);
159
160is_deeply(
161 [ $nest->({ PATH_INFO => '/foo', REQUEST_METHOD => 'PUT' }) ],
162 [],
163 '(GET+/foo)|POST does not match PUT /foo'
164);
a4ec359d 165
166{
167 local $@;
168 ok(
169 !eval { $dp->parse_dispatch_specification('/foo+(GET'); 1 },
170 'Death with missing closing )'
171 );
172 my $err = q{
173 /foo+(GET
174 ^
175 };
176 (s/^\n//s,s/\n $//s,s/^ //mg) for $err;
177 like(
178 $@,
179 qr{\Q$err\E},
180 "Error $@ matches\n${err}\n"
181 );
182}
2ee4ab06 183
184my $not = $dp->parse_dispatch_specification('!.html+.*');
185
186is_deeply(
187 [ $not->({ PATH_INFO => '/foo.xml' }) ],
188 [ { PATH_INFO => '/foo' }, 'xml' ],
189 '!.html+.* matches /foo.xml'
190);
191
192is_deeply(
193 [ $not->({ PATH_INFO => '/foo.html' }) ],
194 [],
195 '!.html+.* does not match /foo.html'
196);
197
198is_deeply(
199 [ $not->({ PATH_INFO => '/foo' }) ],
200 [],
201 '!.html+.* does not match /foo'
202);
da8429c9 203
204my $sub = $dp->parse_dispatch_specification('/foo/*/...');
205
206is_deeply(
207 [ $sub->({ PATH_INFO => '/foo/1/bar' }) ],
208 [ { PATH_INFO => '/bar' }, 1 ],
209 '/foo/*/... matches /foo/1/bar and strips to /bar'
210);
211
212is_deeply(
213 [ $sub->({ PATH_INFO => '/foo/1/' }) ],
214 [ { PATH_INFO => '/' }, 1 ],
215 '/foo/*/... matches /foo/1/bar and strips to /'
216);
217
218is_deeply(
219 [ $sub->({ PATH_INFO => '/foo/1' }) ],
220 [],
221 '/foo/*/... does not match /foo/1 (no trailing /)'
222);
a5917caa 223
224my $q = 'foo=FOO&bar=BAR1&baz=one+two&quux=QUUX1&quux=QUUX2'
225 .'&bar=BAR2&quux=QUUX3&evil=%2F';
226
227my %all_single = (
228 foo => 'FOO',
229 bar => 'BAR2',
230 baz => 'one two',
231 quux => 'QUUX3',
232 evil => '/',
233);
234
235my %all_multi = (
236 foo => [ 'FOO' ],
237 bar => [ qw(BAR1 BAR2) ],
238 baz => [ 'one two' ],
239 quux => [ qw(QUUX1 QUUX2 QUUX3) ],
240 evil => [ '/' ],
241);
242
eb9e0e25 243foreach my $lose ('?foo=','?:foo=','?@foo=','?:@foo=') {
244 my $foo = $dp->parse_dispatch_specification($lose);
a5917caa 245
eb9e0e25 246 is_deeply(
247 [ $foo->({ QUERY_STRING => '' }) ],
248 [],
249 "${lose} fails with no query"
250 );
251
252 is_deeply(
253 [ $foo->({ QUERY_STRING => 'bar=baz' }) ],
254 [],
255 "${lose} fails with query missing foo key"
256 );
257}
a5917caa 258
259foreach my $win (
eb9e0e25 260 [ '?foo=' => 'FOO' ],
261 [ '?:foo=' => { foo => 'FOO' } ],
262 [ '?spoo~' => undef ],
263 [ '?:spoo~' => {} ],
264 [ '?@spoo~' => [] ],
265 [ '?:@spoo~' => { spoo => [] } ],
266 [ '?bar=' => 'BAR2' ],
267 [ '?:bar=' => { bar => 'BAR2' } ],
268 [ '?@bar=' => [ qw(BAR1 BAR2) ] ],
269 [ '?:@bar=' => { bar => [ qw(BAR1 BAR2) ] } ],
270 [ '?foo=&@bar=' => 'FOO', [ qw(BAR1 BAR2) ] ],
271 [ '?foo=&:@bar=' => 'FOO', { bar => [ qw(BAR1 BAR2) ] } ],
272 [ '?:foo=&:@bar=' => { foo => 'FOO', bar => [ qw(BAR1 BAR2) ] } ],
273 [ '?:baz=&:evil=' => { baz => 'one two', evil => '/' } ],
a5917caa 274 [ '?*' => \%all_single ],
275 [ '?@*' => \%all_multi ],
eb9e0e25 276 [ '?foo=&@*' => 'FOO', do { my %h = %all_multi; delete $h{foo}; \%h } ],
277 [ '?:foo=&@*' => { %all_multi, foo => 'FOO' } ],
278 [ '?:@bar=&*' => { %all_single, bar => [ qw(BAR1 BAR2) ] } ],
a5917caa 279) {
eb9e0e25 280 my ($spec, @res) = @$win;
a5917caa 281 my $match = $dp->parse_dispatch_specification($spec);
282#use Data::Dump::Streamer; warn Dump($match);
283 is_deeply(
284 [ $match->({ QUERY_STRING => $q }) ],
eb9e0e25 285 [ {}, @res ],
a5917caa 286 "${spec} matches correctly"
287 );
288}