Wrap dangerous Ordered operations in transactions (still needs optimisations wrt...
[dbsrgits/DBIx-Class.git] / t / 87ordered.t
1 # vim: filetype=perl
2 use strict;
3 use warnings;  
4
5 use Test::More;
6 use lib qw(t/lib);
7 use DBICTest;
8
9 use POSIX qw(ceil);
10
11 my $schema = DBICTest->init_schema();
12
13 plan tests => 1269;
14
15 my $employees = $schema->resultset('Employee');
16 $employees->delete();
17
18 foreach (1..5) {
19     $employees->create({ name=>'temp' });
20 }
21 $employees = $employees->search(undef,{order_by=>'position'});
22 ok( check_rs($employees), "intial positions" );
23
24 hammer_rs( $employees );
25
26 DBICTest::Employee->grouping_column('group_id');
27 $employees->delete();
28 foreach my $group_id (1..4) {
29     foreach (1..6) {
30         $employees->create({ name=>'temp', group_id=>$group_id });
31     }
32 }
33 $employees = $employees->search(undef,{order_by=>'group_id,position'});
34
35 foreach my $group_id (1..4) {
36     my $group_employees = $employees->search({group_id=>$group_id});
37     $group_employees->all();
38     ok( check_rs($group_employees), "group intial positions" );
39     hammer_rs( $group_employees );
40 }
41
42 my $group_3 = $employees->search({group_id=>3});
43 my $to_group = 1;
44 my $to_pos = undef;
45 # now that we have transactions we need to work around stupid sqlite
46 {
47   my @empl = $group_3->all;
48   while (my $employee = shift @empl) {
49     $employee->discard_changes;     # since we are effective shift()ing the $rs while doing this
50     $employee->move_to_group($to_group, $to_pos);
51     $to_pos++;
52     $to_group = $to_group==1 ? 2 : 1;
53   }
54 }
55 foreach my $group_id (1..4) {
56     my $group_employees = $employees->search({group_id=>$group_id});
57     $group_employees->all();
58     ok( check_rs($group_employees), "group positions after move_to_group" );
59 }
60
61 my $employee = $employees->search({group_id=>4})->first;
62 $employee->position(2);
63 $employee->update;
64 ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 1" );
65 $employee = $employees->search({group_id=>4})->first;
66 $employee->update({position=>3});
67 ok( check_rs($employees->search_rs({group_id=>4})), "overloaded update 2" );
68 $employee = $employees->search({group_id=>4})->first;
69 $employee->group_id(1);
70 $employee->update;
71 ok(
72         check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
73         "overloaded update 3"
74 );
75 $employee = $employees->search({group_id=>4})->first;
76 $employee->update({group_id=>2});
77 ok(
78         check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
79         "overloaded update 4"
80 );
81 $employee = $employees->search({group_id=>4})->first;
82 $employee->group_id(1);
83 $employee->position(3);
84 $employee->update;
85 ok(
86         check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
87         "overloaded update 5"
88 );
89 $employee = $employees->search({group_id=>4})->first;
90 $employee->group_id(2);
91 $employee->position(undef);
92 $employee->update;
93 ok(
94         check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
95         "overloaded update 6"
96 );
97 $employee = $employees->search({group_id=>4})->first;
98 $employee->update({group_id=>1,position=>undef});
99 ok(
100         check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
101         "overloaded update 7"
102 );
103
104 # multicol tests begin here
105 DBICTest::Employee->grouping_column(['group_id_2', 'group_id_3']);
106 $employees->delete();
107 foreach my $group_id_2 (1..4) {
108     foreach my $group_id_3 (1..4) {
109         foreach (1..4) {
110             $employees->create({ name=>'temp', group_id_2=>$group_id_2, group_id_3=>$group_id_3 });
111         }
112     }
113 }
114 $employees = $employees->search(undef,{order_by=>[qw/group_id_2 group_id_3 position/]});
115
116 foreach my $group_id_2 (1..3) {
117     foreach my $group_id_3 (1..3) {
118         my $group_employees = $employees->search({group_id_2=>$group_id_2, group_id_3=>$group_id_3});
119         $group_employees->all();
120         ok( check_rs($group_employees), "group intial positions" );
121         hammer_rs( $group_employees );
122     }
123 }
124
125 # move_to_group, specifying group by hash
126 my $group_4 = $employees->search({group_id_2=>4});
127 $to_group = 1;
128 my $to_group_2_base = 7;
129 my $to_group_2 = 1;
130 $to_pos = undef;
131
132 # now that we have transactions we need to work around stupid sqlite
133 {
134   my @empl = $group_3->all;
135   while (my $employee = shift @empl) {
136     $employee->move_to_group({group_id_2=>$to_group, group_id_3=>$to_group_2}, $to_pos);
137     $to_pos++;
138     $to_group = ($to_group % 3) + 1;
139     $to_group_2_base++;
140     $to_group_2 = (ceil($to_group_2_base/3.0) %3) +1
141   }
142 }
143 foreach my $group_id_2 (1..4) {
144     foreach my $group_id_3 (1..4) {
145         my $group_employees = $employees->search({group_id_2=>$group_id_2,group_id_3=>$group_id_3});
146         $group_employees->all();
147         ok( check_rs($group_employees), "group positions after move_to_group" );
148     }
149 }
150
151 $employees->delete();
152 foreach my $group_id_2 (1..4) {
153     foreach my $group_id_3 (1..4) {
154         foreach (1..4) {
155             $employees->create({ name=>'temp', group_id_2=>$group_id_2, group_id_3=>$group_id_3 });
156         }
157     }
158 }
159 $employees = $employees->search(undef,{order_by=>[qw/group_id_2 group_id_3 position/]});
160
161 $employee = $employees->search({group_id_2=>4, group_id_3=>1})->first;
162 $employee->group_id_2(1);
163 $employee->update;
164 ok( 
165     check_rs($employees->search_rs({group_id_2=>4, group_id_3=>1}))
166     && check_rs($employees->search_rs({group_id_2=>1, group_id_3=>1})), 
167     "overloaded multicol update 1" 
168 );
169
170 $employee = $employees->search({group_id_2=>4, group_id_3=>1})->first;
171 $employee->update({group_id_2=>2});
172 ok( check_rs($employees->search_rs({group_id_2=>4, group_id_3=>1}))
173     && check_rs($employees->search_rs({group_id_2=>2, group_id_3=>1})), 
174    "overloaded multicol update 2" 
175 );
176
177 $employee = $employees->search({group_id_2=>3, group_id_3=>1})->first;
178 $employee->group_id_2(1);
179 $employee->group_id_3(3);
180 $employee->update();
181 ok( check_rs($employees->search_rs({group_id_2=>3, group_id_3=>1}))
182     && check_rs($employees->search_rs({group_id_2=>1, group_id_3=>3})),
183     "overloaded multicol update 3" 
184 );
185
186 $employee = $employees->search({group_id_2=>3, group_id_3=>1})->first;
187 $employee->update({group_id_2=>2, group_id_3=>3});
188 ok( check_rs($employees->search_rs({group_id_2=>3, group_id_3=>1}))
189     && check_rs($employees->search_rs({group_id_2=>2, group_id_3=>3})), 
190     "overloaded multicol update 4" 
191 );
192
193 $employee = $employees->search({group_id_2=>3, group_id_3=>2})->first;
194 $employee->update({group_id_2=>2, group_id_3=>4, position=>2});
195 ok( check_rs($employees->search_rs({group_id_2=>3, group_id_3=>2}))
196     && check_rs($employees->search_rs({group_id_2=>2, group_id_3=>4})), 
197     "overloaded multicol update 5" 
198 );
199
200 sub hammer_rs {
201     my $rs = shift;
202     my $employee;
203     my $count = $rs->count();
204     my $position_column = $rs->result_class->position_column();
205     my $row;
206
207     foreach my $position (1..$count) {
208
209         ($row) = $rs->search({ $position_column=>$position })->all();
210         $row->move_previous();
211         ok( check_rs($rs), "move_previous( $position )" );
212
213         ($row) = $rs->search({ $position_column=>$position })->all();
214         $row->move_next();
215         ok( check_rs($rs), "move_next( $position )" );
216
217         ($row) = $rs->search({ $position_column=>$position })->all();
218         $row->move_first();
219         ok( check_rs($rs), "move_first( $position )" );
220
221         ($row) = $rs->search({ $position_column=>$position })->all();
222         $row->move_last();
223         ok( check_rs($rs), "move_last( $position )" );
224
225         foreach my $to_position (1..$count) {
226             ($row) = $rs->search({ $position_column=>$position })->all();
227             $row->move_to($to_position);
228             ok( check_rs($rs), "move_to( $position => $to_position )" );
229         }
230
231         $row = $rs->find({ position => $position });
232         if ($position==1) {
233             ok( !$row->previous_sibling(), 'no previous sibling' );
234             ok( !$row->first_sibling(), 'no first sibling' );
235             ok( $row->next_sibling->position > $position, 'next sibling position > than us');
236             is( $row->next_sibling->previous_sibling->position, $position, 'next-prev sibling is us');
237             ok( $row->last_sibling->position > $position, 'last sibling position > than us');
238         }
239         else {
240             ok( $row->previous_sibling(), 'previous sibling' );
241             ok( $row->first_sibling(), 'first sibling' );
242             ok( $row->previous_sibling->position < $position, 'prev sibling position < than us');
243             is( $row->previous_sibling->next_sibling->position, $position, 'prev-next sibling is us');
244             ok( $row->first_sibling->position < $position, 'first sibling position < than us');
245         }
246         if ($position==$count) {
247             ok( !$row->next_sibling(), 'no next sibling' );
248             ok( !$row->last_sibling(), 'no last sibling' );
249             ok( $row->previous_sibling->position < $position, 'prev sibling position < than us');
250             is( $row->previous_sibling->next_sibling->position, $position, 'prev-next sibling is us');
251             ok( $row->first_sibling->position < $position, 'first sibling position < than us');
252         }
253         else {
254             ok( $row->next_sibling(), 'next sibling' );
255             ok( $row->last_sibling(), 'last sibling' );
256             ok( $row->next_sibling->position > $row->position, 'next sibling position > than us');
257             is( $row->next_sibling->previous_sibling->position, $position, 'next-prev sibling is us');
258             ok( $row->last_sibling->position > $row->position, 'last sibling position > than us');
259         }
260
261     }
262 }
263
264 sub check_rs {
265     my( $rs ) = @_;
266     $rs->reset();
267     my $position_column = $rs->result_class->position_column();
268     my $expected_position = 0;
269     while (my $row = $rs->next()) {
270         $expected_position ++;
271         if ($row->get_column($position_column)!=$expected_position) {
272             return 0;
273         }
274     }
275     return 1;
276 }
277