fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-01-04 12:22 am

[dw-free] Routing should output text/plain errors for unknown ( and non-HTML ) formats

[commit: http://hg.dwscoalition.org/dw-free/rev/2ab32ff63973]

http://bugs.dwscoalition.org/show_bug.cgi?id=3171

Deprecate checkfriends; add new protocol method checkforupdates. See http
://dw-dev.dreamwidth.org/78628.html for more details.

Patch by [personal profile] fu.

Files modified:
  • cgi-bin/LJ/User.pm
  • cgi-bin/ljprotocol.pl
  • t/protocol.t
--------------------------------------------------------------------------------
diff -r 5288d9d5a226 -r 2ab32ff63973 cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Tue Jan 04 08:17:10 2011 +0800
+++ b/cgi-bin/LJ/User.pm	Tue Jan 04 08:21:24 2011 +0800
@@ -2098,7 +2098,7 @@ sub can_track_thread {
     return $_[0]->get_cap( 'track_thread' ) ? 1 : 0;
 }
 
-sub can_use_checkfriends {
+sub can_use_checkforupdates {
     return $_[0]->get_cap( 'checkfriends' ) ? 1 : 0;
 }
 
diff -r 5288d9d5a226 -r 2ab32ff63973 cgi-bin/ljprotocol.pl
--- a/cgi-bin/ljprotocol.pl	Tue Jan 04 08:17:10 2011 +0800
+++ b/cgi-bin/ljprotocol.pl	Tue Jan 04 08:21:24 2011 +0800
@@ -184,6 +184,7 @@ sub do_request
     if ($method eq "editcircle")       { return editcircle(@args);       }
     if ($method eq "friendof")         { return friendof(@args);         }
     if ($method eq "checkfriends")     { return checkfriends(@args);     }
+    if ($method eq "checkforupdates")  { return checkforupdates(@args);  }
     if ($method eq "getdaycounts")     { return getdaycounts(@args);     }
     if ($method eq "postevent")        { return postevent(@args);        }
     if ($method eq "editevent")        { return editevent(@args);        }
@@ -814,20 +815,20 @@ sub friendof
     return $res;
 }
 
-sub checkfriends
-{
-    my ($req, $err, $flags) = @_;
-    return undef unless authenticate($req, $err, $flags);
-    my $u = $flags->{'u'};
-    my $res = {};
-
-    # FIXME: not updated for WTF yet
-    return fail( $err, 507 );
+sub checkfriends {
+    return fail( $_[1], 504, "Use 'checkforupdates' instead." );
+}
+sub checkforupdates
+{
+    my ($req, $err, $flags) = @_;
+    return undef unless authenticate($req, $err, $flags);
+    my $u = $flags->{'u'};
+    my $res = {};
 
     # return immediately if they can't use this mode
-    unless ( $u->can_use_checkfriends ) {
+    unless ( $u->can_use_checkforupdates ) {
         $res->{'new'} = 0;
-        $res->{'interval'} = 36000;  # tell client to bugger off
+        $res->{'interval'} = 36000;
         return $res;
     }
 
@@ -843,23 +844,36 @@ sub checkfriends
     my $interval = LJ::get_cap_min($u, "checkfriends_interval");
     $res->{'interval'} = $interval;
 
-    my $mask;
-    if ($req->{'mask'} and $req->{'mask'} !~ /\D/) {
-        $mask = $req->{'mask'};
-    }
-
-    my $memkey = [$u->{'userid'},"checkfriends:$u->{userid}:$mask"];
+    my $filter;
+    if ( $req->{filter} ) {
+        $filter = $u->content_filters( name => $req->{filter} );
+        return fail( $err, 203, "Invalid filter name. Trying to check updates for a filter that does not exist." )
+            unless $filter;
+    }
+
+    my $memkey = [ $u->id, "checkforupdates:$u->{userid}:" . ( $filter ? $filter->id : "" ) ];
     my $update = LJ::MemCache::get($memkey);
     unless ($update) {
-        # TAG:FR:protocol:checkfriends (wants reading list of mask, not "friends")
-        my $fr = LJ::get_friends($u, $mask);
-        unless ($fr && %$fr) {
+        my @fr = $u->watched_userids;
+
+        # FIXME: see whether we can just get the list of users who are in the filter
+        if ( $filter ) {
+            my @filter_users;
+
+            foreach my $fid ( @fr ) {
+                push @filter_users, $fid
+                    if $filter->contains_userid( $fid );
+            }
+            @fr = @filter_users;
+        }
+
+        unless ( @fr ) {
             $res->{'new'} = 0;
             $res->{'lastupdate'} = $lastupdate;
             return $res;
         }
         if (@LJ::MEMCACHE_SERVERS) {
-            my $tu = LJ::get_timeupdate_multi({ memcache_only => 1 }, keys %$fr);
+            my $tu = LJ::get_timeupdate_multi({ memcache_only => 1 }, @fr);
             my $max = 0;
             while ($_ = each %$tu) {
                 $max = $tu->{$_} if $tu->{$_} > $max;
@@ -874,7 +888,7 @@ sub checkfriends
                 $res->{'lastupdate'} = $lastupdate;
                 return $res;
             }
-            my $list = join(", ", map { int($_) } keys %$fr);
+            my $list = join(", ", map { int($_) } @fr );
             if ($list) {
               my $sql = "SELECT MAX(timeupdate) FROM userusage ".
                   "WHERE userid IN ($list)";
@@ -3309,6 +3323,9 @@ sub do_request
     if ($req->{'mode'} eq "checkfriends") {
         return checkfriends($req, $res, $flags);
     }
+    if ($req->{'mode'} eq "checkforupdates") {
+        return checkforupdates($req, $res, $flags);
+    }
     if ($req->{'mode'} eq "getdaycounts") {
         return getdaycounts($req, $res, $flags);
     }
@@ -3607,6 +3624,28 @@ sub checkfriends
     $res->{'new'} = $rs->{'new'};
     $res->{'lastupdate'} = $rs->{'lastupdate'};
     $res->{'interval'} = $rs->{'interval'};
+    return 1;
+}
+
+## flat wrapper
+sub checkforupdates
+{
+    my ( $req, $res, $flags ) = @_;
+
+    my $err = 0;
+    my $rq = upgrade_request( $req );
+
+    my $rs = LJ::Protocol::do_request( "checkforupdates", $rq, \$err, $flags );
+    unless ( $rs ) {
+        $res->{success} = "FAIL";
+        $res->{errmsg} = LJ::Protocol::error_message( $err );
+        return 0;
+    }
+
+    $res->{success} = "OK";
+    $res->{new} = $rs->{new};
+    $res->{lastupdate} = $rs->{lastupdate};
+    $res->{interval} = $rs->{interval};
     return 1;
 }
 
diff -r 5288d9d5a226 -r 2ab32ff63973 t/protocol.t
--- a/t/protocol.t	Tue Jan 04 08:17:10 2011 +0800
+++ b/t/protocol.t	Tue Jan 04 08:21:24 2011 +0800
@@ -3,7 +3,7 @@ use warnings;
 use warnings;
 
 use Test::More;
-plan tests => 201;
+plan tests => 222;
 
 use lib "$ENV{LJHOME}/cgi-bin";
 require 'ljlib.pl';
@@ -49,27 +49,28 @@ my $do_request = sub {
 
 
 my $check_err = sub {
-    my ( $responsecode, $expectedcode, $testmsg ) = @_;
+    my ( $expectedcode, $testmsg ) = @_;
 
-    is( $responsecode, $expectedcode,
+    # code is either in the form of ###, or ###:description
+    like( $err, qr/^$expectedcode(?:$|[:])/,
         "$testmsg Protocol error ($err) = " . LJ::Protocol::error_message( $err ) );
 };
 
 my $success = sub {
-    my ( $responsecode, $testmsg ) = @_;
+    my ( $testmsg ) = @_;
 
-    is( $responsecode, 0, "$testmsg (success)" );
+    is( $err, 0, "$testmsg (success)" );
 };
 
 note( "getfriendgroups" );
 {
     ( $res, $err ) = $do_request->( "getfriendgroups" );
-    $check_err->( $err, 504, "'getfriendgroups' is deprecated." );
+    $check_err->( 504, "'getfriendgroups' is deprecated." );
     is( $res, undef, "No response expected." );
 
 
     ( $res, $err ) = $do_request->( "getfriendgroups", username => $u->user );
-    $check_err->( $err, 504, "'getfriendgroups' is deprecated." );
+    $check_err->( 504, "'getfriendgroups' is deprecated." );
     is( $res, undef, "No response expected." );
 }
 
@@ -79,12 +80,12 @@ note( "gettrustgroups" );
     #   username
 
     ( $res, $err ) = $do_request->( "gettrustgroups" );
-    $check_err->( $err, 200, "'gettrustgroups' needs a user." );
+    $check_err->( 200, "'gettrustgroups' needs a user." );
     is( $res, undef, "No response expected." );
 
 
     ( $res, $err ) = $do_request->( "gettrustgroups", username => $u->user );
-    $success->( $err, "'gettrustgroups' for user." );
+    $success->( "'gettrustgroups' for user." );
     ok( ref $res->{trustgroups} eq "ARRAY" && scalar @{$res->{trustgroups}} == 0, 
         "Empty trust groups list." );
 };
@@ -101,11 +102,11 @@ note( "getcircle" );
     #   includetrustedusers
 
     ( $res, $err ) = $do_request->( "getcircle" );
-     $check_err->( $err, 200, "'getcircle' needs a user." );
+     $check_err->( 200, "'getcircle' needs a user." );
     is( $res, undef, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user );
-    $success->( $err, "'getcircle' for user." );
+    $success->( "'getcircle' for user." );
     is( scalar keys %$res, 0, "Empty circle; no arguments provided to select the subset of the circle." );
 
     my %circle_args = (
@@ -117,7 +118,7 @@ note( "getcircle" );
     );
     while( my ( $include, $val ) = each %circle_args ) {
         ( $res, $err ) = $do_request->( "getcircle", username => $u->user, $include => 1 );
-        $success->( $err, "'getcircle' => $include" );
+        $success->( "'getcircle' => $include" );
         is( scalar keys %$res, 1, "One key: " . (keys %$res)[0] );
         is( ref $res->{$val->[0]}, "ARRAY", "Returned an arrayref of this user's $include." );
 
@@ -144,7 +145,7 @@ note( "getcircle" );
 
         ( $res, $err ) = $do_request->( "getcircle", username => $u->user, $include => 1,
             limit => $LJ::MAX_WT_EDGES_LOAD + 1 );
-        $success->( $err, "'getcircle' => $include" );
+        $success->( "'getcircle' => $include" );
 
         # check that the users we got back are from the users we added
         # don't check the other way, since we are over limit
@@ -157,14 +158,14 @@ note( "getcircle" );
     }
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includetrustgroups => 1 );
-    $success->( $err, "'getcircle' => includetrustgroups" );
+    $success->( "'getcircle' => includetrustgroups" );
     is( scalar keys %$res, 1, "One key: " . (keys %$res)[0] );
     ok( ref $res->{trustgroups} eq "ARRAY" && scalar @{$res->{trustgroups}} == 0, 
         "Empty trust groups list." );
 
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
-    $success->( $err, "'getcircle' => includecontentfilters" );
+    $success->( "'getcircle' => includecontentfilters" );
     is( scalar keys %$res, 1, "One key: " . (keys %$res)[0] );
     ok( ref $res->{contentfilters} eq "ARRAY" && scalar @{$res->{contentfilters}} == 0, "Empty list of content filters for this user." );
 
@@ -183,12 +184,12 @@ note( "editcircle" );
     #     deletefromcontentfilters
 
     ( $res, $err ) = $do_request->( "editcircle", settrustgroups => 1 );
-    $check_err->( $err, 200, "'editcircle' needs a user." );
+    $check_err->( 200, "'editcircle' needs a user." );
     is( $res, undef, "No response expected." );
 
 
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, settrustgroups => 1 );
-    $success->( $err, "No valid action provided for editcircle; ignore." );
+    $success->( "No valid action provided for editcircle; ignore." );
     is( scalar keys %$res, 0, "No action taken." );
 
 
@@ -206,7 +207,7 @@ note( "editcircle" );
         }
     );
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, settrustgroups => \%trustgroups );
-    $success->( $err, "Set trust groups." );
+    $success->( "Set trust groups." );
     is( scalar keys %$res, 0, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includetrustgroups => 1 );
@@ -235,7 +236,7 @@ note( "editcircle" );
     $trustgroups{20} = $new;
 
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, settrustgroups => { 10 => $edited, 20 => $new } );
-    $success->( $err, "Edited trust groups; those not mentioned should not be affected." );
+    $success->( "Edited trust groups; those not mentioned should not be affected." );
     is( scalar keys %$res, 0, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includetrustgroups => 1 );
@@ -254,7 +255,7 @@ note( "editcircle" );
     delete $trustgroups{5};
     delete $trustgroups{10};
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, deletetrustgroups => [ 10, 5 ] );
-    $success->( $err, "Deleted a trust group." );
+    $success->( "Deleted a trust group." );
     is( scalar keys %$res, 0, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includetrustgroups => 1 );
@@ -271,7 +272,7 @@ note( "editcircle" );
 
     # now add / edit some users' status in your circle
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, add => [ { username => "invalidusername" } ] );
-    $check_err->( $err, 203, "Tried to edit invalid user." );
+    $check_err->( 203, "Tried to edit invalid user." );
     is( scalar keys %$res, 0, "No response expected." );
 
     # let's make our watch/trust mutual
@@ -355,7 +356,7 @@ note( "editcircle" );
     );
 
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, setcontentfilters => \%contentfilters );
-    $success->( $err, "Set content filters." );
+    $success->( "Set content filters." );
     is( scalar keys %$res, 1, "Response contains only the newly added content filters." );
     is( scalar @{$res->{addedcontentfilters}}, scalar keys %contentfilters, "Got back the newly-added content filters." );
 
@@ -384,7 +385,7 @@ note( "editcircle" );
     $contentfilters{3} = $new;
 
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, setcontentfilters => { 2 => $edited, 3 => $new } );
-    $success->( $err, "Edited content filters; those not mentioned should not be affected." );
+    $success->( "Edited content filters; those not mentioned should not be affected." );
     is( scalar keys %$res, 1, "Response contains only the newly added content filters." );
     is( scalar @{$res->{addedcontentfilters}}, 1, "Got back the newly-added content filter." );
 
@@ -404,7 +405,7 @@ note( "editcircle" );
     delete $contentfilters{1};
     delete $contentfilters{3};
     ( $res, $err ) = $do_request->( "editcircle", username => $u->user, deletecontentfilters => [ 1, 3 ] );
-    $success->( $err, "Deleted content filters." );
+    $success->( "Deleted content filters." );
     is( scalar keys %$res, 0, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
@@ -428,7 +429,7 @@ note( "editcircle" );
         username => "invalid_user",
         id => 2
     } ] );
-    $check_err->( $err, 203, "Tried to add an invalid user to a content filter." );
+    $check_err->( 203, "Tried to add an invalid user to a content filter." );
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
     ok( $res->{contentfilters}->[0]->{data} eq "", "No data for the content filter." );
 
@@ -438,7 +439,7 @@ note( "editcircle" );
         username => $watched->user,
         id => 2
     } ] );
-    $success->( $err, "Added a user to a content filter." );
+    $success->( "Added a user to a content filter." );
     is( scalar keys %$res, 0, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
@@ -450,7 +451,7 @@ note( "editcircle" );
         username => "invalid_user",
         id => 2
     } ] );
-    $check_err->( $err, 203, "Tried to remove an invalid user from a content filter." );
+    $check_err->( 203, "Tried to remove an invalid user from a content filter." );
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
     ok( $res->{contentfilters}->[0]->{data} ne "", "Some data in the content filter." );
 
@@ -460,7 +461,7 @@ note( "editcircle" );
         username => $watchedtrusted->user,
         id => 2
     } ] );
-    $success->( $err, "Tried to remove a user who was not in the content filter." );
+    $success->( "Tried to remove a user who was not in the content filter." );
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
     ok( $res->{contentfilters}->[0]->{data} ne "", "Some data in the content filter." );
 
@@ -469,7 +470,7 @@ note( "editcircle" );
         username => $watched->user,
         id => 2
     } ] );
-    $success->( $err, "Removed a user from a content filter." );
+    $success->( "Removed a user from a content filter." );
     is( scalar keys %$res, 0, "No response expected." );
 
     ( $res, $err ) = $do_request->( "getcircle", username => $u->user, includecontentfilters => 1 );
@@ -506,7 +507,7 @@ note( "post to community by various jour
         event      => "new test post to community",
         tz         => "guess",
     );
-    $success->( $err, "Entry posted successfully to community by personal journal." );
+    $success->( "Entry posted successfully to community by personal journal." );
 
 
     my $comm2 = temp_comm();
@@ -515,7 +516,7 @@ note( "post to community by various jour
         usejournal  => $comm->user,
         event       => "new test post to community by community2"
     );
-    $check_err->( $err, 300, "Communities cannot post entries." );
+    $check_err->( 300, "Communities cannot post entries." );
 
     ( $res, $err ) = $do_request->( "postevent",
         username    => $comm->user,
@@ -524,7 +525,7 @@ note( "post to community by various jour
         event       => "new test post to self by a community",
         tz          => "guess",
     );
-    $check_err->( $err, 300, "Communities cannot post entries, not even to themselves." );
+    $check_err->( 300, "Communities cannot post entries, not even to themselves." );
 
 
 
@@ -539,7 +540,7 @@ note( "post to community by various jour
         event       => "new test post to a community by an identity user",
         tz          => "guess",
     );
-    $check_err->( $err, 150, "OpenID users cannot post entries to communities with no OpenID posting prop." );
+    $check_err->( 150, "OpenID users cannot post entries to communities with no OpenID posting prop." );
 
 
     ( $res, $err ) = $do_request->( "postevent",
@@ -551,7 +552,7 @@ note( "post to community by various jour
 
         flags       => { importer_bypass => 1 },
     );
-    $success->( $err, "Always allow posting with the importer bypass." );
+    $success->( "Always allow posting with the importer bypass." );
 
 
     # allow all users to add and control tags (for convenience)
@@ -577,5 +578,151 @@ note( "post to community by various jour
         props       => { taglist => "testing" },
         tz          => "guess",
     );
-    $success->( $err, "OpenID users can post entries to communities with the appropriate prop." );
+    $success->( "OpenID users can post entries to communities with the appropriate prop." );
 }
+
+
+note( "checkforupdates" );
+{
+    
+    my $u = temp_user();
+
+    my $start = 0;
+    my $end = 15;
+
+    # make sure no one can use the protocol...
+    $LJ::CAP{$_}->{checkfriends} = 0
+        foreach( 0.. 15 );
+
+    ( $res, $err ) = $do_request->( "checkfriends" );
+    $check_err->( 504, "Use 'checkforupdates' instead" );
+
+
+    ( $res, $err ) = $do_request->( "checkforupdates" );
+    $check_err->( 200, "Needs arguments" );
+
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username => $u->user,
+        flags    => { noauth => 0 },
+    );
+    $check_err->( 101, "Have all arguments, but needs authorization" );
+
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username => $u->user,
+    );
+    $success->( "Not authorized to use checkforupdates" );
+    is_deeply( $res, {
+            interval => 36000,
+            new => 0
+    }, "Not authorized to use checkforupdates; no new entries, check back in an hour" );
+
+
+    # make sure everyone can use the protocol, and set interval to a known variable we can check against
+    # (not using $LJ::T_HAS_ALL_CAPS = 1, because that makes everyone readonly)
+    $LJ::CAP{$_}->{checkfriends} = 1
+        foreach( 0.. 15 );
+    $LJ::CAP{$_}->{checkfriends_interval} = 7
+        foreach( 0.. 15 );
+
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username => $u->user,
+    );
+    $success->( "Checkforupdates. We don't watch anyone, but that's okay" );
+    is( scalar %{ $u->watch_list }, 0, "Not watching anyone" );
+    is_deeply( $res, {
+            interval => 7,
+            new => 0,
+            lastupdate => "0000-00-00 00:00:00",
+    }, "Watching no one." );
+
+
+    # now we watch some people (who have no updates yet)
+    my $userinfilter = temp_user();
+    $u->add_edge( $userinfilter, watch => { nonotify => 1 } );
+    $u->create_content_filter( name => "filter" );
+    my $filter = $u->content_filters( name => "filter" );
+    $filter->add_row( userid => $userinfilter->userid );
+
+    my $usernotinfilter = temp_user();
+    $u->add_edge( $usernotinfilter, watch => { nonotify => 1 } );
+
+
+    # and go through the protocol again
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username => $u->user,
+    );
+    $success->( "Checkforupdates. No one has updated." );
+    is( scalar $u->watched_userids,2 );
+    is_deeply( $res, {
+            interval => 7,
+            new => 0,
+            lastupdate => "0000-00-00 00:00:00",
+    }, "No new entries." );
+
+
+    # and then let's see what happens when they get updates
+    $do_request->( "postevent", username => $userinfilter->user, event => "update", tz => "guess" );
+    sleep( 1 ); # pause so we have different time stamps (for checking against)
+    $do_request->( "postevent", username => $usernotinfilter->user, event => "update", tz => "guess" );
+
+    # use variables to make it easier to determine what I'm trying to check for
+    # In some tests, I will deliberately *not* use the variables
+    my $earlierupdate = $userinfilter->timeupdate;
+    my $laterupdate = $usernotinfilter->timeupdate;
+    ok( $earlierupdate < $laterupdate, "Timestamps need to be unequal for when we're testing stuff." );
+
+    # and go through the protocol again
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username => $u->user,
+    );
+    $success->( "Checkforupdates. Users have updated." );
+    is_deeply( $res, {
+            interval => 7,
+            new => 0,
+            lastupdate => LJ::mysql_time( $usernotinfilter->timeupdate ),
+    }, "No new entries." );
+
+
+    # and we also check a subset (filter)
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username => $u->user,
+        filter   => $filter->name,
+    );
+    $success->( "Checkforupdates of a subset of watched users." );
+    is_deeply( $res, {
+            interval => 7,
+            new => 0,
+            lastupdate => LJ::mysql_time( $userinfilter->timeupdate ),
+    }, "No new entries." );
+
+
+    # optional argument lastupdate
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username   => $u->user,
+        lastupdate => $earlierupdate
+    );
+    $check_err->( 203, "lastupdate argument needs to be in mysql_time format." );
+
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username   => $u->user,
+        lastupdate => LJ::mysql_time( $earlierupdate ),
+    );
+    $success->( "Checkforupdates since lastupdate. Have new updates" );
+    is_deeply( $res, {
+            interval => 7,
+            new => 1,
+            lastupdate => LJ::mysql_time( $laterupdate ),
+    }, "Have new entries." );
+
+    ( $res, $err ) = $do_request->( "checkforupdates",
+        username   => $u->user,
+        lastupdate => LJ::mysql_time( $laterupdate ),
+    );
+    $success->( "Checkforupdates since lastupdate. No new updates" );
+    is_deeply( $res, {
+            interval => 7,
+            new => 0,
+            lastupdate => LJ::mysql_time( $laterupdate ),
+    }, "No new entries." );
+
+}
--------------------------------------------------------------------------------