add support for the -and, -not and -or operators (RT93864)
[catagits/Catalyst-Controller-DBIC-API.git] / t / rest / list.t
1 use strict;
2 use warnings;
3
4 use lib 't/lib';
5
6 my $base = 'http://localhost';
7
8 use RestTest;
9 use DBICTest;
10 use URI;
11 use Test::More;
12 use Test::WWW::Mechanize::Catalyst 'RestTest';
13 use HTTP::Request::Common;
14 use JSON;
15 use Data::Printer;
16
17 my $json = JSON->new->utf8;
18
19 my $mech = Test::WWW::Mechanize::Catalyst->new;
20 ok( my $schema = DBICTest->init_schema(), 'got schema' );
21
22 my $artist_list_url          = "$base/api/rest/artist";
23 my $filtered_artist_list_url = "$base/api/rest/bound_artist";
24 my $producer_list_url        = "$base/api/rest/producer";
25 my $cd_list_url              = "$base/api/rest/cd";
26 my $track_list_url           = "$base/api/rest/track";
27
28 # test open request
29 {
30     my $req = GET(
31         $artist_list_url,
32         {
33
34         },
35         'Accept' => 'text/x-json'
36     );
37     $mech->request($req);
38     cmp_ok( $mech->status, '==', 200, 'open attempt okay' );
39     my @expected_response = map {
40         { $_->get_columns }
41     } $schema->resultset('Artist')->all;
42     my $response = $json->decode( $mech->content );
43     is_deeply(
44         $response,
45         { list => \@expected_response, success => 'true' },
46         'correct message returned'
47     );
48 }
49
50 {
51     my $uri = URI->new($artist_list_url);
52     $uri->query_form( { 'search.artistid' => 1 } );
53     my $req = GET( $uri, 'Accept' => 'text/x-json' );
54     $mech->request($req);
55     cmp_ok( $mech->status, '==', 200, 'attempt with basic search okay' );
56
57     my @expected_response = map {
58         { $_->get_columns }
59     } $schema->resultset('Artist')->search( { artistid => 1 } )->all;
60     my $response = $json->decode( $mech->content );
61     is_deeply(
62         $response,
63         { list => \@expected_response, success => 'true' },
64         'correct data returned'
65     );
66 }
67
68 {
69     my $uri = URI->new($artist_list_url);
70     $uri->query_form( { 'search.name.LIKE' => '%waul%' } );
71     my $req = GET( $uri, 'Accept' => 'text/x-json' );
72     $mech->request($req);
73     cmp_ok( $mech->status, '==', 200, 'attempt with basic search okay' );
74
75     my @expected_response = map {
76         { $_->get_columns }
77         } $schema->resultset('Artist')
78         ->search( { name => { LIKE => '%waul%' } } )->all;
79     my $response = $json->decode( $mech->content );
80     is_deeply(
81         $response,
82         { list => \@expected_response, success => 'true' },
83         'correct data returned for complex query'
84     );
85 }
86
87 {
88     my $uri = URI->new($producer_list_url);
89     my $req = GET( $uri, 'Accept' => 'text/x-json' );
90     $mech->request($req);
91     cmp_ok( $mech->status, '==', 200, 'open producer request okay' );
92
93     my @expected_response = map {
94         { $_->get_columns }
95         } $schema->resultset('Producer')->search( {}, { select => ['name'] } )
96         ->all;
97     my $response = $json->decode( $mech->content );
98     is_deeply(
99         $response,
100         { list => \@expected_response, success => 'true' },
101         'correct data returned for class with list_returns specified'
102     );
103 }
104
105 {
106     my $uri = URI->new($artist_list_url);
107     $uri->query_form( { 'search.cds.title' => 'Forkful of bees' } );
108     my $req = GET( $uri, 'Accept' => 'text/x-json' );
109     $mech->request($req);
110     cmp_ok( $mech->status, '==', 200, 'search related request okay' );
111
112     my @expected_response = map {
113         { $_->get_columns }
114         } $schema->resultset('Artist')
115         ->search( { 'cds.title' => 'Forkful of bees' }, { join => 'cds' } )
116         ->all;
117     my $response = $json->decode( $mech->content );
118     is_deeply(
119         $response,
120         { list => \@expected_response, success => 'true' },
121         'correct data returned for class with select specified'
122     );
123 }
124
125 {
126     my $uri = URI->new($artist_list_url);
127     $uri->query_form(
128         {   'search.cds.title'     => 'Forkful of bees',
129             'list_returns.0.count' => '*',
130             'as.0'                 => 'count'
131         }
132     );
133     my $req = GET( $uri, 'Accept' => 'text/x-json' );
134     $mech->request($req);
135     cmp_ok( $mech->status, '==', 200, 'search related request okay' );
136
137     my @expected_response = map {
138         { $_->get_columns }
139         } $schema->resultset('Artist')
140         ->search( { 'cds.title' => 'Forkful of bees' },
141         { select => [ { count => '*' } ], as => ['count'], join => 'cds' } )
142         ->all;
143     my $response = $json->decode( $mech->content );
144     is_deeply(
145         $response,
146         { list => \@expected_response, success => 'true' },
147         'correct data returned for count'
148     );
149 }
150
151 {
152     my $uri = URI->new($filtered_artist_list_url);
153     $uri->query_form( { 'search.artistid' => '2' } );
154     my $req = GET( $uri, 'Accept' => 'text/x-json' );
155     $mech->request($req);
156     cmp_ok( $mech->status, '==', 200, 'search related request okay' );
157     my $response          = $json->decode( $mech->content );
158     my @expected_response = map {
159         { $_->get_columns }
160     } $schema->resultset('Artist')->search( { 'artistid' => '1' } )->all;
161     is_deeply(
162         $response,
163         { list => \@expected_response, success => 'true' },
164         'correct data returned for class with setup_list_method specified'
165     );
166 }
167
168 {
169     my $uri = URI->new($cd_list_url);
170     $uri->query_form(
171         {   'search.tracks.position' => '1',
172             'search.artist.name'     => 'Caterwauler McCrae'
173         }
174     );
175     my $req = GET( $uri, 'Accept' => 'text/x-json' );
176     $mech->request($req);
177     cmp_ok( $mech->status, '==', 200, 'search multiple params request okay' );
178     my $response          = $json->decode( $mech->content );
179     my @expected_response = map {
180         { $_->get_columns }
181         } $schema->resultset('CD')->search(
182         {   'artist.name'     => 'Caterwauler McCrae',
183             'tracks.position' => 1,
184         },
185         { join => [qw/ artist tracks /], }
186         )->all;
187     is_deeply(
188         $response,
189         { list => \@expected_response, success => 'true' },
190         'correct data returned for multiple search params'
191     );
192 }
193
194 # page specified in controller config (RT#56226)
195 {
196     my $uri = URI->new($track_list_url);
197     $uri->query_form();
198     my $req = GET( $uri, 'Accept' => 'text/x-json' );
199     $mech->request($req);
200     cmp_ok( $mech->status, '==', 200, 'get first page ok' );
201     my $response          = $json->decode( $mech->content );
202     my @expected_response = map {
203         { $_->get_columns }
204     } $schema->resultset('Track')->search( undef, { page => 1, } )->all;
205     is_deeply(
206         $response,
207
208         # track does set use_json_boolean
209         { list => \@expected_response, success => JSON::true, totalcount => 15 },
210         'correct data returned for static configured paging'
211     );
212 }
213
214 # -and|-or condition
215 {
216     my @variants = (
217         # -or
218         {
219             search => {
220                 title => [qw(Yowlin Howlin)],
221             },
222         },
223         {
224             search => {
225                 -or => [
226                     title => [qw(Yowlin Howlin)],
227                 ],
228             },
229         },
230         {
231             search => {
232                 -or => [
233                     title => [qw(Yowlin)],
234                     title => [qw(Howlin)],
235                 ],
236             },
237         },
238         {
239             search => {
240                 -or => [
241                     { title => [qw(Yowlin)] },
242                     { title => [qw(Howlin)] },
243                 ],
244             },
245         },
246         # -and
247         {
248             search => {
249                 cd => 2,
250                 position => [1, 2],
251             },
252         },
253         {
254             search => {
255                 -and => [
256                     cd => 2,
257                     position => [1, 2],
258                 ],
259             },
260         },
261         # -and & -or
262         {
263             search => {
264                 -or => [
265                     -and => [
266                         cd => 2,
267                         position => [0, 1],
268                     ],
269                     -and => [
270                         cd => 2,
271                         position => [0, 2],
272                     ],
273                 ],
274             },
275         },
276         {
277             search => {
278                 -or => [
279                     {
280                         -and => [
281                             cd => 2,
282                             position => [0, 1],
283                         ],
284                     },
285                     {
286                         -and => [
287                             cd => 2,
288                             position => [0, 2],
289                         ],
290                     },
291                 ],
292             },
293         },
294         {
295             search => {
296                 -or => [
297                     {
298                         -and => [
299                             cd => 2,
300                             position => [0, 1],
301                         ],
302                     },
303                     {
304                         -and => [
305                             cd => 2,
306                             position => [0, 2],
307                         ],
308                     },
309                 ],
310             },
311         },
312     );
313
314     for my $case ( @variants ) {
315         is $schema->resultset('Track')->search($case->{search})->count, 2, 'check -and|-or search param correctness';
316
317         my $uri = URI->new($track_list_url);
318         $uri->query_form( map { $_ => encode_json($case->{$_}) } keys %$case );
319         my $req = GET( $uri, 'Accept' => 'text/x-json' );
320         $mech->request($req);
321         cmp_ok( $mech->status, '==', 200, 'attempt with -or search okay' );
322         my $response          = $json->decode( $mech->content );
323         my @expected_response = map {
324             { $_->get_columns }
325         } $schema->resultset('Track')->search($case->{search})->all;
326         is_deeply(
327             $response,
328             # track does set use_json_boolean
329             { list => \@expected_response, success => JSON::true, totalcount => 2 },
330             'correct data returned for -and|-or search param'
331         )
332             or diag p($case) . p($response);
333     }
334 }
335
336 {
337     my $uri = URI->new($artist_list_url);
338     $uri->query_form( { 'search.cds.track.title' => 'Suicidal' } );
339     my $req = GET( $uri, 'Accept' => 'text/x-json' );
340     $mech->request($req);
341     cmp_ok( $mech->status, '==', 400,
342         'attempt with nonexisting relationship fails' );
343     my $response = $json->decode( $mech->content );
344     like(
345         $response->{messages}->[0],
346         qr/unsupported value 'HASH\([^\)]+\)' for column 'track'/,
347         'correct error message returned'
348     );
349 }
350
351 {
352     my $uri = URI->new($artist_list_url);
353     $uri->query_form( { 'search.cds.tracks.foo' => 'Bar' } );
354     my $req = GET( $uri, 'Accept' => 'text/x-json' );
355     $mech->request($req);
356     cmp_ok( $mech->status, '==', 400,
357         'attempt with nonexisting column fails' );
358     my $response = $json->decode( $mech->content );
359     is_deeply(
360         $response->{messages},
361         ['a database error has occured.'],
362         'correct error message returned'
363     );
364 }
365
366 {
367     my $uri = URI->new($artist_list_url);
368     $uri->query_form( { 'search.cds.tracks.title.like' => 'Boring%' } );
369     my $req = GET( $uri, 'Accept' => 'text/x-json' );
370     $mech->request($req);
371     cmp_ok( $mech->status, '==', 200, 'attempt with sql function ok' );
372     my $response          = $json->decode( $mech->content );
373     my @expected_response = map {
374         { $_->get_columns }
375         } $schema->resultset('Artist')
376         ->search( { 'tracks.title' => { 'like' => 'Boring%' }, },
377         { join => { cds => 'tracks' }, } )->all;
378     is_deeply(
379         $response,
380
381         # artist doesn't set use_json_boolean
382         { list => \@expected_response, success => 'true' },
383         'correct data returned for search with sql function'
384     );
385 }
386
387 done_testing();