root/branches/release-35/t/41-atom.t @ 1947

Revision 1947, 17.4 kB (checked in by fumiakiy, 20 months ago)

Implemented comments retrieval via Atom PP. BugId:79334

  • Property svn:mime-type set to text/plain
  • Property svn:keywords set to Author Date Id Revision
Line 
1use strict;
2use lib 't/lib', 'extlib', 'lib', '../lib', '../extlib';
3use POSIX;
4
5use MT;
6use MT::Atom;
7use XML::LibXML; # this test would not work without it
8use XML::XPath;
9use XML::Atom;
10use XML::Atom::Feed;
11use XML::Atom::Entry;
12
13use Test::More tests => 97;
14
15# To keep away from being under FastCGI
16$ENV{HTTP_HOST} = 'localhost';
17
18use vars qw( $DB_DIR $T_CFG );
19my $mt = MT->new( Config => $T_CFG ) or die MT->errstr;
20isa_ok($mt, 'MT');
21
22use MT::Test qw(:db :data);
23
24my %test_data;
25$test_data{'/mt-atom.cgi/weblog'} = <<XML1;
26<?xml version="1.0" encoding="utf-8"?>
27    <entry xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
28    <title>Fight the Power</title>
29    <content>Elvis was a hero to most but he never meant shit to me</content>
30    <issued>2004-08-06T00:43:34+01:00</issued>
31    </entry>
32XML1
33$test_data{'/mt-atom.cgi/1.0'} = <<XML2;
34<?xml version="1.0" encoding="utf-8"?>
35<entry xmlns="http://www.w3.org/2005/Atom">
36<title>Fight the Power</title>
37<content type="html">Elvis was a hero to most but he never meant shit to me</content>
38<published>2004-08-06T00:43:34+01:00</published>
39</entry>
40XML2
41
42my %feed_link = (
43    '/mt-atom.cgi/weblog' => sub {
44        my ($resp) = @_;
45        my $feed = XML::Atom::Feed->new(\$resp->content());
46        ok($feed, 'got feed');
47        my ($sfeed) = grep {
48            $_->rel eq 'service.feed'
49        } $feed->links;
50        $sfeed->href;
51    },
52    '/mt-atom.cgi/1.0' => sub {
53        my ($resp) = @_;
54        my $feed = XML::XPath->new(xml => $resp->content());
55        ok($feed, 'got feed');
56        my $col = $feed->getNodeText('/service/workspace[1]/collection/@href');
57        $col;
58    }
59);
60
61my $username = 'Chuck D';
62my $chuck = MT::Author->load({name => $username})
63    or die "Couldn't load $username";
64my $chuck_token = $chuck->api_password;
65
66# not a good nonce-maker
67my @hexch = ('0' .. '9', 'a' .. 'f');
68sub make_nonce {
69    join '', map { $hexch[rand() * @hexch] } (0..7);
70}
71sub make_wsse {
72    my ($password) = @_;
73    my $timestamp = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(time));
74    my $nonce = make_nonce();
75    my $PasswordDigest = sha1_base64($nonce . $timestamp . $chuck_token);
76    # print "# PasswordDigest is sha1('$nonce$timestamp$chuck_token')\n";
77    $nonce = MIME::Base64::encode_base64($nonce, '');
78    return "UserNameToken Username=\"$username\", "
79                 . "PasswordDigest=\"$PasswordDigest\", Nonce=\"$nonce\", "
80                 . "Created=\"$timestamp\"";
81}
82
83require LWP::UserAgent::Local;
84my $ua = new LWP::UserAgent::Local({ ScriptAlias => '/' });
85
86foreach my $base_uri ( qw{/mt-atom.cgi/weblog /mt-atom.cgi/1.0 } ) {
87{
88# # # # First try a req with baloney auth, make sure it fails
89    # TBD: Try more bogus auth varieties
90    my $nonce = make_nonce();
91    $nonce = MIME::Base64::encode_base64($nonce, '');
92
93    my $uri = new URI();
94    $uri->path($base_uri . '/blog_id=1');
95    my $req = new HTTP::Request(GET => $uri);
96    $req->header(Authentication => 'Atom');
97    $req->header('X-WSSE' => "UserNameToken Username=\"Melody\", PasswordDigest=\"Oj12fART+XZvBZBe39vVvkirg4w\", Nonce=\"" . $nonce . "\", Created=\"2004-05-19T20:08:57Z\"");
98    print "# requesting: " . $req->uri . "\n";
99    my $resp = $ua->request($req);
100
101    print "# response code was: " . $resp->code . "\n";
102    print "# content was: " . $resp->content . "\n";# if !$resp->code();
103    ok($resp->is_error());
104}
105
106# test blog lists
107{
108    my $wsse_header = make_wsse($chuck_token);
109    my $uri = new URI;
110    $uri->path($base_uri);
111    my $req = new HTTP::Request(GET => $uri);
112    $req->header('Authorization' => 'Atom');
113    $req->header('X-WSSE' => $wsse_header);
114
115    print "# X-WSSE: $wsse_header\n";
116
117    my $resp = $ua->request($req);
118    if (ok($resp->is_success)) {
119        my $blog_feed_url = $feed_link{$base_uri}->($resp);
120        my $blog_feed_uri = new URI($blog_feed_url);
121        is($blog_feed_uri->path, $base_uri . '/blog_id=1', 'blog feed url is correct');
122    }
123    else {
124        die 'failed to retrieve blog feed';
125    }
126}
127
128# test blog feed
129{
130    my $wsse_header = make_wsse($chuck_token);
131    my $uri = new URI;
132    $uri->path($base_uri . "/blog_id=1");
133    my $req = new HTTP::Request(GET => $uri);
134    $req->header('Authorization' => 'Atom');
135    $req->header('X-WSSE' => $wsse_header);
136
137    print "# X-WSSE: $wsse_header\n";
138
139    my $resp = $ua->request($req);
140    if (ok($resp->is_success)) {
141        my $feed = XML::Atom::Feed->new(\$resp->content());
142        ok($feed, 'got feed');
143        is($feed->title, 'none');
144        my ($alternate) = grep {
145            $_->rel eq 'alternate' && $_->type eq 'text/html'
146        } $feed->links;
147        is($alternate->href, 'http://narnia.na/nana/', 'blog url is correct');
148        my $entry_count = MT::Entry->count(
149            { blog_id => 1 },
150            { limit => 21 },
151        );
152        my @entries = $feed->entries;
153        is($entry_count, scalar(@entries), 'number of entries is correct');
154
155        # check if entries have replies link relation
156        my $failed = 0;
157        foreach my $entry (@entries) {
158            next if !$entry->id && $entry->title =~ /^I just finished installing Movable Type/;
159            my $mt_entry = MT::Entry->load({
160                atom_id => $entry->id,
161                blog_id => 1,
162            });
163            $failed = 1, last unless $mt_entry;
164            my ($replies) = grep {
165                $_->rel eq 'replies'
166            } $entry->links;
167            $failed = 1, last unless $replies;
168            my $replies_uri = new URI($replies->href);
169            $failed = 1, last unless $replies_uri->path eq '/mt-atom.cgi/comments/blog_id=1/entry_id='.$mt_entry->id;
170        }
171        is($failed, 0, 'all the entries have replies link rel');
172    }
173    else {
174        die 'failed to retrieve blog feed';
175    }
176}
177
178my $entry_id;
179
180use constant USE_DIGEST => 0;
181
182if (USE_DIGEST)
183{
184# # # # # # # use this to do a Digest auth with MD5 as the algorithm.
185    my $realm = 'movabletype';
186#     $username = shift;
187#     $hashed_pwd = shift;
188    require Digest::MD5;
189    my $hashed_pwd = Digest::MD5::md5(join ':', $username, $realm, $chuck_token);
190    my $method = 'POST';
191
192    my $uri = new URI;
193    $uri->path($base_uri . '/blog_id=1');
194    my $req = new HTTP::Request(POST => $uri);
195
196    my $resp = $ua->simple_request($req);
197    ok($resp->is_error());
198    my $auth_header = $resp->header('WWW-Authenticate');
199    my ($nonce) = $auth_header =~ /nonce=(\S*)/;
200
201    my $A1 = pack('H*', $hashed_pwd);
202    my $A2 = $method . ':' . $uri;
203   
204    require Digest::MD5;
205    my $hash = \&Digest::MD5::md5;
206    my $kd = sub { $hash->($_[0].':'.$_[1]) };
207
208    print STDERR "Signing ", join(':',
209                                  $nonce,
210                                  '',
211                                  '',
212                                  'auth',
213                                  $hash->($A2)), "\n";
214    print STDERR "with key ", $hash->($A1), "\n";
215
216    my $response = $kd->($hash->($A1), join(':',
217                                            $nonce,
218                                            '',
219                                            '',
220                                            'auth',
221                                            $hash->($A2)));
222    print "# ", ($response = unpack('H*', $response)), "\n";
223
224    $uri = new URI;
225    $uri->path($base_uri . '/blog_id=1');
226    $req = new HTTP::Request(POST => $uri);
227
228    $req->header('X-Atom-Authorization', "Digest nonce=$nonce, username=$username, realm=movabletype, uri=$uri, qop=auth, response=$response");
229    print "# sending: ", $req->header('X-Atom-Authorization');
230
231    $req->content($test_data{$base_uri});
232
233    $resp = $ua->simple_request($req);
234
235    print "# " . $resp->code . " " . $resp->message . "\n";
236    print "# " . $resp->content . "\n";
237    ok($resp->is_success());
238
239    print "##\n". $resp->header('Location')  . "\n##\n";
240    my $location = $resp->header('Location')
241        || die "No Location: header. Did get\n"
242                . $resp->code() . $resp->message() . "\n"
243                . $resp->headers_as_string() . "\n"
244                . $resp->content();
245    ($entry_id) = ($location =~ /entry_id=(\d+)/);
246
247    $entry_id || die "Couldn't get entry ID from header Location: "
248        . $location;
249
250    print "# entry ID is $entry_id\n";
251
252    ok($entry_id);
253}
254
255unless (USE_DIGEST)
256{
257    print "# Doing Digest auth\n";
258# # # # Now try posting an entry with authentication
259    my $nonce = make_nonce();
260    my $timestamp = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(time));
261    use Digest::SHA1 qw(sha1_base64);
262    my $PasswordDigest = sha1_base64($nonce . $timestamp . $chuck_token);
263
264#     print STDERR ("Client hashing (",
265#                 unpack('H*', $nonce . $timestamp . $chuck_token), ")\n");
266#     print STDERR "      produces: $PasswordDigest\n";
267
268    $nonce = MIME::Base64::encode_base64($nonce, '');
269
270    print "# nonce: $nonce\n";
271
272    my $dir = `pwd`; chomp $dir;
273    while (! -x ($dir . "/mt-atom.cgi")) {
274        $dir =~ s!(/[^/]*)$!!;
275        last if $dir =~ m!^/?$!;
276    }   
277
278    my $uri = new URI;
279    $uri->path($base_uri . '/blog_id=1');
280    my $req = new HTTP::Request(POST => $uri);
281    $req->header('Authorization' => 'Atom');
282    $req->header('X-WSSE' => "UserNameToken Username=\"$username\", "
283                 . "PasswordDigest=\"$PasswordDigest\", Nonce=\"$nonce\", "
284                 . "Created=\"$timestamp\"");
285
286    $req->content($test_data{$base_uri});
287
288    my $resp = $ua->simple_request($req);
289
290    print "######### RESPONSE #########\n";
291    print "# " . $resp->code . " " . $resp->message . "\n";
292    my $content =  $resp->content;
293    $content =~ s/^/# /gm;
294    print $content;
295    print "#########~RESPONSE #########\n";
296    ok($resp->is_success());
297
298    #print "##\n". $resp->header('Location')  . "\n##\n";
299    my $location = $resp->header('Location')
300        || die "No Location: header. Did get\n"
301                . $resp->headers_as_string() . "\n"
302                . $resp->content();
303    ($entry_id) = ($location =~ /entry_id=(\d+)/);
304
305    $entry_id || die "Couldn't get entry ID from header Location: "
306        . $location;
307
308    print "# entry ID is $entry_id\n";
309
310    ok($entry_id);
311    my $atom_obj = XML::Atom::Entry->new(\$resp->content());
312    require Date::Parse;
313    print "# ", $atom_obj->issued(), "\n";
314    is(Date::Parse::str2time($atom_obj->issued()),
315       Date::Parse::str2time('2004-08-06T00:43:34+01:00'), $atom_obj->issued());
316}
317
318{
319    my $wsse_header = make_wsse($chuck_token);
320    my $uri = new URI;
321    $uri->path($base_uri . "/blog_id=1/entry_id=$entry_id");
322    my $req = new HTTP::Request(GET => $uri);
323    $req->header('Authorization' => 'Atom');
324    $req->header('X-WSSE' => $wsse_header);
325
326    print "# X-WSSE: $wsse_header\n";
327
328    my $resp = $ua->request($req);
329
330    my $atom_entry = XML::Atom::Entry->new(Stream => \$resp->content());
331
332    is($atom_entry->title(), "Fight the Power");
333    is($atom_entry->author()->name(), $chuck->nickname);
334    is($atom_entry->content()->body(),
335       "Elvis was a hero to most but he never meant shit to me");
336
337    $wsse_header = make_wsse($chuck_token);
338    $uri = new URI;
339    $uri->path($base_uri . "/blog_id=1/entry_id=$entry_id");
340    $req = new HTTP::Request(PUT => $uri);
341    $req->header('Authorization' => 'Atom');
342    $req->header('X-WSSE' => $wsse_header);
343    my $body = $atom_entry->as_xml();
344    $req->content($body);
345   
346    $resp = $ua->request($req);
347
348    if (ok($resp->is_success)) {
349        $atom_entry = XML::Atom::Entry->new(Stream => \$resp->content());
350        is($atom_entry->title, "Fight the Power");
351        is(Date::Parse::str2time($atom_entry->issued),
352           Date::Parse::str2time("2004-08-05T21:13:34-0230"));
353    } else {
354        print STDERR "# PUT request returned ", $resp->status_line(), "\n";
355        skip(1);
356        skip(1);
357    }
358}
359
360} #end foreach
361
362COMMENT:
363# comments retrieval
364{
365    my $wsse_header = make_wsse($chuck_token);
366    my $uri = new URI;
367    $uri->path('/mt-atom.cgi/comments/blog_id=1');
368    my $req = new HTTP::Request(GET => $uri);
369    $req->header('Authorization' => 'Atom');
370    $req->header('X-WSSE' => $wsse_header);
371
372    my $resp = $ua->request($req);
373    if (ok($resp->is_success)) {
374        my $thr_ns = XML::Atom::Namespace->new(prefix => undef, uri => 'http://purl.org/syndication/thread/1.0');
375        my $comments = XML::Atom::Feed->new(\$resp->content());
376        my $count = MT::Comment->count({
377            blog_id => 1, visible => 1
378        });
379        is( $count, scalar($comments->entries), 'comment count' );
380        foreach my $c ( $comments->entries ) {
381            my $id = $c->id;
382            my ( $cmt_id ) = $id =~ m{/([0-9]+)$};
383            die unless $cmt_id;
384            my $mt_comment = MT::Comment->load($cmt_id);
385            die unless $mt_comment;
386            my $mt_entry = $mt_comment->entry;
387            is($c->title, $mt_entry->title, 'comment title == entry title');
388            is( $c->author->name, $mt_comment->author, 'comment author' );
389            is( $c->author->email || '', $mt_comment->email || '', 'commenter email' );
390            is( $c->author->uri || '', $mt_comment->url || '', 'commenter url'  );
391            if ( $XML::Atom::LIBXML ) {
392                my $nodelist = $c->elem->getElementsByTagNameNS('http://purl.org/syndication/thread/1.0', 'in-reply-to');   
393                my $irt = $nodelist->shift;
394                ok($irt, 'in-reply-to');
395                is( $irt->ref, $mt_entry->atom_id, 'in-reply-to/ref' );
396                is( $irt->href, $mt_entry->permalink, 'in-reply-to/href' );
397            }
398        }
399    }
400    else {
401        die $resp->content();
402    }
403}
404
405{
406    my $iter = MT::Comment->count_group_by(
407        { blog_id => 1, visible => 1 },
408        { group => ['entry_id'], sort => [ { desc => 'DESC', column => '1' } ]
409        }
410    );
411    #$Data::ObjectDriver::PROFILE = 1;
412    #my $p = Data::ObjectDriver->profiler;
413    #$p->reset;
414    #print "$_\n" foreach @{$p->query_log};
415    my ( $count, $eid ) = $iter->();
416    $iter->('finish');
417    my $entry = MT::Entry->load($eid);
418
419    my $wsse_header = make_wsse($chuck_token);
420    my $uri = new URI;
421    $uri->path('/mt-atom.cgi/1.0/blog_id=1/entry_id=' . $entry->id);
422    my $req = new HTTP::Request(GET => $uri);
423    $req->header('Authorization' => 'Atom');
424    $req->header('X-WSSE' => $wsse_header);
425
426    my $resp = $ua->request($req);
427    if (ok($resp->is_success)) {
428        my $feed = XML::Atom::Entry->new(\$resp->content());
429        my ($replies) = grep {
430            $_->rel eq 'replies'
431        } $feed->links;
432
433        # retrieve comments from replies url
434        my $replies_uri = new URI($replies->href);
435        my $wsse_header = make_wsse($chuck_token);
436        my $uri = new URI;
437        $uri->path($replies_uri->path);
438        my $req = new HTTP::Request(GET => $uri);
439        $req->header('Authorization' => 'Atom');
440        $req->header('X-WSSE' => $wsse_header);
441
442        my $resp = $ua->request($req);
443        my $thr_ns = XML::Atom::Namespace->new(prefix => undef, uri => 'http://purl.org/syndication/thread/1.0');
444        if (ok($resp->is_success)) {
445            my $comments = XML::Atom::Feed->new(\$resp->content());
446            is( $count, scalar($comments->entries), 'comment count' );
447            foreach my $e ( $comments->entries ) {
448                is($e->title, $entry->title, 'comment title == entry title');
449                my $id = $e->id;
450                my ( $cmt_id ) = $id =~ m{/([0-9]+)$};
451                die unless $cmt_id;
452                my $mt_comment = MT::Comment->load($cmt_id);
453                die unless $mt_comment;
454                is( $e->author->name, $mt_comment->author, 'comment author' );
455                is( $e->author->email || '', $mt_comment->email || '', 'commenter email' );
456                is( $e->author->uri || '', $mt_comment->url || '', 'commenter url'  );
457                if ( $XML::Atom::LIBXML ) {
458                    my $nodelist = $e->elem->getElementsByTagNameNS('http://purl.org/syndication/thread/1.0', 'in-reply-to');   
459                    my $irt = $nodelist->shift;
460                    ok($irt, 'in-reply-to');
461                    is( $irt->ref, $entry->atom_id, 'in-reply-to/ref' );
462                    is( $irt->href, $entry->permalink, 'in-reply-to/href' );
463                }
464            }
465        }
466        else {
467            die $resp->content();
468        }
469    }
470}
471
472{
473    my $thr_ns = XML::Atom::Namespace->new(prefix => undef, uri => 'http://purl.org/syndication/thread/1.0');
474    my $wsse_header = make_wsse($chuck_token);
475    my $uri = new URI;
476    $uri->path('/mt-atom.cgi/comments/blog_id=1/comment_id=1');
477    my $req = new HTTP::Request(GET => $uri);
478    $req->header('Authorization' => 'Atom');
479    $req->header('X-WSSE' => $wsse_header);
480
481    my $resp = $ua->request($req);
482    if (ok($resp->is_success)) {
483        my $c = XML::Atom::Entry->new(\$resp->content());
484        my $mt_comment = MT::Comment->load(1);
485        die unless $mt_comment;
486        my $entry = $mt_comment->entry;
487        is( $c->title, $entry->title, 'comment title == entry title' );
488        is( $c->author->name, $mt_comment->author, 'comment author' );
489        is( $c->author->email || '', $mt_comment->email || '', 'commenter email' );
490        is( $c->author->uri || '', $mt_comment->url || '', 'commenter url'  );
491        if ( $XML::Atom::LIBXML ) {
492            my $nodelist = $c->elem->getElementsByTagNameNS('http://purl.org/syndication/thread/1.0', 'in-reply-to');   
493            my $irt = $nodelist->shift;
494            ok($irt, 'in-reply-to');
495            is( $irt->ref, $entry->atom_id, 'in-reply-to/ref' );
496            is( $irt->href, $entry->permalink, 'in-reply-to/href' );
497        }
498    }
499    else {
500        die $resp->content();
501    }
502}
503
504END {
505    #my $melody = MT::Author->load({ name => $username });
506    #$melody->delete() if $melody;
507}
Note: See TracBrowser for help on using the browser.