root/branches/release-40/t/driver-tests.pl @ 2584

Revision 2584, 28.0 kB (checked in by mpaschal, 18 months ago)

Move basic searching tests into a test class
Move is_object(), are_object(), and table resetting code into MT::Test routines
Export is_object and are_object from MT::Test for Test::More-like behavior
BugzID: 79953

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl
2
3# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
4# This program is distributed under the terms of the
5# GNU General Public License, version 2.
6#
7# $Id$
8
9use strict;
10use warnings;
11use Data::Dumper;
12use English qw( -no_match_vars );
13
14$OUTPUT_AUTOFLUSH = 1;
15
16# Run this script as a symlink, in the form of 99-driver.t, ie:
17# ln -s driver-tests.pl 99-driver.t
18
19BEGIN {
20    # Set config to driver-test.cfg when run as /path/to/99-driver.t
21    $ENV{MT_CONFIG} = "$1-test.cfg"
22        if __FILE__ =~ m{ [\\/] \d+- ([^\\/]+) \.t \z }xms;
23}
24
25use Test::More;
26use lib 't/lib';
27
28BEGIN {
29    plan skip_all => "Configuration file $ENV{MT_CONFIG} not found"
30        if !-r "t/$ENV{MT_CONFIG}";
31}
32
33use MT::Test qw(:testdb :time);
34
35
36package Zot;
37use base 'MT::Object';
38__PACKAGE__->install_properties({
39    column_defs => {
40        'id' => 'integer not null auto_increment',
41        'x' => 'string(255)',
42    },
43    primary_key => 'id',
44    datasource => 'zot',
45});
46
47
48package Test::GroupBy;
49use base qw( Test::Class MT::Test );
50use Test::More;
51use POSIX qw(strftime);
52
53sub reset_db : Test(setup) {
54    my $self = shift;
55    $self->clean_db();
56
57    my @obj_data = (
58        { class => 'Foo',
59          id => 1,
60          name => 'foo',
61          text => 'bar',
62          status => 2, },
63        { class => 'Foo',
64          id => 2,
65          name => 'baz',
66          text => 'quux',
67          status => 1, },
68        { class => 'Bar',
69          id => 1,
70          foo_id => 2,
71          name => 'bar0',
72          status => 0, },
73        { class => 'Bar',
74          id => 2,
75          foo_id => 2,
76          name => 'bar1',
77          status => 1, },
78        { class => 'Bar',
79          id => 3,
80          foo_id => 1,
81          name => 'bar2',
82          status => 0, },
83    );
84
85    for my $data (@obj_data) {
86        my $class = delete $data->{class};
87        my $obj = $class->new;
88        $obj->set_values($data);
89        $obj->save();
90    }
91}
92
93sub count_group_by : Tests(26) {
94    # legacy way of specifying sort direction
95    my $cgb_iter = Bar->count_group_by({
96            status => '0',
97        }, {
98            group => [ 'foo_id' ],
99            sort => 'foo_id desc',
100        });
101    my ($count, $bfid, $month);
102    isa_ok($cgb_iter, 'CODE');
103    ok(($count, $bfid) = $cgb_iter->(), 'set');
104    is($bfid, 2, 'id');
105    is($count, 1, 'count4');
106    ok(($count, $bfid) = $cgb_iter->(), 'set');
107    is($bfid, 1, 'id');
108    is($count, 1, 'count5');
109    ok(!$cgb_iter->(), 'no $iter');
110
111    # new way of specifying sort direction
112    my $cgb_iter2 = Bar->count_group_by({
113            status => '0',
114        }, {
115            group => [ 'foo_id' ],
116            sort => 'foo_id',
117            direction => 'descend'
118        });
119
120    isa_ok($cgb_iter2, 'CODE');
121    ok(($count, $bfid) = $cgb_iter2->(), 'set');
122    is($bfid, 2, 'id');
123    is($count, 1, 'count4');
124    ok(($count, $bfid) = $cgb_iter2->(), 'set');
125    is($bfid, 1, 'id');
126    is($count, 1, 'count5');
127    ok(!$cgb_iter2->(), 'no $iter');
128
129    # legacy way of specifying sort direction
130    my $cgb_iter3 = Bar->count_group_by(undef, {
131            group => [ 'extract(month from created_on)' ],
132            sort => 'extract(month from created_on) desc',
133        });
134    isa_ok($cgb_iter3, 'CODE');
135    ok(($count, $month) = $cgb_iter3->(), 'set');
136    is(int($month), int(strftime("%m", localtime)), 'month');
137    is($count, 3, 'count6');
138    ok(!$cgb_iter3->(), 'no $iter');
139
140    # new way of specifying sort direction
141    my $cgb_iter4 = Bar->count_group_by(undef, {
142            group => [ 'extract(month from created_on)' ],
143            sort => [{ column => 'extract(month from created_on)',
144                desc => 'desc' }]
145        });
146    isa_ok($cgb_iter4, 'CODE');
147    ok(($count, $month) = $cgb_iter4->(), 'set');
148    is(int($month), int(strftime("%m", localtime)), 'month');
149    is($count, 3, 'count6');
150    ok(!$cgb_iter4->(), 'no $iter');
151}
152
153sub sum_group_by : Tests(7) {
154    # Sum status values across groups of ids (that is, a group for each Foo).
155    my $sgb = Foo->sum_group_by(undef, {
156        sum       => 'status',
157        group     => ['id'],
158        direction => 'ascend',
159    });
160
161    my ($status, $id) = $sgb->();
162    ok($status && $id, 'sum_group_by results had a first result');
163    is($status, 1, q{sum_group_by result #1's status is 1});
164    is($id, 2, 'sum_group_by result #1 was for Foo #2');
165   
166    ($status, $id) = $sgb->();
167    ok($status && $id, 'sum_group_by results had a second result');
168    is($status, 2, q{sum_group_by result #2's status is 2});
169    is($id, 1, 'sum_group_by result #2 was for Foo #1');
170   
171    ($status, $id) = $sgb->();
172    ok(!$status, 'sum_group_by only had two results');
173}
174
175sub avg_group_by : Tests(7) {
176    my $agb = Foo->avg_group_by(undef, {
177        avg => 'status',
178        group => ['id'],
179        direction => 'ascend',
180    });
181   
182    my ($status, $id) = $agb->();
183    ok($status && $id, 'avg_group_by results had a first result');
184    # Compare numerically; is() will compare stringwise.
185    ok($status == 1, q{avg_group_by result #1's status is 1});
186    is($id, 2, 'avg_group_by result #1 was for Foo #2');
187   
188    ($status, $id) = $agb->();
189    ok($status && $id, 'avg_group_by results had a second result');
190    # Compare numerically; is() will compare stringwise.
191    ok($status == 2, q{avg_group_by result #2's status is 2});
192    is($id, 1, 'avg_group_by result #2 was for Foo #1');
193   
194    ($status, $id) = $agb->();
195    ok(!$status, 'avg_group_by only had two results');
196}
197
198sub clean_db : Test(teardown) {
199    MT::Test->reset_table_for(qw( Foo Bar ));
200}
201
202
203package Test::Search;
204use MT::Test;
205use base qw( Test::Class MT::Test );
206
207sub make_foos : Test(setup) {
208    MT::Test->reset_table_for('Foo');
209
210    my $foo = Foo->new;
211    $foo->set_values({
212        name => 'foo',
213        status => 2,
214        text => 'bar',
215    });
216    $foo->save or die "Could not save test Foo: ", $foo->errstr, "\n";
217}
218
219sub basic : Tests(5) {
220    my $foo = Foo->load(1);  # not a search
221   
222    is_object(scalar Foo->load({ id => 1 }), $foo, 'Foo #1 by id hash is Foo #1');
223    is_object(scalar Foo->load({ id => 1, name => 'foo' }), $foo, 'Foo #1 by id-name hash is Foo #1');
224    is_object(scalar Foo->load({ name => 'foo' }), $foo, 'Foo #1 by name hash is Foo #1');
225    is_object(scalar Foo->load({ created_on => $foo->created_on }), $foo, 'Foo #1 by created_on hash is Foo #1');
226    is_object(scalar Foo->load({ status => 2 }), $foo, 'Foo #1 by status hash is Foo #1');
227}
228
229sub unmake_foos : Test(teardown) {
230    MT::Test->reset_table_for('Foo');
231}
232
233
234package main;
235use MT::Test;
236
237Test::Class->runtests(qw( Test::GroupBy Test::Search +147 ));
238
239my($foo, @foo, @bar);
240my($tmp, @tmp);
241
242# Test for existing table
243ok(MT::Object->driver->dbd->ddl_class->column_defs('Foo'), "table mt_foo exists after upgrade");
244# Test for non-existent table
245ok(!MT::Object->driver->dbd->ddl_class->column_defs('Zot'), "table mt_zot does not exist after upgrade where undefined");
246
247## Test creating object with new
248##     test column access through column, then through AUTOLOAD
249$foo = Foo->new;
250isa_ok($foo, 'Foo', 'New Foo could be created');
251$foo->column('name', 'foo');
252is($foo->column('name'), 'foo', 'Setting name field with column() persists through access');
253$foo->name('foo');
254is($foo->name, 'foo', 'Setting name field with mutator method persists through access');
255$foo->status(2);
256$foo->text('bar');
257
258## Test saving created object
259ok($foo->save, 'A Foo could be saved');
260is($foo->id, 1, 'First Foo was given an id of 1, says accessor method');
261is($foo->column('id'), 1, 'First Foo was given an id of 1, says column()');
262
263is_object(scalar Foo->load(1), $foo, 'Foo #1 by id is Foo #1');
264
265##     Change column value, save, try to load using old value (fail?),
266##     then load again using new value
267$foo->status(0);
268ok($foo->save, 'Foo #1 saved with new status (0)');
269$tmp = Foo->load({ status => 2 });
270ok(!$tmp, 'Foo #1 no longer loads with old status (2)');
271$tmp = Foo->load({ status => 0 });
272is_object($tmp, $foo, 'Foo #1 by new status (0) is Foo #1');
273
274## Create a new object so we can do range and last/first lookups.
275## Sleep first so that they get different created_on timestamps.
276sleep(3);
277
278## Create new object for iterator testing
279$foo[0] = $foo;
280$foo[1] = Foo->new;
281$foo[1]->name('baz');
282$foo[1]->text('quux');
283$foo[1]->status(1);
284$foo[1]->save;
285
286## TEST LOADING IN VARIOUS WAYS
287
288## Load all objects via iterator
289my $iter = Foo->load_iter(undef, { sort => 'created_on', direction => 'ascend' });
290isa_ok($iter, 'CODE', "Iterator for all Foos");
291ok($tmp = $iter->(), 'Iterator for our two Foos had one object');
292is_object($tmp, $foo[0], "All Foo iterator's first Foo is Foo #1");
293ok($tmp = $iter->(), 'Iterator for our two Foos had two objects');
294is_object($tmp, $foo[1], "All Foo iterator's second Foo is Foo #2");
295ok(!$iter->(), 'Iterator for our two Foos did not have a third object');
296
297## Load all objects with status == 1 via iterator
298$iter = Foo->load_iter({ status => 1 });
299isa_ok($iter, 'CODE', "Iterator for status=1 Foos");
300ok($tmp = $iter->(), 'Iterator for our status=1 Foos had one object');
301is_object($tmp, $foo[1], "Status=1 Foo iterator's first Foo is Foo #2");
302ok(!$iter->(), "Iterator for our status=1 Foos did not have a second object");
303
304## Load using non-existent ID (should fail)
305$tmp = Foo->load(3);
306ok(!$tmp, 'There is no Foo #3');
307
308## Load using descending sort (newest)
309$tmp = Foo->load(undef, {
310    sort => 'created_on',
311    direction => 'descend',
312    limit => 1 });
313is_object($tmp, $foo[1], 'Newest Foo is Foo #2');
314
315## Load using ascending sort (oldest)
316$tmp = Foo->load(undef, {
317    sort => 'created_on',
318    direction => 'ascend',
319    limit => 1 });
320is_object($tmp, $foo[0], 'Oldest Foo is Foo #1');
321
322## Load using descending sort with limit = 2
323@tmp = Foo->load(undef, {
324    sort => 'created_on',
325    direction => 'descend',
326    limit => 2 });
327are_objects(\@tmp, [ reverse @foo ], 'Two Foos newest-first load() finds Foos #2 and #1');
328
329## Load using descending sort by created_on, no limit
330@tmp = Foo->load(undef, {
331    sort => 'created_on',
332    direction => 'descend' });
333are_objects(\@tmp, [ reverse @foo ], 'All Foos newest-first load() finds Foos #2 and #1');
334
335## Load using ascending sort by status, no limit
336@tmp = Foo->load(undef, { sort => 'status', });
337are_objects(\@tmp, \@foo, 'All Foos lowest-status-first load() finds Foos #1 and #2');
338
339## Load using 'last' where status == 0
340$tmp = Foo->load({ status => 0 }, {
341    sort => 'created_on',
342    direction => 'descend',
343    limit => 1 });
344is_object($tmp, $foo[0], 'Newest status=0 Foo is Foo #1');
345
346## Load using range search, one less than foo[1]->created_on and newer
347$tmp = Foo->load(
348    { created_on => [ $foo[1]->column('created_on')-1 ] },
349    { range => { created_on => 1 } });
350is_object($tmp, $foo[1], 'Foo from open-ended date range before Foo #2 is Foo #2');
351
352## Load using EXCLUSIVE range search, up through the momment $foo[1] created
353$tmp = Foo->load(
354    { created_on => [ $foo[1]->column('created_on')-1, 
355                      $foo[1]->column('created_on') ] },
356    { range => { created_on => 1 } });
357ok(!$tmp, "Exclusive date range load() ending at Foo #1's date found no Foos");
358
359$tmp = Foo->load(
360    { created_on => [ $foo[1]->column('created_on'), 
361                      $foo[1]->column('created_on')+1 ] },
362    { range => { created_on => 1 } });
363ok(!$tmp, "Exclusive date range load() starting at Foo #1's date found no Foos");
364
365## Load using INCLUSIVE range search, up through the momment $foo[1] created
366$tmp = Foo->load(
367    { created_on => [ $foo[1]->column('created_on')-1, 
368                      $foo[1]->column('created_on') ] },
369    { range_incl => { created_on => 1 } });
370ok($tmp, 'Loaded an object based on range_incl (ts-1 to ts)');
371is_object($tmp, $foo[1], "Foo from inclusive date-range load() ending at Foo #1's date is Foo #2");
372
373$tmp = Foo->load(
374    { created_on => [ $foo[1]->column('created_on'), 
375                      $foo[1]->column('created_on')+1 ] },
376    { range_incl => { created_on => 1 } });
377ok($tmp, 'Loaded an object based on range_incl (ts to ts+1)');
378is_object($tmp, $foo[1], "Foo from inclusive date-range load() starting at Foo #1's date is Foo #2");
379
380## Check that range searches return nothing when nothing is in the range.
381$tmp = Foo->load( { created_on => [ undef, '19690101000000' ] },
382                  { range => { created_on => 1 } });
383ok(!$tmp, 'Prehistoric date range load() found no Foos');
384
385## Range search, all items with created_on less than foo[1]->created_on
386$tmp = Foo->load(
387    { created_on => [ undef, $foo[1]->column('created_on')-1 ] },
388    { range => { created_on => 1 } });
389is_object($tmp, $foo[0], "Foo from exclusive open-started date-range load() ending before Foo #1 is Foo #1");
390
391## Get count of objects
392is(Foo->count(), 2, 'Count of all Foos finds both');
393is(Foo->count({ status => 0 }), 1, 'Count of all status=0 Foos finds all one');
394my $ranged_count = Foo->count(
395    { created_on => [ $foo[1]->column('created_on')-1 ] },
396    { range => { created_on => 1 } }
397);
398is($ranged_count, 1, 'Count of all Foos in open-ended date range starting before Foo #1 finds all one');
399
400## Update status for later tests.
401$foo[0]->status(2);
402$foo[0]->save;
403
404## Test start_val loads.
405## Given the first Foo object, should load the "next" one
406## (the one with a larger created_on time)
407$tmp = Foo->load(undef, {
408    limit => 1,
409    sort => 'created_on',
410    direction => 'ascend',
411    start_val => $foo[0]->created_on });
412is_object($tmp, $foo[1], 'Next newer Foo after Foo #1 is Foo #2');
413
414## Given the first Foo object, try to load the "previous" one
415## (the one with a smaller created_on time). This should fail.
416$tmp = Foo->load(undef, {
417    limit => 1,
418    sort => 'created_on',
419    direction => 'descend',
420    start_val => $foo[0]->created_on });
421ok(!$tmp, 'Search for next older Foo before Foo #1 found none');
422
423## Given the second Foo object, try to load the "previous" one
424## (the one with a smaller created_on time). This should work.
425$tmp = Foo->load(undef, {
426    limit => 1,
427    sort => 'created_on',
428    direction => 'descend',
429    start_val => $foo[1]->created_on });
430is_object($tmp, $foo[0], 'Next older Foo before Foo #2 is Foo #1');
431
432## Given the second Foo object, try to load the "next" one
433## (the one with a larger created_on time). This should fail.
434$tmp = Foo->load(undef, {
435    limit => 1,
436    sort => 'created_on',
437    direction => 'ascend',
438    start_val => $foo[1]->created_on });
439ok(!$tmp, 'Search for next newer Foo after Foo #2 found none');
440
441## Now, given the second Foo object's created_on - 1, try to
442## load the "previous" one. This should work.
443$tmp = Foo->load(undef, {
444    limit => 1,
445    sort => 'created_on',
446    direction => 'descend',
447    start_val => $foo[1]->created_on-1 });
448is_object($tmp, $foo[0], 'Next older Foo before just before Foo #2 is Foo #1');
449
450## Now, given the second Foo object's created_on - 1, try to
451## load the "next" one. This should work.
452$tmp = Foo->load(undef, {
453    limit => 1,
454    sort => 'created_on',
455    direction => 'ascend',
456    start_val => $foo[1]->created_on-1 });
457is_object($tmp, $foo[1], 'Next newer Foo after just before Foo #2 is Foo #2');
458
459## Override created_on timestamp, make sure it works
460my $ts = substr($foo[1]->created_on, 0, -4) . '0000';
461$foo[1]->created_on($ts);
462$foo[1]->save;
463
464@tmp = Foo->load(undef, {
465    sort => 'created_on',
466    direction => 'descend',
467    limit => 2 });
468are_objects(\@tmp, \@foo, 'Time-traveled Foos newest-first are Foos #1 and #2');
469
470## Test limit of 2 with direction descend, but without
471## a sort option. This should sort by the most recently-added
472## records, ie. sorted by ID, basically.
473@tmp = Foo->load(undef, {
474    direction => 'descend',
475    limit => 2 });
476are_objects(\@tmp, [ reverse @foo ], 'Foos highest-id-first are Foos #2 and #1');
477
478## Test loading using offset.
479## Should load the second Foo object.
480$tmp = Foo->load(undef, {
481    direction => 'descend',
482    sort => 'created_on',
483    limit => 1,
484    offset => 1 });
485is_object($tmp, $foo[1], 'Second newest Foo is Foo #2');
486
487## We only have 2 Foo objects, so this should load
488## only the second Foo object (because offset is 1).
489@tmp = Foo->load(undef, {
490    direction => 'descend',
491    sort => 'created_on',
492    limit => 2,
493    offset => 1 });
494are_objects(\@tmp, [ $foo[1] ], 'Second and third newest Foos is just Foo #2');
495
496## Should load the first Foo object (ascend with offset of 1).
497$tmp = Foo->load(undef, {
498    direction => 'ascend',
499    sort => 'created_on',
500    limit => 1,
501    offset => 1 });
502is_object($tmp, $foo[0], 'Second oldest Foo is Foo #1');
503
504## Now test join loads.
505## First we need to create a couple of Bar objects.
506$bar[0] = Bar->new;
507$bar[0]->foo_id($foo[1]->id);
508$bar[0]->name('bar0');
509$bar[0]->status(0);
510ok($bar[0]->save, 'saved');
511sleep(2);  ## Sleep to ensure created_on timestamps are unique
512
513$bar[1] = Bar->new;
514$bar[1]->foo_id($foo[1]->id);
515$bar[1]->name('bar1');
516$bar[1]->status(1);
517ok($bar[1]->save, 'saved');
518sleep(2);  ## Sleep to ensure created_on timestamps are unique
519
520$bar[2] = Bar->new;
521$bar[2]->foo_id($foo[0]->id);
522$bar[2]->name('bar2');
523$bar[2]->status(0);
524ok($bar[2]->save, 'saved');
525sleep(2);  ## Sleep to ensure created_on timestamps are unique
526
527## Get a count of all Foo objects in order of most recently
528## created Bar object. No uniqueness requirement. This tests
529## the on_load_complete temporary table stuff with count.
530
531is(Foo->count(undef,
532    { join => [ 'Bar', 'foo_id',
533                undef,
534                { unique => 1,
535                  sort => 'created_on',
536                  direction => 'descend', } ] }), 2, 'There are 2 unique Foos associated with Bars');
537
538## Now load all Foo objects in order of most recently
539## created Bar object. Make sure they are unique.
540@tmp = Foo->load(undef,
541    { join => [ 'Bar', 'foo_id',
542                undef,
543                { sort => 'created_on',
544                  direction => 'descend',
545                  unique => 1 } ] });
546are_objects(\@tmp, \@foo, 'unique Foos associated with Bars, oldest first');
547
548## Load all Foo objects in order of most recently
549## created Bar object. No uniqueness requirement.
550@tmp = Foo->load(undef,
551    { join => [ 'Bar', 'foo_id',
552                undef,
553                { sort => 'created_on',
554                  direction => 'descend', } ] });
555are_objects(\@tmp, [ @foo, $foo[1] ], 'Foos associated with Bars, oldest first');
556
557## Load last 1 Foo object in order of most recently
558## created Bar object.
559@tmp = Foo->load(undef,
560    { join => [ 'Bar', 'foo_id',
561                undef,
562                { sort => 'created_on',
563                  direction => 'descend',
564                  unique => 1,
565                  limit => 1, } ] });
566are_objects(\@tmp, [ $foo[0] ], 'Foos associated with oldest Bar');
567
568## Load all Foo objects where Bar.name = 'bar0'
569@tmp = Foo->load(undef,
570    { join => [ 'Bar', 'foo_id',
571                { name => 'bar0' },
572                { sort => 'created_on',
573                  direction => 'descend',
574                  unique => 1, } ] });
575are_objects(\@tmp, [ $foo[1] ], 'Foos associated with Bars named bar0');
576
577## foo[1] is older than foo[0] because we overrode the timestamp,
578## so this should load foo[0]
579@tmp = Foo->load(undef,
580    { sort => 'created_on', direction => 'descend', limit => 1,
581    join => [ 'Bar', 'foo_id', { status => 0 }, { unique => 1 } ] });
582are_objects(\@tmp, [ $foo[0] ], 'One Foo associated with Bars of status=0');
583
584## This is the same join as the last one, but without the limit--so
585## we should get both Foo objects this time, in descending order.
586@tmp = Foo->load(undef,
587    { sort => 'created_on', direction => 'descend',
588      join => [ 'Bar', 'foo_id', { status => 0 }, { unique => 1 } ] });
589are_objects(\@tmp, \@foo, 'All Foos associated with Bars of status=0');
590
591## Filter join results by providing a value for 'status'; only Foo[0]
592## has a 'status' == 2, so only that record should be returned.
593@tmp = Foo->load({ status => 2 },
594    { sort => 'created_on', direction => 'descend',
595      join => [ 'Bar', 'foo_id', { status => 0 }, { unique => 1 } ] });
596are_objects(\@tmp, [ $foo[0] ], 'Foos of status=2 associated with Bars of status=0');
597
598# Join across a column.
599@tmp = Foo->load({},
600    { sort => 'created_on', direction => 'descend',
601      join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 0 }, { unique => 1 } ] });
602are_objects(\@tmp, \@foo, 'Foos loaded by explicit join across columns');
603
604@tmp = Foo->load({ status => 2 },
605    { sort => 'created_on', direction => 'descend',
606      join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 0 }, { unique => 1 } ] });
607are_objects(\@tmp, [ $foo[0] ], 'Foos of status=2 loaded by explicit join across columns');
608
609## TEST EXISTS METHOD
610ok($foo->exists, 'First Foo long saved exists in db');
611$tmp = Foo->new;
612ok(!$tmp->exists, 'New Foo just created does not exist in db');
613$tmp->id(5);
614ok(!$tmp->exists, 'New Foo just created with fake id does not exist in db');
615
616## Change foo[1]->status so that its value is unique (for index)
617$foo[1]->status(5);
618ok($foo[1]->save, 'saved');
619ok(Foo->load({ status => 5 }), 'loaded' );
620
621## Test remove
622ok($foo[1]->remove, 'removed');
623ok(! Foo->load(2), 'not loaded');
624ok(! Foo->load({ status => 5 }), 'not loaded');
625ok(! Foo->load({ name => $foo[1]->name }), 'not loaded');
626ok(! Foo->load({ created_on => $foo[1]->created_on }), 'not loaded');
627
628## Test methods:
629##     * properties
630my $props1 = Foo->properties;
631is($props1->{audit}, 1, 'audit');
632is(scalar keys %{ $props1->{indexes} }, 3, 'indexes');
633is($props1->{primary_key}, 'id', 'id');
634is(scalar @{ $props1->{columns} }, 9, 'columns');
635my $props2 = $foo->properties;
636is($props1, $props2, "$props1 is $props2");  ## Same address, because same hashref
637
638##     * column_names
639my $cols = $foo->column_names;
640isa_ok($cols, 'ARRAY');
641my %cols = map { $_ => 1 } @$cols;
642for (qw(id name status text data created_on created_by modified_on modified_by)) {
643    ok($cols{$_}, 'cols');
644}
645
646##     * column_values
647my $vals = $foo->column_values;
648isa_ok($vals, 'HASH');
649is($vals->{id}, $foo->id, 'id');
650is($vals->{name}, $foo->name, 'name');
651is($vals->{status}, $foo->status, 'status');
652is($vals->{text}, $foo->text, 'text');
653is($vals->{created_on}, $foo->created_on, 'created_on');
654is($vals->{created_by}, $foo->created_by, 'created_by');
655is($vals->{modified_on}, $foo->modified_on, 'modified_on');
656is($vals->{modified_by}, $foo->modified_by, 'modified_by');
657
658##     * set_values
659$vals = {
660    id => 5,
661    name => 'baz',
662    status => 7,
663    text => 'quux',
664    created_on => 13209,
665    created_by => 'bar',
666    #modified_on => 39023, modified_by auto-set modified_on in our new code.
667    modified_by => 'foo',
668};
669$foo->set_values($vals);
670for my $col (keys %$vals) {
671    is($vals->{$col}, $foo->column($col), $col);
672}
673
674##     * binary data
675
676my $binmonster = Foo->new;
677
678$vals = {
679    funky => "yes",
680    monkey => "no",
681};
682
683require MT::Serialize;
684my $srlzr = MT::Serialize->new('MT');
685$binmonster->data($srlzr->serialize(\$vals));
686my $x = $binmonster->save();
687warn 'Failed binary data test: ' . $binmonster->errstr() unless $x;
688ok($x, 'saved');
689ok($binmonster->id, 'id');
690Foo->driver->clear_cache if Foo->driver->can('clear_cache');
691my $chk = Foo->load($binmonster->id);
692if ($chk) {
693    my $chk_data = $chk->data;
694    my $chk_vals = $srlzr->unserialize($chk_data);
695    foreach (keys %$vals) {
696        is($$chk_vals->{$_}, $vals->{$_}, $_);
697    }
698} else {
699    foreach (keys %$vals) {
700        ok(0, $_);
701    }
702}
703
704##     * datasource
705is($foo->table_name, 'mt_' . $foo->datasource, 'datasource');
706
707##     * clone
708my $clone = $foo->clone_all;
709for my $col (@$cols) {
710    is($clone->column($col), $foo->column($col), $col);
711}
712
713## Sleep first so that they get different created_on timestamps.
714sleep(3);
715
716Foo->set_by_key({name => "this"});
717my $obj = Foo->load({name => "this"});
718isa_ok($obj, 'Foo');
719
720Foo->set_by_key({name => "this"}, {status => 42});
721$obj = Foo->load({name => "this"});
722is($obj && $obj->status, 42, 'status');
723
724Foo->set_by_key({name => "this"}, {status => 47});
725$obj = Foo->load({name => "this"});
726is($obj && $obj->status, 47, 'status');
727
728Foo->set_by_key({name => "this", status => 47}, {text => "spiffy"});
729$obj = Foo->load({name => "this", status => 47});
730is($obj && $obj->text, 'spiffy', 'text');
731
732sleep(3);
733
734Foo->set_by_key({name => "that"}, {text => "Once"});
735$obj = Foo->load({name => "that"});
736is($obj && $obj->text, 'Once', 'text');
737
738Foo->driver->clear_cache if Foo->driver->can('clear_cache');
739## Load use direct set of values for non-PK column
740@tmp = Foo->load({ name => [qw(foo this)] });
741@tmp = sort {$a->name cmp $b->name} @tmp;
742is(@tmp, 2, 'array length 2');
743
744is(Foo->count(), 4, 'check number of Foos');
745
746## check offsets without limits
747## Should load the third and fourth Foo objects.
748my $foo4 = Foo->load({name => "this"});
749my $foo5 = Foo->load({name => "that"});
750my $foo1 = Foo->load(undef, { 'sort' => 'created_on', 'direction' => 'ascend' });
751my @offs = Foo->load(undef, {
752    direction => 'ascend',
753    sort => 'created_on',
754    offset => 2 });
755is(@offs, 2, 'array length 2');
756isa_ok($offs[0], 'Foo');
757is($offs[0]->id, $foo4->id, 'id');
758isa_ok($offs[1], 'Foo');
759is($offs[1]->id, $foo5->id, 'id');
760
761## Should load the third and fourth Foo objects.
762@offs = Foo->load(undef, {
763    direction => 'descend',
764    sort => 'created_on',
765    offset => 1 });
766is(@offs, 3, 'array length 3');
767isa_ok($offs[0], 'Foo');
768is($offs[0]->id, $foo4->id, 'id');
769isa_ok($offs[1], 'Foo');
770is($offs[1]->id, $binmonster->id, 'id');
771isa_ok($offs[2], 'Foo');
772is($offs[2]->id, $foo1->id, 'id');
773
774# TODO: what are these even about?
775SKIP: {
776    skip(1, '$tmp[0] undefined') unless $tmp[0];
777    ok($tmp[0] && ($tmp[0]->name eq 'foo'), 'name')
778}
779SKIP: {
780    skip(1, '$tmp[1] undefined') unless $tmp[1];
781    ok($tmp[1] && ($tmp[1]->name eq 'this'), 'name');
782}
783
784# -or
785my $newdata = Foo->new;
786$newdata->status(11);
787$newdata->name('Apple');
788$newdata->text('MacBook');
789$newdata->save;
790$newdata = Foo->new;
791$newdata->status(12);
792$newdata->name('Linux');
793$newdata->text('Ubuntu');
794$newdata->save;
795$newdata = Foo->new;
796$newdata->status(13);
797$newdata->name('Microsoft');
798$newdata->text('Vista');
799$newdata->save;
800$newdata = Foo->new;
801$newdata->status(10);
802$newdata->name('Microsoft');
803$newdata->text('XP');
804$newdata->save;
805$newdata = Foo->new;
806$newdata->status(10);
807$newdata->name('Apple');
808$newdata->text('iBook');
809$newdata->save;
810
811my $count = Foo->count( [{status => 10}, -or => {name => 'Apple'}] );
812# ==> select count(*) from mt_foo where foo_status = 10 or foo_name = 'Apple'
813is($count, 3, '-or count');
814
815$count = Foo->count( [ { status => { '<=' => 20 }, name => 'Apple' }, -and_not => { status => 11 } ] );
816# ==> select count(*) from mt_foo where (foo_status <= 20 and foo_name = 'Apple') and not (foo_status = 11)
817is($count, 1, '-and_not count');
818
819$count = Foo->count( [
820    { status => 10 },
821    -or => { name => 'Apple' },
822    -or => { name => { like => '%nux' } },
823] );
824# ==> select count(*) from mt_foo where (foo_status = 10) or (foo_name = 'Apple') or (foo_name like '%nux')
825# (selects Apple+MacBook, Apple+iBook, Microsoft+XP, Linux+Ubuntu)
826is($count, 4, '-or count, 3 clauses');
827
828# alias support
829my $vista = Foo->load({name=>'Microsoft', status=>13});
830my $newbar = Bar->new;
831$newbar->foo_id($vista->id);
832$newbar->name('Silverlight');
833$newbar->status(2);
834$newbar->save;
835sleep(3);
836$newbar = Bar->new;
837$newbar->foo_id($vista->id);
838$newbar->name('IronPython');
839$newbar->status(3);
840$newbar->save;
841sleep(3);
842my $mb = Foo->load({name=>'Apple', status=>11});
843$newbar = Bar->new;
844$newbar->foo_id($mb->id);
845$newbar->name('IronRuby');
846$newbar->status(0);
847$newbar->save;
848
849# select * from foo, bar bar1, bar bar2
850# where bar1.bar_foo_id = foo_id
851# and bar2.bar_foo_id = bar1.bar_foo_id
852# and bar1.status = 2
853# and bar2.status = 3
854my @a_foos = Foo->load(
855    undef,
856    { join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 2 },
857        { join => [ 'Bar', undef, { foo_id => \'= bar1.bar_foo_id', status => 3 },
858            { alias => 'bar2' } ],
859          alias => 'bar1'
860        }
861      ],
862      sort => 'created_on', direction => 'descend',
863    }
864);
865is(scalar(@a_foos), 1, 'join the same table using alias 1');
866is($a_foos[0]->id, $vista->id, 'join the same table using alias 2');
867
868@a_foos = Foo->load(
869    undef,
870    { join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 2 },
871        { join => [ 'Bar', undef, { foo_id => \'= bar1.bar_foo_id', status => 0 },
872            { alias => 'bar2' } ],
873          alias => 'bar1'
874        }
875      ],
876      sort => 'created_on', direction => 'descend',
877    }
878);
879is(scalar(@a_foos), 0, 'join the same table using alias 3');
880 
881
8821;
Note: See TracBrowser for help on using the browser.