More tests and tighter code with better error reporting in collapser maker
[dbsrgits/DBIx-Class.git] / t / resultset / inflate_result_api.t
1 BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
2
3 use strict;
4 use warnings;
5 no warnings 'exiting';
6
7 use Test::More;
8 use Test::Deep;
9
10 use DBICTest;
11
12 my $schema = DBICTest->init_schema(no_populate => 1);
13
14 $schema->resultset('Artist')->create({ name => 'JMJ', cds => [{
15   title => 'Magnetic Fields',
16   year => 1981,
17   genre => { name => 'electro' },
18   tracks => [
19     { title => 'm1' },
20     { title => 'm2' },
21     { title => 'm3' },
22     { title => 'm4' },
23   ],
24 } ] });
25
26 $schema->resultset('CD')->create({
27   title => 'Equinoxe',
28   year => 1978,
29   artist => { name => 'JMJ' },
30   genre => { name => 'electro' },
31   tracks => [
32     { title => 'e1' },
33     { title => 'e2' },
34     { title => 'e3' },
35   ],
36   single_track => {
37     title => 'o1',
38     cd => {
39       title => 'Oxygene',
40       year => 1976,
41       artist => { name => 'JMJ' },
42       artwork => {},
43       tracks => [
44         { title => 'o2', position => 2},  # the position should not be needed here, bug in MC
45       ],
46     },
47   },
48 });
49
50 $schema->resultset('CD')->create({ artist => 1, year => 1977, title => "fuzzy_1" });
51
52 $schema->resultset('Artist')->create({ name => "${_}_cdless" })
53   for (qw( Z A ));
54
55 # subs at the end of the test refer to this
56 my $native_inflator;
57
58 ### TESTS START
59 # run entire test twice - with and without "native inflator"
60 INFTYPE: for ('', '(native inflator)') {
61
62   $native_inflator = $_;
63
64   cmp_structures(
65     rs_contents( $schema->resultset ('CD')->search_rs ({}, {
66       prefetch => { single_track => { cd => 'artist' } },
67       order_by => 'me.cdid',
68     }) ),
69     [
70       [
71         { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
72         { single_track => code(sub { null_branch ( \@_, [
73           { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
74           {  cd => code(sub { null_branch ( \@_, [
75             { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
76             {
77               artist => code(sub { null_branch ( \@_, [
78                 { artistid => undef, name => undef, charfield => undef, rank => undef }
79               ] ) } )
80             }
81           ] ) } ) }
82         ] ) } ) }
83       ],
84       [
85         { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
86         { single_track => code(sub { null_branch ( \@_, [
87           { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
88           {  cd => code(sub { null_branch ( \@_, [
89             { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
90             {
91               artist => code(sub { null_branch ( \@_, [
92                 { artistid => undef, name => undef, charfield => undef, rank => undef }
93               ] ) } )
94             }
95           ] ) } ) }
96         ] ) } ) }
97       ],
98       [
99         { cdid => 3, single_track => 6, artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
100         { single_track => [
101           { trackid => 6, title => 'o1', position => 1, cd => 2, last_updated_at => undef, last_updated_on => undef },
102           {  cd => [
103             { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
104             {
105               artist => [
106                 { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 }
107               ]
108             }
109           ] }
110         ] }
111       ],
112       [
113         { cdid => 4, single_track => undef, artist => 1, genreid => undef, year => 1977, title => "fuzzy_1" },
114         { single_track => code(sub { null_branch ( \@_, [
115           { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
116           {  cd => code(sub { null_branch ( \@_, [
117             { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
118             {
119               artist => code(sub { null_branch ( \@_, [
120                 { artistid => undef, name => undef, charfield => undef, rank => undef }
121               ] ) } )
122             }
123           ] ) } ) }
124         ] ) } ) }
125       ],
126     ],
127     "Simple 1:1 descend with classic prefetch $native_inflator"
128   );
129
130   cmp_structures(
131     rs_contents( $schema->resultset ('CD')->search_rs ({}, {
132       join => { single_track => { cd => 'artist' } },
133       columns => [
134         { 'year'                                    => 'me.year' },
135         { 'genreid'                                 => 'me.genreid' },
136         { 'single_track.cd.artist.artistid'         => 'artist.artistid' },
137         { 'title'                                   => 'me.title' },
138         { 'artist'                                  => 'me.artist' },
139       ],
140       order_by => 'me.cdid',
141     }) ),
142     [
143       [
144         { artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
145         { single_track => code(sub { null_branch ( \@_, [
146           undef,
147           {  cd => [
148             undef,
149             {
150               artist => [
151                 { artistid => undef }
152               ]
153             }
154           ] }
155         ] ) } ) }
156       ],
157       [
158         { artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
159         { single_track => code(sub { null_branch ( \@_, [
160           undef,
161           {  cd => [
162             undef,
163             {
164               artist => [
165                 { artistid => undef }
166               ]
167             }
168           ] }
169         ] ) } ) }
170       ],
171       [
172         { artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
173         { single_track => [
174           undef,
175           {  cd => [
176             undef,
177             {
178               artist => [
179                 { artistid => 1 }
180               ]
181             }
182           ] }
183         ] }
184       ],
185       [
186         { artist => 1, genreid => undef, year => 1977, title => "fuzzy_1" },
187         { single_track => code(sub { null_branch ( \@_, [
188           undef,
189           {  cd => [
190             undef,
191             {
192               artist => [
193                 { artistid => undef }
194               ]
195             }
196           ] }
197         ] ) } ) }
198       ],
199     ],
200     "Simple 1:1 descend with missing selectors $native_inflator",
201   );
202
203   cmp_structures(
204     rs_contents( $schema->resultset ('CD')->search_rs ({}, {
205       prefetch => [ { single_track => { cd => { artist => { cds => 'tracks' } } } } ],
206       order_by => [qw/me.cdid tracks.trackid/],
207     }) ),
208     [
209       [
210         { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
211         { single_track => code(sub { null_collapsed_branch ( \@_, [
212           { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
213           {  cd => [
214             { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
215             {
216               artist => [
217                 { artistid => undef, name => undef, charfield => undef, rank => undef },
218                 { cds => code(sub { null_collapsed_branch ( \@_, [ [
219                   { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
220                   { tracks => code(sub { null_collapsed_branch ( \@_, [ [
221                     { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
222                   ] ] ) } ) },
223                 ] ] ) } ) },
224               ],
225             },
226           ] },
227         ] ) } ) },
228       ],
229       [
230         { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
231         { single_track => code(sub { null_collapsed_branch ( \@_, [
232           { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
233           {  cd => [
234             { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
235             {
236               artist => [
237                 { artistid => undef, name => undef, charfield => undef, rank => undef },
238                 { cds => code(sub { null_collapsed_branch ( \@_, [ [
239                   { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
240                   { tracks => code(sub { null_collapsed_branch ( \@_, [ [
241                     { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
242                   ] ] ) } ) },
243                 ] ] ) } ) },
244               ],
245             },
246           ] },
247         ] ) } ) },
248       ],
249       [
250         { cdid => 3, single_track => 6, artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
251         { single_track => [
252           { trackid => 6, title => 'o1', position => 1, cd => 2, last_updated_at => undef, last_updated_on => undef },
253           {  cd => [
254             { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
255             {
256               artist => [
257                 { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
258                 { cds => [
259                   [
260                     { cdid => 4, single_track => undef, artist => 1, genreid => undef, year => 1977, title => "fuzzy_1" },
261                     { tracks => code(sub { null_collapsed_branch ( \@_, [
262                       [ { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef } ],
263                     ] ) } ) },
264                   ],
265                   [
266                     { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
267                     { tracks => [
268                       [ { trackid => 1, title => 'm1', position => 1, cd => 1, last_updated_at => undef, last_updated_on => undef } ],
269                       [ { trackid => 2, title => 'm2', position => 2, cd => 1, last_updated_at => undef, last_updated_on => undef } ],
270                       [ { trackid => 3, title => 'm3', position => 3, cd => 1, last_updated_at => undef, last_updated_on => undef } ],
271                       [ { trackid => 4, title => 'm4', position => 4, cd => 1, last_updated_at => undef, last_updated_on => undef } ],
272                     ]},
273                   ],
274                   [
275                     { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
276                     { tracks => [
277                       [ { trackid => 5, title => 'o2', position => 2, cd => 2, last_updated_at => undef, last_updated_on => undef } ],
278                       [ { trackid => 6, title => 'o1', position => 1, cd => 2, last_updated_at => undef, last_updated_on => undef } ],
279                     ]},
280                   ],
281                   [
282                     { cdid => 3, single_track => 6, artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
283                     { tracks => [
284                       [ { trackid => 7, title => 'e1', position => 1, cd => 3, last_updated_at => undef, last_updated_on => undef } ],
285                       [ { trackid => 8, title => 'e2', position => 2, cd => 3, last_updated_at => undef, last_updated_on => undef } ],
286                       [ { trackid => 9, title => 'e3', position => 3, cd => 3, last_updated_at => undef, last_updated_on => undef } ],
287                     ]},
288                   ],
289                 ]},
290               ]
291             }
292           ] }
293         ] }
294       ],
295       [
296         { cdid => 4, single_track => undef, artist => 1, genreid => undef, year => 1977, title => "fuzzy_1" },
297         { single_track => code(sub { null_collapsed_branch ( \@_, [
298           { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
299           {  cd => [
300             { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
301             {
302               artist => [
303                 { artistid => undef, name => undef, charfield => undef, rank => undef },
304                 { cds => code(sub { null_collapsed_branch ( \@_, [ [
305                   { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
306                   { tracks => code(sub { null_collapsed_branch ( \@_, [ [
307                     { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
308                   ] ] ) } ) },
309                 ] ] ) } ) },
310               ],
311             },
312           ] },
313         ] ) } ) },
314       ],
315     ],
316     "Collapsing 1:1 ending in chained has_many with classic prefetch $native_inflator",
317   );
318
319   cmp_structures (
320     rs_contents( $schema->resultset ('Artist')->search_rs ({}, {
321       join => { cds => 'tracks' },
322       '+columns' => [
323         (map { "cds.$_" } $schema->source('CD')->columns),
324         (map { +{ "cds.tracks.$_" => "tracks.$_" } } $schema->source('Track')->columns),
325       ],
326       order_by => [qw/cds.cdid tracks.trackid me.name/],
327     }) ),
328     [
329       [
330         { artistid => 3, name => 'A_cdless', charfield => undef, rank => 13 },
331         { cds => code(sub { null_branch ( \@_, [
332           { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
333           { tracks => code(sub { null_branch ( \@_, [
334             { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
335           ] ) } ) },
336         ] ) } ) },
337       ],
338       [
339         { artistid => 2, name => 'Z_cdless', charfield => undef, rank => 13 },
340         { cds => code(sub { null_branch ( \@_, [
341           { cdid => undef, single_track => undef, artist => undef, genreid => undef, year => undef, title => undef },
342           { tracks => code(sub { null_branch ( \@_, [
343             { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
344           ] ) } ) },
345         ] ) } ) },
346       ],
347       [
348         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
349         { cds => [
350           { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
351           { tracks => [
352             { trackid => 1, title => 'm1', position => 1, cd => 1, last_updated_at => undef, last_updated_on => undef },
353           ]},
354         ]},
355       ],
356       [
357         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
358         { cds => [
359           { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
360           { tracks => [
361             { trackid => 2, title => 'm2', position => 2, cd => 1, last_updated_at => undef, last_updated_on => undef },
362           ]},
363         ]},
364       ],
365       [
366         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
367         { cds => [
368           { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
369           { tracks => [
370             { trackid => 3, title => 'm3', position => 3, cd => 1, last_updated_at => undef, last_updated_on => undef },
371           ]},
372         ]},
373       ],
374       [
375         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
376         { cds => [
377           { cdid => 1, single_track => undef, artist => 1, genreid => 1, year => 1981, title => "Magnetic Fields" },
378           { tracks => [
379             { trackid => 4, title => 'm4', position => 4, cd => 1, last_updated_at => undef, last_updated_on => undef },
380           ]},
381         ]},
382       ],
383       [
384         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
385         { cds => [
386           { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
387           { tracks => [
388             { trackid => 5, title => 'o2', position => 2, cd => 2, last_updated_at => undef, last_updated_on => undef },
389           ]},
390         ]},
391       ],
392       [
393         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
394         { cds => [
395           { cdid => 2, single_track => undef, artist => 1, genreid => undef, year => 1976, title => "Oxygene" },
396           { tracks => [
397             { trackid => 6, title => 'o1', position => 1, cd => 2, last_updated_at => undef, last_updated_on => undef },
398           ]},
399         ]},
400       ],
401       [
402         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
403         { cds => [
404           { cdid => 3, single_track => 6, artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
405           { tracks => [
406             { trackid => 7, title => 'e1', position => 1, cd => 3, last_updated_at => undef, last_updated_on => undef },
407           ]},
408         ]},
409       ],
410       [
411         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
412         { cds => [
413           { cdid => 3, single_track => 6, artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
414           { tracks => [
415             { trackid => 8, title => 'e2', position => 2, cd => 3, last_updated_at => undef, last_updated_on => undef },
416           ]},
417         ]},
418       ],
419       [
420         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
421         { cds => [
422           { cdid => 3, single_track => 6, artist => 1, genreid => 1, year => 1978, title => "Equinoxe" },
423           { tracks => [
424             { trackid => 9, title => 'e3', position => 3, cd => 3, last_updated_at => undef, last_updated_on => undef },
425           ]},
426         ]},
427       ],
428       [
429         { artistid => 1, name => 'JMJ', charfield => undef, rank => 13 },
430         { cds => [
431           { cdid => 4, single_track => undef, artist => 1, genreid => undef, year => 1977, title => "fuzzy_1" },
432           { tracks => code(sub { null_branch ( \@_, [
433             { trackid => undef, title => undef, position => undef, cd => undef, last_updated_at => undef, last_updated_on => undef },
434           ] ) } ) },
435         ]},
436       ],
437     ],
438     "Non-Collapsing chained has_many $native_inflator",
439   );
440
441   cmp_structures (
442     rs_contents( $schema->resultset ('Artist')->search_rs ({}, {
443       collapse => 1,
444       join => 'cds',
445       columns => [qw( cds.title cds.artist )],
446       order_by => [qw( me.name cds.title )],
447     }) ),
448     [
449       [
450         undef,
451         { cds => code(sub { null_collapsed_branch ( \@_, [
452           [ { artist => undef, title => undef } ]
453         ] ) } ) },
454       ],
455       [
456         undef,
457         { cds => [
458           [ { artist => 1, title => "Equinoxe" } ],
459           [ { artist => 1, title => "Magnetic Fields" } ],
460           [ { artist => 1, title => "Oxygene" } ],
461           [ { artist => 1, title => "fuzzy_1" } ],
462         ] }
463       ],
464       [
465         undef,
466         { cds => code(sub { null_collapsed_branch ( \@_, [
467           [ { artist => undef, title => undef } ]
468         ] ) } ) },
469       ],
470     ],
471     "Expected output of collapsing 1:M with empty root selection $native_inflator",
472   );
473
474   cmp_structures (
475     rs_contents( $schema->resultset ('CD')->search_rs (
476       {
477         'tracks.title' => 'e2',
478         'cds.title' => 'Oxygene',
479       },
480       {
481         collapse => 1,
482         join => [
483           'tracks',
484           { single_track => { cd => 'mandatory_artwork' } },
485           { artist => { cds => 'mandatory_artwork'} },
486         ],
487         columns => {
488           cdid                                      => 'cdid',
489           'single_track.cd.mandatory_artwork.cd_id' => 'mandatory_artwork.cd_id',
490           'artist.cds.mandatory_artwork.cd_id'      => 'mandatory_artwork_2.cd_id',
491         },
492       },
493     )),
494     [[
495       { cdid => 3 },
496       {
497         single_track => [
498           undef,
499           { cd => [
500             undef,
501             { mandatory_artwork => [ { cd_id => 2 } ] }
502           ] }
503         ],
504         artist => [
505           undef,
506           { cds => [
507             [
508               undef,
509               { mandatory_artwork => [ { cd_id => 2 } ] }
510             ]
511           ] },
512         ],
513       }
514     ]],
515   );
516 }
517
518 sub null_branch {
519   cmp_deeply(
520     $_[0][0],
521     $native_inflator ? undef : bless( $_[1], $DBIx::Class::ResultSource::RowParser::Util::null_branch_class ),
522   );
523 }
524 sub null_collapsed_branch {
525   cmp_deeply(
526     $_[0][0],
527     $native_inflator ? [] : bless( $_[1], $DBIx::Class::ResultSource::RowParser::Util::null_branch_class ),
528   );
529 }
530
531 {
532   package DBICTest::_IRCapture;
533   sub inflate_result { [@_[2,3]] };
534 }
535
536 sub rs_contents {
537   my $rs = shift;
538   $rs->result_class('DBICTest::_IRCapture');
539   die 'eeeeek - preprocessed $rs' if defined $rs->{_result_inflator}{is_core_row};
540   $rs->{_result_inflator}{is_core_row} = 1 if $native_inflator;
541   [$rs->all],
542 }
543
544 sub cmp_structures {
545   my ($left, $right, $msg) = @_;
546
547   local $Test::Builder::Level = $Test::Builder::Level + 1;
548   cmp_deeply($left, $right, $msg||()) or next INFTYPE;
549 }
550
551 {
552   package DBICTest::_DoubleResult;
553
554   sub inflate_result {
555     my $class = shift;
556     return map { DBIx::Class::ResultClass::HashRefInflator->inflate_result(@_) } (1,2);
557   }
558 }
559
560 my $oxygene_rs = $schema->resultset('CD')->search({ 'me.title' => 'Oxygene' });
561
562 is_deeply(
563   [ $oxygene_rs->search({}, { result_class => 'DBICTest::_DoubleResult' })->all ],
564   [ ({ $oxygene_rs->single->get_columns }) x 2 ],
565 );
566
567 is_deeply(
568   [ $oxygene_rs->search({}, {
569     result_class => 'DBICTest::_DoubleResult', prefetch => [qw(artist tracks)],
570     order_by => [qw(me.cdid tracks.title)],
571   })->all ],
572   [ (@{$oxygene_rs->search({}, {
573     prefetch=> [qw(artist tracks)],
574     order_by => [qw(me.cdid tracks.title)],
575   })->all_hri}) x 2 ],
576 );
577
578 done_testing;