From: <chr...@us...> - 2006-05-22 23:24:35
|
Revision: 881 Author: chromatic Date: 2006-05-22 16:24:16 -0700 (Mon, 22 May 2006) ViewCVS: http://svn.sourceforge.net/everydevel/?rev=881&view=rev Log Message: ----------- r17440@windwheel: chromatic | 2006-05-22 16:24:07 -0700 Extracted Everything::NodeBase::Workspace from Everything::NodeBase. Started porting Everything::NodeBase tests to the new style. Modified Paths: -------------- trunk/ebase/MANIFEST trunk/ebase/lib/Everything/HTML.pm trunk/ebase/lib/Everything/NodeBase.pm trunk/ebase/t/NodeBase.t Added Paths: ----------- trunk/ebase/lib/Everything/NodeBase/Workspace.pm trunk/ebase/lib/Everything/Test/ trunk/ebase/lib/Everything/Test/NodeBase.pm Property Changed: ---------------- trunk/ebase/ Property changes on: trunk/ebase ___________________________________________________________________ Name: svk:merge - a6810612-c0f9-0310-9d3e-a9e4af8c5745:/ebase/offline:17231 + a6810612-c0f9-0310-9d3e-a9e4af8c5745:/ebase/offline:17440 Modified: trunk/ebase/MANIFEST =================================================================== --- trunk/ebase/MANIFEST 2006-05-20 01:01:56 UTC (rev 880) +++ trunk/ebase/MANIFEST 2006-05-22 23:24:16 UTC (rev 881) @@ -120,8 +120,10 @@ lib/Everything/Node/Test/workspace.pm lib/Everything/Nodeball.pm lib/Everything/NodeBase.pm +lib/Everything/NodeBase/Workspace.pm lib/Everything/NodeCache.pm lib/Everything/Security.pm +lib/Everything/Test/NodeBase.pm lib/Everything/Util.pm lib/Everything/XML.pm Makefile.PL Modified: trunk/ebase/lib/Everything/HTML.pm =================================================================== --- trunk/ebase/lib/Everything/HTML.pm 2006-05-20 01:01:56 UTC (rev 880) +++ trunk/ebase/lib/Everything/HTML.pm 2006-05-22 23:24:16 UTC (rev 881) @@ -3032,7 +3032,7 @@ $query = getCGI($initializer); - $AUTH ||= new Everything::Auth($options); + $AUTH ||= Everything::Auth->new($options); ( $USER, $VARS ) = $AUTH->authUser(); Added: trunk/ebase/lib/Everything/NodeBase/Workspace.pm =================================================================== --- trunk/ebase/lib/Everything/NodeBase/Workspace.pm (rev 0) +++ trunk/ebase/lib/Everything/NodeBase/Workspace.pm 2006-05-22 23:24:16 UTC (rev 881) @@ -0,0 +1,204 @@ +=head1 Everything::NodeBase::Workspace + +Wrapper for the Everything database and cache with workspace ability. + +Copyright 2006 Everything Development Inc. + +=cut + +package Everything::NodeBase::Workspace; + +use strict; +use warnings; + +use base 'Everything::NodeBase'; + +use File::Spec; + +=head2 C<joinWorkspace> + +create the $DB-E<gt>{workspace} object if a workspace is specified. If the +sole parameter is 0, then the workspace is deleted. + +=over 4 + +=item * WORKSPACE + +workspace_id, node, or 0 for none + +=back + +=cut + +sub joinWorkspace +{ + my ( $this, $WORKSPACE ) = @_; + + delete $this->{workspace} if exists $this->{workspace}; + + return 1 unless $WORKSPACE; + + $this->getRef($WORKSPACE); + return -1 unless $WORKSPACE; + + $this->{workspace} = $WORKSPACE; + $this->{workspace}{nodes} = $WORKSPACE->getVars(); + $this->{workspace}{nodes} ||= {}; + $this->{workspace}{cached_nodes} = {}; + + 1; +} + +=head2 C<getNodeWorkspace> + +Helper funciton for getNode's workspace functionality. Given a $WHERE hash ( +field =E<gt> value, or field =E<gt> [value1, value2, value3]) return a list of +nodes in the workspace which fullfill this query + +=over 4 + +=item * $WHERE + +where hash, similar to getNodeWhere + +=item * $TYPE + +type discrimination (optional) + +=back + +=cut + +sub getNodeWorkspace +{ + my ( $this, $WHERE, $TYPE ) = @_; + my @results; + $TYPE = $this->getType($TYPE) if $TYPE; + + my $cmpval = sub { + my ( $val1, $val2 ) = @_; + + $val1 = $val1->{node_id} if eval { $val1->isa( 'Everything::Node' ) }; + $val2 = $val2->{node_id} if eval { $val2->isa( 'Everything::Node' ) }; + + $val1 eq $val2; + }; + + #we need to iterate through our workspace + foreach my $node ( keys %{ $this->{workspace}{nodes} } ) + { + my $N = $this->getNode($node); + next if $TYPE and $$N{type}{node_id} != $$TYPE{node_id}; + + my $match = 1; + foreach ( keys %$WHERE ) + { + if ( ref $$WHERE{$_} eq 'ARRAY' ) + { + my $matchor = 0; + foreach my $orval ( @{ $$WHERE{$_} } ) + { + $matchor = 1 if $cmpval->( $$N{$_}, $orval ); + } + $match = 0 unless $matchor; + } + else + { + $match = 0 unless $cmpval->( $$N{$_}, $$WHERE{$_} ); + } + } + push @results, $N if $match; + } + + \@results; +} + +=head2 C<getNode> + +This overrides C<getNode()> to allow for workspaced fetches. + +=over 4 + +=item * $node + +either the string title, node id, NODE object, or "where hash ref". The NODE +object is just for ease of use, so you can call this function without worrying +if the node thingy is an ID or object. If this is a where hash ref, this +simply does a getNodeWhere() and returns the first match only (just a quicky +way of doing a getNodeWhere()) + +=item * $ext + +extra info. If $node is a string title, this must be either a hashref to a +nodetype node, or a nodetype id. If $node is an id, $ext is optional and can +be either 'light' or 'force'. If 'light' it will retrieve only the information +from the node table (faster). If 'force', it will reload the node even if it +is cached. + +=item * $ext2 + +more extra info. If this is a "title/type" query, passing 'create' will cause +a dummy object to be created and returned if a node is not found. Using the +dummy node, you can then add or modify its fields and then do a +$NODE-E<gt>insert($USER) to insert it into the database. If you wish to create +a node even if a node of the same "title/type" exists, pass "create force". A +dummy node has a node_id of '-1'. + +If $node is a "where hash ref", this is the "order by" string that you can pass +to order the result by (you will still only get one node). + +=back + +Returns a node object if successful. undef otherwise. + +=cut + +sub getNode +{ + my ( $this, $node, $ext, $ext2 ) = @_; + warn "<$node>\n"; + return unless defined $node and $node ne ''; + + # it may already be a node + return $node if eval { $node->isa( 'Everything::Node' ) }; + warn "Not a node\n"; + + my $cache = ""; + + if ( ref $node eq 'HASH' ) + { + # This a "where" select + my $nodeArray = $this->getNodeWhere( $node, $ext, $ext2, 1 ) || []; + my $wspaceArray = $this->getNodeWorkspace( $node, $ext ); + + # the nodes we get back are unordered, must be merged with the + # workspace. Also any nodes which were in the nodearray, and the + # workspace, but not the wspace array must be removed + + my @results = ( + ( grep { !exists $this->{workspace}{nodes}{ $_->{node_id} } } + @$nodeArray ), + @$wspaceArray + ); + + return unless @results; + my $orderby = $ext2 || 'node_id'; + my $position = ( $orderby =~ /\s+desc/i ) ? -1 : 0; + @results = sort { $a->{$orderby} cmp $b->{$orderby} } @results; + return $results[$position]; + } + + my $NODE = $this->SUPER( $node, $ext, $ext2 ); + return unless $NODE; + + if (exists $this->{workspace}{nodes}{ $NODE->{node_id} } + and $this->{workspace}{nodes}{ $NODE->{node_id} } ) + { + my $WS = $NODE->getWorkspaced(); + return $WS if $WS; + } + + return $NODE; +} + +1; Property changes on: trunk/ebase/lib/Everything/NodeBase/Workspace.pm ___________________________________________________________________ Name: svn:mime-type + text/plain; charset=UTF-8 Name: svn:eol-style + native Modified: trunk/ebase/lib/Everything/NodeBase.pm =================================================================== --- trunk/ebase/lib/Everything/NodeBase.pm 2006-05-20 01:01:56 UTC (rev 880) +++ trunk/ebase/lib/Everything/NodeBase.pm 2006-05-22 23:24:16 UTC (rev 881) @@ -16,6 +16,8 @@ use Everything::DB; use Everything::Node; use Everything::NodeCache; +use Everything::NodeBase::Workspace; + use Scalar::Util 'reftype'; BEGIN @@ -108,7 +110,8 @@ =head2 C<joinWorkspace> create the $DB-E<gt>{workspace} object if a workspace is specified. If the -sole parameter is 0, then the workspace is deleted. +sole parameter is 0, then the workspace is deleted. Note that this will +re-bless the current object, if the user is in a workspace. =over 4 @@ -128,80 +131,12 @@ return 1 unless $WORKSPACE; - $this->getRef($WORKSPACE); - return -1 unless $WORKSPACE; - $this->{workspace} = $WORKSPACE; - $this->{workspace}{nodes} = $WORKSPACE->getVars; - $this->{workspace}{nodes} ||= {}; - $this->{workspace}{cached_nodes} = {}; + # XXX - ugly workaround; fix soon + bless $this, 'Everything::NodeBase::Workspace'; - 1; + $this->joinWorkspace( $WORKSPACE ); } -=head2 C<getNodeWorkspace> - -Helper funciton for getNode's workspace functionality. Given a $WHERE hash ( -field =E<gt> value, or field =E<gt> [value1, value2, value3]) return a list of -nodes in the workspace which fullfill this query - -=over 4 - -=item * $WHERE - -where hash, similar to getNodeWhere - -=item * $TYPE - -type discrimination (optional) - -=back - -=cut - -sub getNodeWorkspace -{ - my ( $this, $WHERE, $TYPE ) = @_; - my @results; - $TYPE = $this->getType($TYPE) if $TYPE; - - my $cmpval = sub { - my ( $val1, $val2 ) = @_; - - $val1 = $val1->{node_id} if eval { $val1->isa( 'Everything::Node' ) }; - $val2 = $val2->{node_id} if eval { $val2->isa( 'Everything::Node' ) }; - - $val1 eq $val2; - }; - - #we need to iterate through our workspace - foreach my $node ( keys %{ $this->{workspace}{nodes} } ) - { - my $N = $this->getNode($node); - next if $TYPE and $$N{type}{node_id} != $$TYPE{node_id}; - - my $match = 1; - foreach ( keys %$WHERE ) - { - if ( ref $$WHERE{$_} eq 'ARRAY' ) - { - my $matchor = 0; - foreach my $orval ( @{ $$WHERE{$_} } ) - { - $matchor = 1 if $cmpval->( $$N{$_}, $orval ); - } - $match = 0 unless $matchor; - } - else - { - $match = 0 unless $cmpval->( $$N{$_}, $$WHERE{$_} ); - } - } - push @results, $N if $match; - } - - \@results; -} - =head2 C<rebuildNodetypeModules> Call this to account for any new nodetypes that may have been installed. @@ -372,40 +307,10 @@ if ( ref $node eq 'HASH' ) { - # This a "where" select my $nodeArray = $this->getNodeWhere( $node, $ext, $ext2, 1 ) || []; - if ( exists $this->{workspace} ) - { - my $wspaceArray = $this->getNodeWorkspace( $node, $ext ); - - #the nodes we get back are unordered, must be merged - #with the workspace. Also any nodes which were in the - #nodearray, and the workspace, but not the wspace array - #must be removed - - my @results = ( - ( - grep { !exists $this->{workspace}{nodes}{ $_->{node_id} } } - @$nodeArray - ), - @$wspaceArray - ); - - return unless @results; - my $orderby = $ext2 || 'node_id'; - - my $position = ( $orderby =~ /\s+desc/i ) ? -1 : 0; - - @results = sort { $a->{$orderby} cmp $b->{$orderby} } @results; - return $results[$position]; - return shift @results; - } - else - { - return $nodeArray->[0] if @$nodeArray; - return; - } + return $nodeArray->[0] if @$nodeArray; + return; } elsif ( $node =~ /^\d+$/ ) { @@ -450,16 +355,8 @@ return unless $NODE; - $NODE = Everything::Node->new( $NODE, $this, $cache ); + return Everything::Node->new( $NODE, $this, $cache ); - if ( exists $this->{workspace} - and exists $this->{workspace}{nodes}{ $NODE->{node_id} } - and $this->{workspace}{nodes}{ $NODE->{node_id} } ) - { - my $WS = $NODE->getWorkspaced(); - return $WS if $WS; - } - return $NODE; } Added: trunk/ebase/lib/Everything/Test/NodeBase.pm =================================================================== --- trunk/ebase/lib/Everything/Test/NodeBase.pm (rev 0) +++ trunk/ebase/lib/Everything/Test/NodeBase.pm 2006-05-22 23:24:16 UTC (rev 881) @@ -0,0 +1,668 @@ +package Everything::Test::NodeBase; + +use strict; +use warnings; + +use base 'Test::Class'; + +use Test::More; +use Test::MockObject; +use Test::MockObject::Extends; + +use Scalar::Util 'blessed'; + +sub module_class +{ + my $self = shift; + my $name = blessed( $self ); + $name =~ s/Test:://; + return $name; +} + +sub startup :Test( startup => 3 ) +{ + my $self = shift; + my $module = $self->module_class(); + use_ok( $module ) or exit; + + can_ok( $module, 'new' ); + + $self->reset_mock_nb(); + + isa_ok( $self->{nb}, $module ); +} + +sub make_fixture :Test( setup ) +{ + my $self = shift; + my $storage = Test::MockObject->new(); + $self->reset_mock_nb(); + + $self->{storage} = $storage; + $self->{nb}{storage} = $storage; + $self->{errors} = []; +} + +sub reset_mock_nb +{ + my $self = shift; + my $module = $self->module_class(); + + my $mock_db = Test::MockObject->new(); + $mock_db->set_false(qw( getNodeByIdNew getNodeByName )) + ->set_true(qw( databaseConnect buildNodetypeModules )) + ->fake_module( 'Everything::DB::fake_db', 'new', sub { $mock_db }); + + my $nb = $module->new( '', 0, 'fake_db' ); + $self->{nb} = Test::MockObject::Extends->new( $nb ); +} + +sub test_new +{ + # check $db param + # check presence of NodeCache + # check dbname + # check staticNodetypes field + # check storage + # check nodetypeModules + # check if setting type exists + # - check cache settings + # - check cache size +} + +BEGIN +{ + for my $method (qw( + buildNodetypeModules getDatabaseHandle sqlDelete sqlSelect + sqlSelectJoined sqlSelectMany sqlSelectHashref sqlUpdate sqlInsert + _quoteData sqlExecute getNodeByIdNew getNodeByName constructNode + selectNodeWhere getNodeCursor countNodeMatches getAllTypes + dropNodeTable quote genWhereString + )) + { + eval <<" END_SUB"; + sub test_$method :Test( 3 ) + { + my \$self = shift; + my \$nb = \$self->{nb}; + my \$storage = \$self->{storage}; + + \$storage->set_always( $method => 'proxied_$method' ); + can_ok( \$nb, '$method' ); + + my \$result = \$nb->$method(); + + is( \$storage->next_call(), '$method', + '$method should proxy to storage method' ); + is( \$result, 'proxied_$method', '... returning result' ); + } + END_SUB + } +} + +sub test_get_type :Test( 9 ) +{ + my $self = shift; + my $nb = $self->{nb}; + + is( $nb->getType(), undef, + 'getType() should return unless passed a node thing' ); + + is( $nb->getType( '' ), undef, '... or if it is empty' ); + + my $mock_node = Test::MockObject::Extends->new( 'Everything::Node' ); + + is( $nb->getType( $mock_node ), $mock_node, + '... returning node if it is a node' ); + + $nb->set_series( getNode => 'name', 'id' ); + + is( $nb->getType( 'name' ), 'name', + '... returning fetched node, if named' ); + + my ( $method, $args ) = $nb->next_call(); + is( join( '-', @$args ), "$nb-name-1", '... by name for nodetype' ); + + is( $nb->getType( 12345 ), 'id', + '... returning node for positive node_id' ); + + ( $method, $args ) = $nb->next_call(); + is( join( '-', @$args ), "$nb-12345", '... by id alone' ); + + is( $nb->getType( 0 ), undef, '... returning nothing for zero id' ); + is( $nb->getType( -1 ), undef, '... or for negative node_id' ); +} + +1; +__END__ + +can_ok( $package, 'getAllTypes' ); +my @list = ( 1 .. 3 ); +$mock_storage->set_series( sqlSelectMany => undef, $mock_storage ) + ->mock( fetchrow => sub { return shift @list if @list; return; } ) + ->set_series( getNode => 'a', 'b', 'c' ) + ->set_true('finish') + ->clear(); + +ok( !getAllTypes($mock), 'getAllTypes() should return without a cursor' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'sqlSelectMany', '... selecting several rows' ); +is( + join( '-', @$args ), + "$mock_storage-node_id-node-type_nodetype=1", + '... node_ids of nodetype nodes from node table' +); +my @result = getAllTypes($mock); +is_deeply( \@result, [qw( a b c )], '... returning fetched nodes in order' ); + +can_ok( $package, 'getFields' ); +$mock->set_always( getFieldsHash => 'gfh' )->clear(); + +$result = getFields( $mock, 'table' ); + +( $method, $args ) = $mock->next_call(); +is( $method, 'getFieldsHash', 'getFields() should call getFieldsHash()' ); +is( join( '-', @$args ), "$mock-table-0", '... passing table name' ); +is( $result, 'gfh', '... returning results' ); + +can_ok( $package, 'dropNodeTable' ); +{ + local *Everything::printLog; + my @log; + *Everything::printLog = sub { + push @log, [@_]; + }; + + # lots of nodroppables, but testing them all is tedious + ok( !dropNodeTable( $mock, 'container' ), + 'dropNodeTable() should fail if attempting to drop core table' ); + like( $le[0][1], qr/core table 'container'!/, '... logging an error' ); + + $mock_storage->set_series( tableExists => 0, 1 ) + ->set_always( genTableName => 'tname' ) + ->set_always( do => 'done' ) + ->clear(); + + ok( !dropNodeTable( $mock, 'zapit' ), '... failing unless table exists' ); + + ( $method, $args ) = $mock_storage->next_call(); + is( $method, 'tableExists', '... so should check' ); + is( $args->[1], 'zapit', '... with passed table name' ); + + $mock_storage->{dbh} = $mock_storage; + + $result = dropNodeTable( $mock, 'zapit' ); + like( + $log[0][0], + qr/Dropping table 'zapit'/, + '... should log the drop, if attempted' + ); + + ( $method, $args ) = $mock_storage->next_call(2); + is( $method, 'genTableName', '... generating table name' ); + is( $args->[1], 'zapit', '... from passed name' ); + + ( $method, $args ) = $mock_storage->next_call(); + is( $method, 'do', '... performing a SQL call' ); + is( $args->[1], 'drop table tname', '... dropping table' ); + + is( $result, 'done', '... returning result' ); +} + +can_ok( $package, 'quote' ); +$mock_storage->set_always( quote => 'quoted' )->clear(); +$result = quote( $mock, 'quoteme' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'quote', 'quote() should call DB quote()' ); +is( $args->[1], 'quoteme', '... on passed string' ); +is( $result, 'quoted', '... returning results' ); + +# this interface sucks. Really sucks. +can_ok( $package, 'getRef' ); + +$mock->set_series( getNode => 'first', 'second', 'not third' )->clear(); + +my ( $first, $second, $third, $u ) = ( 1, 2, bless {}, 'Everything::Node' ); +$result = getRef( $mock, $first, $second, $third, $u ); +is( $first, 'first', 'getRef() should modify references in place' ); +is( $second, 'second', '... for all passed in node_ids' ); +ok( $third->isa( 'Everything::Node' ), '... not mangling existing nodes' ); +is( $u, undef, '... skipping undefined values' ); +is( $result, 'first', '... returning node of first element' ); + +can_ok( $package, 'getId' ); +is( getId(), undef, 'getId() should return without node id' ); +my $node = bless { node_id => 11 }, 'Everything::Node'; +is( getId( $mock, $node ), 11, '... returning node_id of node, if provided' ); +is( getId( $mock, 12 ), 12, '... or node_id, if a number' ); +is( getId( $mock, -13 ), -13, '... or an integer' ); +is( getId( $mock, 'foo' ), undef, '... but undef not an integer' ); + +can_ok( $package, 'hasPermission' ); +$mock->set_series( getNode => 0, { code => 'return 1' } )->clear(); +{ + local *Everything::Security::checkPermissions; + + my @cp; + *Everything::Security::checkPermissions = sub { + push @cp, [@_]; + return 'cp'; + }; + + $result = hasPermission( $mock, 'u', 'p', 'm' ); + + ( $method, $args ) = $mock->next_call(); + is( $method, 'getNode', 'checkPermission() should fetch permission node' ); + is( join( '-', @$args ), + "$mock-p-permission", '... by identifier and type' ); + is( $result, 0, '... returning false without that node' ); + + $result = hasPermission( $mock, 'u', 'p', 'm' ); + is( @cp, 1, '... should check permissions with a perm node' ); + is( join( '-', @{ $cp[0] } ), + '1-m', '... with permissions results and mode' ); + is( $result, 'cp', '... returning results' ); +} + +can_ok( $package, 'joinWorkspace' ); +can_ok( $package, 'joinWorkspace' ); +can_ok( $package, 'buildNodetypeModules' ); + +$mock_storage->set_series( sqlSelectMany => 0, $mock_storage ) + ->set_series( fetchrow_array => qw( user nodetype blah ) ) + ->set_series( loadNodetypeModule => 1, 1, 0 ); + +is( buildNodetypeModules($mock), undef, + 'buildNodetypeModules() should return with no database cursor' ); + +is_deeply( + buildNodetypeModules($mock), + { "Everything::Node::user" => 1, "Everything::Node::nodetype" => 1 }, + '... returning a hashref of available nodetype names' +); + +can_ok( $package, 'loadNodetypeModule' ); +ok( + loadNodetypeModule( $mock, 'Everything::NodeBase' ), + 'loadNodetypeModule() should return true if module is loaded' +); + +@le = (); +ok( loadNodetypeModule( $mock, 'Everything::Node::user' ), + '... or if module can be loaded' ); +ok( !loadNodetypeModule( $mock, 'Everything::Node::blah' ), + '... but false if it cannot' ); + +can_ok( $package, 'getNode' ); + +my ( @ennew, $ennew ); +$mock->set_always( getNodeByIdNew => { title => 'node by id' } ) + ->fake_new( "Everything::Node" => sub { push @ennew, [@_]; $ennew } ); +$mock->clear(); + +$ennew = { node_id => 11 }; + +isnt( getNode( $mock, 0 ), + undef, 'getNode() should return node zero given node_id of 0' ); + +exit; + +can_ok( $package, 'getNodeByName' ); +can_ok( $package, 'getNodeByIdNew' ); +can_ok( $package, 'constructNode' ); +can_ok( $package, 'getNodeCursor' ); +can_ok( $package, 'genWhereString' ); +can_ok( $package, 'getNodetypeTables' ); + +can_ok( $package, 'rebuildNodetypeModules' ); +$mock->set_always( 'buildNodetypeModules', 'bntm' ); +$mock->{nodetypeModules} = ''; +rebuildNodetypeModules($mock); +is( $mock->call_pos(-1), 'buildNodetypeModules', + 'rebuildNodetypeModules() should call buildNodetypeModules' ); +is( $mock->{nodetypeModules}, 'bntm', '... caching results' ); + +can_ok( $package, 'resetNodeCache' ); +$mock->set_true('resetCache')->{cache} = $mock; +$mock->{storage}{cache} = $mock; +resetNodeCache($mock); +is( $mock->call_pos(-1), 'resetCache', + 'resetNodeCache() should call resetCache() on cache' ); + +can_ok( $package, 'getDatabaseHandle' ); +$mock_storage->{dbh} = 'dbh'; +is( getDatabaseHandle($mock), 'dbh', 'getDatabaseHandle() should return dbh' ); + +can_ok( $package, 'getCache' ); +$mock->{cache} = 'cache'; +$mock->{storage}{cache} = 'cache'; +is( getCache($mock), 'cache', 'getCache() should return cache' ); + +can_ok( $package, 'newNode' ); +$mock->set_always( 'getType', 'gt' )->set_always( 'getNode', 'gn' )->clear(); + +$result = newNode( $mock, 'type', 'title' ); + +( $method, $args ) = $mock->next_call(); +is( $method, 'getType', 'newNode() should fetch nodetype node' ); +is( $args->[1], 'type', '... for the requested nodetype' ); + +( $method, $args ) = $mock->next_call(); +is( $method, 'getNode', '... calling getNode()' ); +is( join( '-', @$args[ 1, 2 ] ), + 'title-gt', '... with title and nodetype node' ); +is( $args->[3], 'create force', '... forcing node creation' ); + +newNode( $mock, '' ); + +( $method, $args ) = $mock->next_call(2); +like( $args->[1], qr/^dummy\d+/, + '... using a dummy title if none is provided' ); + +can_ok( $package, 'getNodeZero' ); +$mock->{nodezero} = 'ZERO'; +is( getNodeZero($mock), 'ZERO', + 'getNodeZero() should return cached node if it exists' ); +delete $mock->{nodezero}; +my $zero = {}; +$mock->set_series( 'getNode', $zero, 'author_user' )->clear(); +$result = getNodeZero($mock); +is( $result, $zero, '... and should cache node if it must be created' ); + +( $method, $args ) = $mock->next_call(); +is( $method, 'getNode', '... fetching a node' ); +is( join( '-', @$args ), "$mock-/-location-create force", + '... forcing creation of the root location' ); + +( $method, $args ) = $mock->next_call(); +is( $method, 'getNode', '... fetching another node' ); +is( join( '-', @$args ), "$mock-root-user", '... the root user' ); +is_deeply( + $zero, + { + node_id => 0, + author_user => 'author_user', + guestaccess => '-----', + otheraccess => '-----', + groupaccess => '-----', + }, + '... and zero node attributes should be set correctly' +); +is( $mock->{nodezero}, $result, '... and node should be cached' ); + +can_ok( $package, 'getNodeWhere' ); +$mock->set_series( 'selectNodeWhere', undef, 'foo', [ 1 .. 5 ] ) + ->set_series( 'getNode', 0, 2, 0, 4, 5 )->clear(); + +my @expected = qw( where type orderby limit offset reftotalrows ); +$result = getNodeWhere( $mock, @expected ); + +( $method, $args ) = $mock->next_call(); +is( $method, 'selectNodeWhere', + 'getNodeWhere() should delegate to selectNodeWhere()' ); +is( + join( '-', @$args ), + join( '-', $mock, @expected ), + '... passing most args' +); +is( $result, undef, '... returning if it fails' ); +is( getNodeWhere($mock), undef, '... or if it does not return a listref' ); +$result = getNodeWhere( $mock, @expected ); +is_deeply( + $result, + [ 2, 4, 5 ], + '... fetching and returning a list ref of nodes' +); + +can_ok( $package, 'sqlDelete' ); +$mock->{storage} = $mock_storage; +$mock_storage->{dbh} = $mock_storage; +ok( !sqlDelete( $mock ), + 'sqlDelete() should return false with no where clause' ); + +$mock_storage->set_always( 'genTableName', 'table name' ) + ->set_always( 'prepare', $mock_storage ) + ->set_always( 'execute', 'executed' ) + ->clear(); + +$result = sqlDelete( $mock, 'table', 'clause', [ 'one', 'two' ] ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'genTableName', '... generating correct table name' ); +is( $args->[1], 'table', '... passing the passed table name' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'prepare', '... preparing a SQL call' ); +is( $args->[1], 'DELETE FROM table name WHERE clause', + '... with the generated name and where clause' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'execute', '... executing a SQL call' ); +is( join( '-', @$args ), "$mock_storage-one-two", '... with any bound arguments' ); +sqlDelete( $mock, 1, 2 ); +$mock_storage->called_args_string_is( -1, '-', "$mock_storage", + '... or an empty list with no bound args' ); +is( $result, 'executed', '... returning the result of the execution' ); + +can_ok( $package, 'sqlSelect' ); +my @frargs = ( [], ['one'], [ 'two', 'three' ] ); +$mock_storage->set_series( 'sqlSelectMany', undef, ($mock_storage) x 3 ) + ->mock( 'fetchrow', sub { return @{ shift @frargs } } ) + ->set_true('finish') + ->clear(); + +$result = sqlSelect( $mock, 1 .. 10 ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'sqlSelectMany', 'sqlSelect() should call sqlSelectMany()' ); +is( join( '-', @$args ), "$mock_storage-1-2-3-4-5-6-7-8-9-10", + '... passing all args' ); +ok( !$result, '... returning false if call fails' ); + +ok( !sqlSelect($mock), '... or if no rows are selected' ); +is_deeply( sqlSelect($mock), 'one', '... one item if only one is returned' ); +is_deeply( + sqlSelect($mock), + [ 'two', 'three' ], + '... and a list reference if many' +); + +can_ok( $package, 'sqlSelectJoined' ); +$mock_storage->set_always( 'genTableName', 'gentable' ) + ->set_series( 'prepare', ($mock_storage) x 2, 0 ) + ->set_series( 'execute', 1, 0 ) + ->clear(); + +my $joins = { one => 1, two => 2 }; +$result = sqlSelectJoined( $mock, 'select', 'table', $joins, 'where', 'other', + 'bound', 'values' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'genTableName', 'sqlSelectJoined() should generate table name' ); +is( $args->[1], 'table', '... if provided' ); + +for my $join ( keys %$joins ) +{ + ( $method, $args ) = $mock_storage->next_call(); + is( $method, 'genTableName', '... and genTable name' ); + is( $args->[1], $join, '... for each joined table' ); +} + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'prepare', '... preparing a SQL call' ); +like( $args->[1], qr/SELECT select/, '... selecting the requested columns' ); +like( $args->[1], qr/FROM gentable/, + '... from the generated table name if supplied' ); +like( $args->[1], qr/LEFT JOIN gentable ON 1/, + '... left joining joined tables' ); +like( $args->[1], qr/LEFT JOIN gentable ON 2/, '... as necessary' ); +like( $args->[1], qr/WHERE where/, '... adding the where clause if present' ); +like( $args->[1], qr/other/, '... and the other clause' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'execute', '... executing the query' ); +is( join( '-', @$args ), "$mock_storage-bound-values", + '... with bound values' ); + +is( $result, $mock_storage, '... returning the cursor if it executes' ); +$result = sqlSelectJoined( $mock, 'select' ); +is( $result, undef, '... or undef otherwise' ); + +( $method, $args ) = $mock_storage->next_call(1); +is( $method, 'prepare', '... not joining tables if they are not present' ); +is( $args->[1], 'SELECT select ', + '... nor any table, where, or other clauses unless requested' ); +ok( !sqlSelectJoined( $mock, 'select' ), + '... returning false if prepare fails' ); + +can_ok( $package, 'sqlSelectMany' ); +$mock_storage->set_always( 'genTableName', 'gentable' ) + ->set_series( 'prepare', 0, ($mock_storage) x 5 ) + ->set_series( 'execute', (0) x 3, 1 ) + ->unmock( 'sqlSelectMany' ) + ->clear(); + +$result = sqlSelectMany( $mock, 'sel' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'prepare', 'sqlSelectMany() should prepare a SQL statement' ); +is( $args->[1], 'SELECT sel ', '... with the selected fields' ); +sqlSelectMany( $mock, 'sel', 'tab' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'genTableName', '... generating a table name, if passed' ); + +is( ( $mock_storage->next_call() )[1]->[1], 'SELECT sel FROM gentable ', + '... using it in the SQL statement' ); +sqlSelectMany( $mock, 'sel', '', 'whe' ); + +is( ( $mock_storage->next_call(2) )[1]->[1], 'SELECT sel WHERE whe ', + '... adding a where clause if needed' ); +sqlSelectMany( $mock, 'sel', '', '', 'oth' ); + +is( ( $mock_storage->next_call(2) )[1]->[1], 'SELECT sel oth', + '... and an other clause as necessary' ); +ok( !$result, '... returning false if prepare fails' ); +is( sqlSelectMany( $mock, '' ), $mock_storage, + '... the cursor if it succeeds' ); +$mock_storage->called_args_string_is( -1, '-', "$mock_storage", + '... using no bound values by default' ); +sqlSelectMany( $mock, ('') x 4, [ 'hi', 'there' ] ); +$mock_storage->called_args_string_is( -1, '-', "$mock_storage-hi-there", + '... or any bounds passed' ); + +can_ok( $package, 'sqlSelectHashref' ); +$mock_storage->set_series( 'sqlSelectMany', 0, $mock_storage ) + ->set_always( 'fetchrow_hashref', 'hash' ) + ->set_true('finish') + ->clear(); + +$result = sqlSelectHashref( $mock, 'foo', 'bar', 'baz', 'quux', 'qiix' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'sqlSelectMany', + 'sqlSelectHashref() should call sqlSelectMany()' ); +is( join( '-', @$args ), "$mock_storage-foo-bar-baz-quux-qiix", + '... passing all args' ); +ok( !$result, '... returning false if that fails' ); +is( sqlSelectHashref($mock), 'hash', '... or a fetched hashref on success' ); + +is( $mock_storage->next_call(3), 'finish', + '... finishing the statement handle' ); + +can_ok( $package, 'sqlUpdate' ); +$mock_storage->mock( _quoteData => sub { + [ 'n', 'm', 's' ], [ '?', 1, 8 ], ['foo'] + } ) + ->set_always( 'genTableName', 'gentable' ) + ->set_always( 'sqlExecute', 'executed' ) + ->clear(); + +ok( !sqlUpdate( $mock, 'table', {} ), + 'sqlUpdate() should return false without update data' ); + +my $data = { foo => 'bar' }; +$result = sqlUpdate( $mock, 'table', $data ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, '_quoteData', '... quoting data, if present' ); +is( $args->[1], $data, '... passing in the data argument' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'genTableName', '... quoting the table name' ); +is( $args->[1], 'table', '... passing in the table argument' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'sqlExecute', '... and should execute query' ); +is( $args->[1], "UPDATE gentable SET n = ?,\nm = 1,\ns = 8", + '... with names and values quoted appropriately' ); +is_deeply( $args->[2], ['foo'], '.. and bound args as appropriate' ); + +$mock->clear(); +sqlUpdate( $mock, 'table', $data, 'where clause' ); + +( $method, $args ) = $mock_storage->next_call(3); +like( $args->[1], qr/\nWHERE where clause\n/m, + '... adding the where clause as necessary' ); + +can_ok( $package, 'sqlInsert' ); + +$data = { foo => 'bar' }; +$result = sqlInsert( $mock, 'table', $data ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, '_quoteData', 'sqlInsert() should quote data, if present' ); +is( $args->[1], $data, '... passing in the data argument' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'genTableName', '... quoting the table name' ); +is( $args->[1], 'table', '... passing in the table argument' ); + +( $method, $args ) = $mock_storage->next_call(); +is( $method, 'sqlExecute', '... and should execute query' ); +is( $args->[1], "INSERT INTO gentable (n, m, s) VALUES(?, 1, 8)", + '... with names and values quoted appropriately' ); +is_deeply( $args->[2], ['foo'], '.. and bound args as appropriate' ); + +can_ok( $package, '_quoteData' ); +$mock_storage->unmock( '_quoteData' ); + +my ( $names, $values, $bound ) = + $mock_storage->_quoteData( { foo => 'bar', -baz => 'quux' } ); +is( join( '|', sort @$names ), + 'baz|foo', '_quoteData() should remove leading minus from names' ); +ok( ( grep { /quux/ } @$values ), '... treating unquoted values literally' ); +ok( ( grep { /\?/, } @$values ), '... and using placeholders for quoted ones' ); +is( join( '|', @$bound ), 'bar', '... returning quoted values in bound arg' ); + +can_ok( $package, 'sqlExecute' ); +{ + my $log; + + local *Everything::printLog; + *Everything::printLog = sub { $log = shift }; + + $mock_storage->set_series( 'prepare', $mock_storage, 0 ) + ->set_always( 'execute', 'success' ) + ->unmock( 'sqlExecute' ) + ->clear(); + + $result = sqlExecute( $mock, 'sql here', [ 1, 2, 3 ] ); + + ( $method, $args ) = $mock_storage->next_call(); + is( $method, 'prepare', 'sqlExecute() should prepare a statement' ); + is( $args->[1], 'sql here', '... with the passed in SQL' ); + + ( $method, $args ) = $mock_storage->next_call(); + is( $method, 'execute', '... executing the statement' ); + is( join( '-', @$args ), "$mock_storage-1-2-3", + '... with bound variables' ); + is( $result, 'success', '... returning the results' ); + + @le = (); + ok( !sqlExecute( $mock, 'bad', [ 6, 5, 4 ] ), '... or false on failure' ); + is( $le[0][1], "SQL failed: bad [6 5 4]\n", + '... logging SQL and bound values as error' ); +} Property changes on: trunk/ebase/lib/Everything/Test/NodeBase.pm ___________________________________________________________________ Name: svn:mime-type + text/plain; charset=UTF-8 Name: svn:eol-style + native Modified: trunk/ebase/t/NodeBase.t =================================================================== --- trunk/ebase/t/NodeBase.t 2006-05-20 01:01:56 UTC (rev 880) +++ trunk/ebase/t/NodeBase.t 2006-05-22 23:24:16 UTC (rev 881) @@ -1,8 +1,14 @@ #! perl -use strict; -use warnings; +=cut +use Everything::Test::NodeBase; +Everything::Test::NodeBase->runtests(); + +__END__ + +=cut + use strict; use vars qw( $AUTOLOAD ); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |