diff options
Diffstat (limited to 't')
-rw-r--r-- | t/config_limiter.t | 1 | ||||
-rw-r--r-- | t/git.t | 23 | ||||
-rw-r--r-- | t/git_async.t | 142 | ||||
-rw-r--r-- | t/git_idx.t | 24 | ||||
-rw-r--r-- | t/httpd-unix.t | 7 | ||||
-rw-r--r-- | t/hval.t | 20 | ||||
-rw-r--r-- | t/repo_git_search_idx.t | 28 | ||||
-rw-r--r-- | t/repobrowse.t | 21 | ||||
-rw-r--r-- | t/repobrowse_common_git.perl | 67 | ||||
-rw-r--r-- | t/repobrowse_git.t | 11 | ||||
-rw-r--r-- | t/repobrowse_git_atom.t | 38 | ||||
-rw-r--r-- | t/repobrowse_git_commit.t | 19 | ||||
-rw-r--r-- | t/repobrowse_git_httpd.t | 138 | ||||
-rw-r--r-- | t/repobrowse_git_log.t | 19 | ||||
-rw-r--r-- | t/repobrowse_git_raw.t | 24 | ||||
-rw-r--r-- | t/repobrowse_git_snapshot.t | 46 | ||||
-rw-r--r-- | t/repobrowse_git_src.t | 38 | ||||
-rw-r--r-- | t/search.t | 34 | ||||
-rw-r--r-- | t/spawn.t | 11 | ||||
-rw-r--r-- | t/thread-all.t | 5 |
20 files changed, 668 insertions, 48 deletions
diff --git a/t/config_limiter.t b/t/config_limiter.t index f0b65281..04f32cbf 100644 --- a/t/config_limiter.t +++ b/t/config_limiter.t @@ -4,6 +4,7 @@ use strict; use warnings; use Test::More; use PublicInbox::Config; +use PublicInbox::Inbox; my $cfgpfx = "publicinbox.test"; { my $config = PublicInbox::Config->new({ @@ -139,4 +139,27 @@ if (1) { ok($nl > 1, "qx returned array length of $nl"); } +{ + my $git = PublicInbox::Git->new($dir); + + my $err = $git->popen([qw(cat-file blob non-existent)], undef, + { 2 => $git->err_begin }); + my @out = <$err>; + my $close_ret = close $err; + my $close_err = $?; + is(join('', @out), '', 'no output on stdout on error'); + isnt($close_err, 0, 'close set $? on bad command'); + ok(!$close_ret, 'close returned error on bad command'); + isnt($git->err, '', 'got stderr output'); + + $err = $git->popen([qw(tag -l)], undef, { 2 => $git->err_begin }); + @out = <$err>; + $close_ret = close $err; + $close_err = $?; + is(join('', @out), '', 'no output on stdout on error'); + ok(!$close_err, 'close clobbered $? on empty output'); + ok($close_ret, 'close returned error on empty output'); + is($git->err, '', 'no stderr output'); +} + done_testing(); diff --git a/t/git_async.t b/t/git_async.t new file mode 100644 index 00000000..ffe2b1a2 --- /dev/null +++ b/t/git_async.t @@ -0,0 +1,142 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +use Test::More; +$SIG{PIPE} = 'IGNORE'; +foreach my $mod (qw(Danga::Socket)) { + eval "require $mod"; + plan skip_all => "$mod missing for git_async.t" if $@; +} +use File::Temp qw/tempdir/; +use Cwd qw/getcwd/; +my $tmpdir = tempdir('git_async-XXXXXX', TMPDIR => 1, CLEANUP => 1); +use_ok 'PublicInbox::Git'; +my $dir = "$tmpdir/git.git"; +{ + is(system(qw(git init -q --bare), $dir), 0, 'created git directory'); + my @cmd = ('git', "--git-dir=$dir", 'fast-import', '--quiet'); + my $fi_data = getcwd().'/t/git.fast-import-data'; + ok(-r $fi_data, "fast-import data readable (or run test at top level)"); + my $pid = fork; + defined $pid or die "fork failed: $!\n"; + if ($pid == 0) { + open STDIN, '<', $fi_data or die "open $fi_data: $!\n"; + exec @cmd; + die "failed exec: ",join(' ', @cmd),": $!\n"; + } + waitpid $pid, 0; + is($?, 0, 'fast-import succeeded'); +} + +{ + my $f = 'HEAD:foo.txt'; + my @args; + my $n = 0; + my $git = PublicInbox::Git->new($dir); + Danga::Socket->SetPostLoopCallback(sub { + my ($fdmap) = @_; + foreach (values %$fdmap) { + return 1 if ref($_) =~ /::GitAsync/; + } + 0 + }); + $git->check_async_ds($f, sub { + $n++; + @args = @_; + $git = undef; + }); + Danga::Socket->EventLoop; + my @exp = PublicInbox::Git->new($dir)->check($f); + my $exp = [ \@exp ]; + is_deeply(\@args, $exp, 'matches regular check'); + is($n, 1, 'callback only called once'); + $git = PublicInbox::Git->new($dir); + $n = 0; + my $max = 100; + my $missing = 'm'; + my $m = 0; + for my $i (0..$max) { + my $k = "HEAD:m$i"; + $git->check_async_ds($k, sub { + my ($info) = @_; + ++$n; + ++$m if $info->[1] eq 'missing' && $info->[0] eq $k; + }); + if ($git->{async_c}->{wr}->{write_buf_size}) { + diag("async_check capped at $i"); + $max = $i; + last; + } + } + is($m, $n, 'everything expected missing is missing'); + $git->check_async_ds($f, sub { $git = undef }); + Danga::Socket->EventLoop; + + $git = PublicInbox::Git->new($dir); + my $info; + my $str = ''; + my @missing; + $git->cat_async_ds('HEAD:miss', sub { + my ($miss) = @_; + push @missing, $miss; + }); + $git->cat_async_ds($f, sub { + my $res = $_[0]; + if (ref($res) eq 'ARRAY') { + is($info, undef, 'info unset, setting..'); + $info = $res; + } elsif (ref($res) eq 'SCALAR') { + $str .= $$res; + if (length($str) >= $info->[2]) { + is($info->[2], length($str), 'length match'); + $git = undef + } + } + }); + Danga::Socket->EventLoop; + is_deeply(\@missing, [['HEAD:miss', 'missing']], 'missing cat OK'); + is($git, undef, 'git undefined'); + $git = PublicInbox::Git->new($dir); + my $sref = $git->cat_file($f); + is($str, $$sref, 'matches synchronous version'); + $git = undef; + Danga::Socket->RunTimers; +} + +{ + my $git = PublicInbox::Git->new($dir); + foreach my $s (qw(check_async_compat cat_async_compat)) { + my @missing; + $git->check_async_compat('HED:miss1ng', sub { + my ($miss) = @_; + push @missing, $miss; + }); + is_deeply(\@missing, [['HED:miss1ng', 'missing']], + "missing $s OK"); + } + my @info; + my $str = ''; + my $eof_seen = 0; + $git->cat_async_compat('HEAD:foo.txt', sub { + my $ref = $_[0]; + my $t = ref $ref; + if ($t eq 'ARRAY') { + push @info, $ref; + } elsif ($t eq 'SCALAR') { + $str .= $$ref; + } elsif ($ref == 0) { + $eof_seen++; + } else { + fail "fail type: $t"; + } + }); + is($eof_seen, 1, 'EOF seen once'); + is_deeply(\@info, [ [ 'bf4f17855632367a160bef055fc8ba4675d10e6b', + 'blob', 18 ]], 'info matches compat'); + is($str, "-----\nhello\nworld\n", 'data matches compat'); +} + +done_testing(); + +1; diff --git a/t/git_idx.t b/t/git_idx.t new file mode 100644 index 00000000..65667cfc --- /dev/null +++ b/t/git_idx.t @@ -0,0 +1,24 @@ +# Copyright (C) 2015 all contributors <meta@public-inbox.org> +# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt) +use strict; +use warnings; +use Test::More; +use File::Temp qw/tempdir/; +use Email::MIME; +my $tmpdir = tempdir('pi-git-idx-XXXXXX', TMPDIR => 1, CLEANUP => 1); +my $git_dir = "$tmpdir/a.git"; +use_ok 'PublicInbox::Git'; +use_ok 'PublicInbox::GitIdx'; +my $git = PublicInbox::Git->new($git_dir); +is(0, system(qw(git init -q --bare), $git_dir), "git init (main)"); + +$git->qx(qw(config core.sharedRepository 0644)); +is(git_umask_for($git_dir), oct '022', 'umask is correct for 644'); + +$git->qx(qw(config core.sharedRepository 0664)); +is(git_umask_for($git_dir), oct '002', 'umask is correct for 664'); + +$git->qx(qw(config core.sharedRepository group)); +is(git_umask_for($git_dir), oct '007', 'umask is correct for "group"'); + +done_testing(); diff --git a/t/httpd-unix.t b/t/httpd-unix.t index 4b0f116e..5ebe2f50 100644 --- a/t/httpd-unix.t +++ b/t/httpd-unix.t @@ -4,6 +4,7 @@ use strict; use warnings; use Test::More; +use Carp qw(carp); foreach my $mod (qw(Plack::Util Plack::Builder Danga::Socket HTTP::Date HTTP::Status)) { @@ -54,7 +55,7 @@ ok(-S $unix, 'UNIX socket was bound by -httpd'); sub check_sock ($) { my ($unix) = @_; my $sock = IO::Socket::UNIX->new(Peer => $unix, Type => SOCK_STREAM); - warn "E: $! connecting to $unix\n" unless defined $sock; + carp "E: $! connecting to $unix\n" unless defined $sock; ok($sock, 'client UNIX socket connected'); ok($sock->write("GET /host-port HTTP/1.0\r\n\r\n"), 'wrote req to server'); @@ -95,11 +96,13 @@ SKIP: { eval 'require Net::Server::Daemonize'; skip('Net::Server missing for pid-file/daemonization test', 10) if $@; - # wait for daemonization + # wait for daemonization, PublicInbox::Daemon should bind + # listener BEFORE the grandparent exits. $spawn_httpd->("-l$unix", '-D', '-P', "$tmpdir/pid"); my $kpid = $pid; $pid = undef; is(waitpid($kpid, 0), $kpid, 'existing httpd terminated'); + ok(-S $unix, 'unix socket exists'); check_sock($unix); ok(-f "$tmpdir/pid", 'pid file written'); diff --git a/t/hval.t b/t/hval.t new file mode 100644 index 00000000..f824752c --- /dev/null +++ b/t/hval.t @@ -0,0 +1,20 @@ +# Copyright (C) 2015 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ (https://www.gnu.org/licenses/agpl-3.0.txt) +use strict; +use warnings; +use Test::More; +use PublicInbox::Hval qw(to_attr from_attr); + +foreach my $s ('Hello/World.pm', 'Zcat', 'hello world.c', 'Eléanor', '$at') { + my $attr = to_attr($s); + is(from_attr($attr), $s, "$s => $attr => $s round trips"); +} + +{ + my $bad = eval { to_attr('foo//bar') }; + my $err = $@; + ok(!$bad, 'double-slash rejected'); + like($err, qr/invalid filename:/, 'got exception message'); +} + +done_testing(); diff --git a/t/repo_git_search_idx.t b/t/repo_git_search_idx.t new file mode 100644 index 00000000..934a4e6f --- /dev/null +++ b/t/repo_git_search_idx.t @@ -0,0 +1,28 @@ +# Copyright (C) 2017 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +use Test::More; +use File::Temp qw/tempdir/; +use_ok 'PublicInbox::RepoGitSearchIdx'; +my $test = require './t/repobrowse_common_git.perl'; +my $git_dir = $test->{git_dir}; +my $xdir = "$git_dir/rg"; +my $idx = PublicInbox::RepoGitSearchIdx->new($git_dir, $xdir); +ok($idx->xdb && -d $xdir, 'Xapian dir created'); +$idx->index_sync; + +my $mset = $idx->query('bs:"add header"'); +my $doc; +$doc = $_->get_document foreach $mset->items; +ok($doc, 'got document'); +is('cb3b92d257e628b512a2eee0861f8935c594cd12', $doc->get_data, 'DATA OK'); + +foreach my $q (qw(id:cb3b92d257e628b512a2eee0861f8935c594cd12 id:cb3b92d2*)) { + $mset = $idx->query($q); + $doc = undef; + $doc = $_->get_document foreach $mset->items; + ok($doc, "got document for $q"); +} + +done_testing(); diff --git a/t/repobrowse.t b/t/repobrowse.t new file mode 100644 index 00000000..de8a7952 --- /dev/null +++ b/t/repobrowse.t @@ -0,0 +1,21 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; + +my $test = require './t/repobrowse_common_git.perl'; +test_psgi($test->{app}, sub { + my ($cb) = @_; + my $req = 'http://example.com/test.git/tree/dir'; + my $res = $cb->(GET($req . '/')); + is($res->code, 301, 'got 301 with trailing slash'); + is($res->header('Location'), $req, 'redirected without tslash'); + + my $q = '?id=deadbeef'; + + $res = $cb->(GET($req . "/$q")); + is($res->code, 301, 'got 301 with trailing slash + query string'); + is($res->header('Location'), $req.$q, 'redirected without tslash'); +}); + +done_testing(); diff --git a/t/repobrowse_common_git.perl b/t/repobrowse_common_git.perl new file mode 100644 index 00000000..de61efe6 --- /dev/null +++ b/t/repobrowse_common_git.perl @@ -0,0 +1,67 @@ +#!/usr/bin/perl -w +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +use Test::More; +use File::Temp qw/tempdir/; +use Cwd qw/getcwd/; +my @mods = qw(HTTP::Request::Common Plack::Test URI::Escape); +foreach my $mod (@mods) { + eval "require $mod"; + plan skip_all => "$mod missing for $0" if $@; +} + +sub dechunk ($) { + my ($res) = @_; + my $s = $res->content; + if (lc($res->header('Transfer-Encoding') || '') eq 'chunked') { + my $rv = ''; + while ($s =~ s/\A([a-f0-9]+)\r\n//i) { # no comment support :x + my $n = hex($1) or last; + $rv .= substr($s, 0, $n); + $s = substr($s, $n); + $s =~ s/\A\r\n// or die "broken parsing in $s\n"; + } + $s =~ s/\A\r\n// or die "broken end parsing in $s\n"; + $s = $rv; + } + $s; +} + +use_ok $_ foreach @mods; +my $git_dir = tempdir('repobrowse-XXXXXX', CLEANUP => 1, TMPDIR => 1); +my $psgi = "examples/repobrowse.psgi"; +my $repobrowse_config = "$git_dir/repobrowse_config"; +my $app; +ok(-f $psgi, 'psgi example for repobrowse.psgi found'); +{ + is(system(qw(git init -q --bare), $git_dir), 0, 'created git directory'); + my @cmd = ('git', "--git-dir=$git_dir", 'fast-import', '--quiet'); + my $fi_data = getcwd().'/t/git.fast-import-data'; + ok(-r $fi_data, "fast-import data readable (or run test at top level)"); + my $pid = fork; + defined $pid or die "fork failed: $!\n"; + if ($pid == 0) { + open STDIN, '<', $fi_data or die "open $fi_data: $!\n"; + exec @cmd; + die "failed exec: ",join(' ', @cmd),": $!\n"; + } + waitpid $pid, 0; + is($?, 0, 'fast-import succeeded'); + my $fh; + ok((open $fh, '>', $repobrowse_config and + print $fh '[repo "test.git"]', "\n", + "\t", "path = $git_dir", "\n" and + close $fh), 'created repobrowse config'); + local $ENV{REPOBROWSE_CONFIG} = $repobrowse_config; + ok($app = require $psgi, 'loaded PSGI app'); +} + +# return value +bless { + psgi => $psgi, + git_dir => $git_dir, + app => $app, + repobrowse_config => $repobrowse_config, +}, 'Repobrowse::TestGit'; diff --git a/t/repobrowse_git.t b/t/repobrowse_git.t new file mode 100644 index 00000000..0ac977f3 --- /dev/null +++ b/t/repobrowse_git.t @@ -0,0 +1,11 @@ +# Copyright (C) 2015 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ (https://www.gnu.org/licenses/agpl-3.0.txt) +use strict; +use warnings; +use Test::More; +use PublicInbox::RepoGit qw(git_unquote); + +is("foo\nbar", git_unquote('"foo\\nbar"'), 'unquoted newline'); +is("Eléanor", git_unquote('"El\\303\\251anor"'), 'unquoted octal'); + +done_testing(); diff --git a/t/repobrowse_git_atom.t b/t/repobrowse_git_atom.t new file mode 100644 index 00000000..6769bf9f --- /dev/null +++ b/t/repobrowse_git_atom.t @@ -0,0 +1,38 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +my $have_xml_feed = eval { require XML::Feed; 1 }; +my $test = require './t/repobrowse_common_git.perl'; +use Test::More; + +test_psgi($test->{app}, sub { + my ($cb) = @_; + my $req = 'http://example.com/test.git/atom'; + my $res = $cb->(GET($req)); + is($res->code, 200, 'got 200'); + is($res->header('Content-Type'), 'application/atom+xml', + 'got correct Content-Type'); + my $body = dechunk($res); + SKIP: { + skip 'XML::Feed missing', 2 unless $have_xml_feed; + my $p = XML::Feed->parse(\$body); + is($p->format, "Atom", "parsed atom feed"); + is(scalar $p->entries, 6, "parsed six entries"); + } + like($body, qr!<pre\s*[^>]+>\* header:\n add header</pre>!, + 'body wrapped in <pre>'); + + $res = $cb->(GET($req . '/master/foo.txt')); + is($res->code, 200, 'got 200'); + $body = dechunk($res); + like($body, qr{\bhref="http://[^/]+/test\.git/}, 'hrefs OK'); + SKIP: { + skip 'XML::Feed missing', 2 unless $have_xml_feed; + my $p = XML::Feed->parse(\$body); + is($p->format, "Atom", "parsed atom feed"); + is(scalar $p->entries, 4, "parsed 4 entries"); + } +}); + +done_testing(); diff --git a/t/repobrowse_git_commit.t b/t/repobrowse_git_commit.t new file mode 100644 index 00000000..f5913023 --- /dev/null +++ b/t/repobrowse_git_commit.t @@ -0,0 +1,19 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; + +my $test = require './t/repobrowse_common_git.perl'; +test_psgi($test->{app}, sub { + my ($cb) = @_; + my $path = '/path/to/something'; + my $req = 'http://example.com/test.git/commit'; + my $res; + + $res = $cb->(GET($req)); + is($res->code, 200, 'got proper 200 response for default'); + my $body = dechunk($res); + like($body, qr!</html>\z!, 'response body finished'); +}); + +done_testing(); diff --git a/t/repobrowse_git_httpd.t b/t/repobrowse_git_httpd.t new file mode 100644 index 00000000..3e6c074c --- /dev/null +++ b/t/repobrowse_git_httpd.t @@ -0,0 +1,138 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +# +# Integration test for public-inbox-httpd and (git) repobrowse +# since we may use some special APIs not available in other servers +use strict; +use warnings; +use Test::More; +foreach my $mod (qw(Danga::Socket HTTP::Date HTTP::Status + Plack::Test::ExternalServer)) { + eval "require $mod"; + plan skip_all => "$mod missing for repobrowse_git_httpd.t" if $@; +} +my $test = require './t/repobrowse_common_git.perl'; +{ + no warnings 'once'; + $Plack::Test::Impl = 'ExternalServer'; +} +use File::Temp qw/tempdir/; +use Cwd qw/getcwd/; +use IO::Socket; +use Fcntl qw(F_SETFD); +use POSIX qw(dup2); +my $tmpdir = tempdir('repobrowse_git_httpd-XXXXXX', TMPDIR => 1, CLEANUP => 1); +my $err = "$tmpdir/stderr.log"; +my $out = "$tmpdir/stdout.log"; +my $httpd = 'blib/script/public-inbox-httpd'; +my $psgi = getcwd() . '/' . $test->{psgi}; +my %opts = ( + LocalAddr => '127.0.0.1', + ReuseAddr => 1, + Proto => 'tcp', + Type => SOCK_STREAM, + Listen => 1024, +); +my $sock = IO::Socket::INET->new(%opts); +my $host = $sock->sockhost; +my $port = $sock->sockport; +my $uri = "http://$host:$port/"; +my $pid; +END { kill 'TERM', $pid if defined $pid }; +my $spawn_httpd = sub { + $pid = fork; + if ($pid == 0) { + # pretend to be systemd: + dup2(fileno($sock), 3) or die "dup2 failed: $!\n"; + my $t = IO::Handle->new_from_fd(3, 'r'); + $t->fcntl(F_SETFD, 0); + $ENV{REPOBROWSE_CONFIG} = $test->{repobrowse_config}; + $ENV{LISTEN_PID} = $$; + $ENV{LISTEN_FDS} = 1; + exec $httpd, '-W0', $psgi; + # exec $httpd, '-W0', "--stdout=$out", "--stderr=$err", $psgi; + die "FAIL: $!\n"; + } + ok(defined $pid, 'forked httpd process successfully'); +}; + +$spawn_httpd->(); + +{ # git clone tests + my $url = $uri . 'test.git'; + is(system(qw(git clone -q --mirror), $url, "$tmpdir/smart.git"), + 0, 'smart clone successful'); + is(system('git', "--git-dir=$tmpdir/smart.git", 'fsck'), 0, 'fsck OK'); + is(system('git', "--git-dir=$test->{git_dir}", + qw(config http.uploadpack 0)), 0, 'disabled smart HTTP'); + is(system('git', "--git-dir=$test->{git_dir}", + qw(update-server-info)), 0, 'enable dumb HTTP'); + is(system(qw(git clone -q --mirror), $url, "$tmpdir/dumb.git"), + 0, 'dumb clone successful'); + is(system('git', "--git-dir=$tmpdir/dumb.git", 'fsck'), + 0, 'fsck dumb OK'); +} + +test_psgi(uri => $uri, client => sub { + my ($cb) = @_; + my $res = $cb->(GET($uri . 'test.git/info/refs')); + is(200, $res->code, 'got info/refs'); + + $res = $cb->(GET($uri . 'best.git/info/refs')); + is(404, $res->code, 'bad request fails'); + + $res = $cb->(GET($uri . 'test.git/patch')); + is(200, $res->code, 'got patch'); + is('text/plain; charset=UTF-8', $res->header('Content-Type'), + 'got proper content-type with patch'); + + # ignore signature from git-format-patch: + my ($patch, undef) = split(/\n-- \n/s, $res->content); + + my $cmd = 'format-patch --signature=git -1 -M --stdout HEAD'; + my ($exp, undef) = split(/\n-- \n/s, + `git --git-dir=$test->{git_dir} $cmd`); + is($patch, $exp, 'patch content matches expected'); +}); + +{ + # allow reading description file + my %conn = ( PeerAddr => $host, PeerPort => $port, Proto => 'tcp', + Type => SOCK_STREAM); + my $conn = IO::Socket::INET->new(%conn); + ok($conn, "connected for description check"); + $conn->write("GET /test.git/description HTTP/1.0\r\n\r\n"); + ok($conn->read(my $buf, 8192), 'read response'); + my ($head, $body) = split(/\r\n\r\n/, $buf, 2); + like($head, qr!\AHTTP/1\.0 200 !s, 'got 200 response for description'); + + $conn = IO::Socket::INET->new(%conn); + ok($conn, "connected for range check"); + $conn->write("GET /test.git/description HTTP/1.0\r\n" . + "Range: bytes=5-\r\n\r\n"); + ok($conn->read($buf, 8192), 'read partial response'); + my ($h2, $b2) = split(/\r\n\r\n/, $buf, 2); + like($h2, qr!\AHTTP/1\.0 206 !s, 'got 206 response for range'); + is($b2, substr($body, 5), 'substring matches on 206'); +} + +test_psgi(uri => $uri, client => sub { + my ($cb) = @_; + my $res = $cb->(GET($uri . 'test.git/snapshot/test-master.tar.gz')); + is(200, $res->code, 'got gzipped tarball'); + my $got = "$tmpdir/got.tar.gz"; + my $exp = "$tmpdir/exp.tar.gz"; + open my $fh, '>', $got or die "open got.tar.gz: $!"; + print $fh $res->content; + close $fh or die "close failed: $!"; + $res = undef; + my $rc = system('git', "--git-dir=$test->{git_dir}", + qw(archive --prefix=test-master/ --format=tar.gz), + '-o', $exp, 'master'); + is(0, $rc, 'git-archive generated check correctly'); + is(0, system('cmp', $got, $exp), 'got expected gzipped tarball'); + +}); + +done_testing(); +1; diff --git a/t/repobrowse_git_log.t b/t/repobrowse_git_log.t new file mode 100644 index 00000000..2caba546 --- /dev/null +++ b/t/repobrowse_git_log.t @@ -0,0 +1,19 @@ +# Copyright (C) 2017 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +my $test = require './t/repobrowse_common_git.perl'; +use Test::More; + +test_psgi($test->{app}, sub { + my ($cb) = @_; + my $req = 'http://example.com/test.git/log'; + my $res = $cb->(GET($req)); + is($res->code, 200, 'got 200'); + is($res->header('Content-Type'), 'text/html; charset=UTF-8', + 'got correct Content-Type'); + my $body = dechunk($res); + like($body, qr!</html>!, 'valid HTML :)'); +}); + +done_testing(); diff --git a/t/repobrowse_git_raw.t b/t/repobrowse_git_raw.t new file mode 100644 index 00000000..aefe88c7 --- /dev/null +++ b/t/repobrowse_git_raw.t @@ -0,0 +1,24 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +my $test = require './t/repobrowse_common_git.perl'; + +test_psgi($test->{app}, sub { + my ($cb) = @_; + + my $req = 'http://example.com/test.git/raw/master/dir'; + my $res = $cb->(GET($req)); + is(200, $res->code, 'got 200 response from dir'); + my $noslash_body = dechunk($res); + like($noslash_body, qr{href="dir/dur">dur</a></li>}, + 'path ok w/o slash'); + + $req = 'http://example.com/test.git/raw/master/foo.txt'; + my $blob = $cb->(GET($req)); + like($blob->header('Content-Type'), qr!\Atext/plain\b!, + 'got text/plain blob'); + is($blob->content, "-----\nhello\nworld\n", 'raw blob passed'); +}); + +done_testing(); diff --git a/t/repobrowse_git_snapshot.t b/t/repobrowse_git_snapshot.t new file mode 100644 index 00000000..b608459e --- /dev/null +++ b/t/repobrowse_git_snapshot.t @@ -0,0 +1,46 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +my $test = require './t/repobrowse_common_git.perl'; + +test_psgi($test->{app}, sub { + my ($cb) = @_; + my ($req, $rc, $res); + + $req = 'http://example.com/test.git/snapshot/test-master.tar.gz'; + $res = $cb->(GET($req)); + is($res->code, 200, 'got 200 response from $NAME-master-tar.gz'); + is($res->header('Content-Type'), 'application/x-gzip', + 'Content-Type is as expected'); + + $req = 'http://example.com/test.git/snapshot/test-nonexistent.tar.gz'; + $res = $cb->(GET($req)); + is($res->code, 404, 'got 404 for non-existent'); + + $rc = system('git', "--git-dir=$test->{git_dir}", 'tag', '-a', + '-m', 'annotated tag!', 'v1.0.0'); + is($rc, 0, 'created annotated 1.0.0 tag'); + $req = 'http://example.com/test.git/snapshot/test-1.0.0.tar.gz'; + $res = $cb->(GET($req)); + is($res->code, 200, 'got 200 response for tag'); + is($res->header('Content-Type'), 'application/x-gzip', + 'Content-Type is as expected'); + is($res->header('Content-Disposition'), + 'inline; filename="test-1.0.0.tar.gz"', + 'Content-Disposition is as expected'); + + $rc = system('git', "--git-dir=$test->{git_dir}", 'tag', + '-m', 'lightweight tag!', 'v2.0.0'); + is($rc, 0, 'created lightweight 2.0.0 tag'); + $req = 'http://example.com/test.git/snapshot/test-2.0.0.tar.gz'; + $res = $cb->(GET($req)); + is($res->code, 200, 'got 200 response for tag'); + is($res->header('Content-Type'), 'application/x-gzip', + 'Content-Type is as expected'); + is($res->header('Content-Disposition'), + 'inline; filename="test-2.0.0.tar.gz"', + 'Content-Disposition is as expected'); +}); + +done_testing(); diff --git a/t/repobrowse_git_src.t b/t/repobrowse_git_src.t new file mode 100644 index 00000000..aa341d38 --- /dev/null +++ b/t/repobrowse_git_src.t @@ -0,0 +1,38 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use strict; +use warnings; +my $test = require './t/repobrowse_common_git.perl'; + +test_psgi($test->{app}, sub { + my ($cb) = @_; + + my $req = 'http://example.com/test.git/src/HEAD/dir'; + my $res = $cb->(GET($req)); + is(200, $res->code, 'got 200 response from dir'); + my $noslash_body = dechunk($res); + like($noslash_body, qr{href="dir/dur">dur/</a>}, + 'path ok w/o slash'); + + $req = 'http://example.com/test.git/src'; + $res = $cb->(GET($req)); + is(302, $res->code, 'got 302 response from dir'); + is("$req/master", $res->header('Location'), 'redirected to tip'); + + my $slash = $req . '/'; + my $r2 = $cb->(GET($slash)); + is(301, $r2->code, 'got 301 response from dir with slash'); + is($req, $r2->header('Location'), 'redirected w/o slash'); + + $req = 'http://example.com/test.git/src/master/foo.txt'; + my $blob = $cb->(GET($req)); + is($blob->header('Content-Type'), 'text/html; charset=UTF-8', + 'got text/html blob'); + + my $body = dechunk($blob); + foreach (qw(----- hello world)) { + ok(index($body, $_) >= 0, "substring $_ in body"); + } +}); + +done_testing(); @@ -27,31 +27,6 @@ my $rw_commit = sub { }; { - # git repository perms - is(PublicInbox::SearchIdx->_git_config_perm(undef), - &PublicInbox::SearchIdx::PERM_GROUP, - "undefined permission is group"); - is(PublicInbox::SearchIdx::_umask_for( - PublicInbox::SearchIdx->_git_config_perm('0644')), - 0022, "644 => umask(0022)"); - is(PublicInbox::SearchIdx::_umask_for( - PublicInbox::SearchIdx->_git_config_perm('0600')), - 0077, "600 => umask(0077)"); - is(PublicInbox::SearchIdx::_umask_for( - PublicInbox::SearchIdx->_git_config_perm('0640')), - 0027, "640 => umask(0027)"); - is(PublicInbox::SearchIdx::_umask_for( - PublicInbox::SearchIdx->_git_config_perm('group')), - 0007, 'group => umask(0007)'); - is(PublicInbox::SearchIdx::_umask_for( - PublicInbox::SearchIdx->_git_config_perm('everybody')), - 0002, 'everybody => umask(0002)'); - is(PublicInbox::SearchIdx::_umask_for( - PublicInbox::SearchIdx->_git_config_perm('umask')), - umask, 'umask => existing umask'); -} - -{ my $root = Email::MIME->create( header_str => [ Date => 'Fri, 02 Oct 1993 00:00:00 +0000', @@ -95,15 +70,8 @@ sub filter_mids { is($found->mid, 'root@s', 'mid set correctly'); ok(int($found->thread_id) > 0, 'thread_id is an integer'); + my ($res, @res); my @exp = sort qw(root@s last@s); - my $res = $ro->query("path:hello_world"); - my @res = filter_mids($res); - is_deeply(\@res, \@exp, 'got expected results for path: match'); - - foreach my $p (qw(hello hello_ hello_world2 hello_world_)) { - $res = $ro->query("path:$p"); - is($res->{total}, 0, "path variant `$p' does not match"); - } $res = $ro->query('s:(Hello world)'); @res = filter_mids($res); @@ -81,17 +81,6 @@ use PublicInbox::Spawn qw(which spawn popen_rd); isnt($?, 0, '$? set properly: '.$?); } -{ - my ($fh, $pid) = popen_rd([qw(sleep 60)], undef, { Blocking => 0 }); - ok(defined $pid && $pid > 0, 'returned pid when array requested'); - is(kill(0, $pid), 1, 'child process is running'); - ok(!defined(sysread($fh, my $buf, 1)) && $!{EAGAIN}, - 'sysread returned quickly with EAGAIN'); - is(kill(9, $pid), 1, 'child process killed early'); - is(waitpid($pid, 0), $pid, 'child process reapable'); - isnt($?, 0, '$? set properly: '.$?); -} - done_testing(); 1; diff --git a/t/thread-all.t b/t/thread-all.t index 8ccf4f8c..b1f9b47c 100644 --- a/t/thread-all.t +++ b/t/thread-all.t @@ -1,7 +1,7 @@ # Copyright (C) 2016 all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # -# real-world testing of search threading +# real-world testing of search threading performance use strict; use warnings; use Test::More; @@ -16,7 +16,6 @@ plan skip_all => "$pi_dir not initialized for $0" if $@; require PublicInbox::View; require PublicInbox::SearchThread; -my $pfx = PublicInbox::Search::xpfx('thread'); my $opts = { limit => 1000000, asc => 1 }; my $t0 = clock_gettime(CLOCK_MONOTONIC); my $elapsed; @@ -35,4 +34,6 @@ PublicInbox::View::thread_results($msgs); $elapsed = clock_gettime(CLOCK_MONOTONIC) - $t0; diag "thread_results $elapsed"; +ok(1, 'test completed without crashing :)'); + done_testing(); |