# Copyright (C) 2016 all contributors # License: AGPL-3.0+ # # 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;