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