The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl -w

use strict;
use Test::More tests => 33;
#use Test::More 'no_plan';
use Plack::Test;
use URI;

BEGIN { use_ok 'Plack::Middleware::MethodOverride' or die; }

my $base_app = sub {
    my $env = shift;
    return [
        200,
        ['Content-Type' => 'text/plain'],
        [ "$env->{REQUEST_METHOD} ($env->{'plack.original_request_method'})" ]
    ];
};
ok my $app = Plack::Middleware::MethodOverride->wrap($base_app),
    'Create MethodOverride app with no args';

my $uri = URI->new('/');

test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(GET => $uri));
    is $res->content, 'GET (GET)', 'GET should be GET';
};

test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(PUT => $uri));
    is $res->content, 'PUT (PUT)', 'PUT should be PUT';
};

test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => $uri));
    is $res->content, 'POST (POST)', 'POST should be POST';
};

# Override over POST.
$uri->query_form('x-tunneled-method' => 'PUT');
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => $uri));
    is $res->content, 'PUT (POST)', 'Should send PUT over POST';
};

test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(GET => $uri));
    is $res->content, 'GET (GET)', 'Should not send PUT over GET';
};

# Try to confuse the parser.
$uri->query_form('foo' => 'x-tunneled-method', name => 'Scott');
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => $uri));
    is $res->content, 'POST (POST)', 'POST should be POST with no tunnel';
};

# Override with a DELETE
$uri->query_form('x-tunneled-method' => 'DELETE', PUT => 'x-tunneled-method');
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => $uri));
    is $res->content, 'DELETE (POST)', 'Should send DELETE over POST';
};

##############################################################################
# Now try with a header.
my $head =  ['x-http-method-override' => 'PUT'];
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => '/', $head));
    is $res->content, 'PUT (POST)', 'Should send PUT over POST via header';
};

test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(GET => '/', $head));
    is $res->content, 'GET (GET)', 'Should not send PUT over GET via header';
};

# Try a different method.
$head->[1] = 'OPTIONS';
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => '/', $head));
    is $res->content, 'OPTIONS (POST)', 'Should send OPTIONS over POST via header';
};

# Make sure all supported methods work.
for my $meth (qw(GET HEAD PUT DELETE OPTIONS TRACE CONNECT)) {
    $head->[1] = $meth;
    test_psgi $app, sub {
        my $res = shift->(HTTP::Request->new(POST => '/', $head));
        is $res->content, "$meth (POST)", "Should support $meth";
    };

    # Lowercase too.
    $head->[1] = lc $meth;
    test_psgi $app, sub {
        my $res = shift->(HTTP::Request->new(POST => '/', $head));
        is $res->content, "$meth (POST)", "Should support $meth";
    };
}

# Make sure no other methods are allowed.
for my $meth (qw(FOO SUCK CALL EXEC)) {
    $head->[1] = $meth;
    test_psgi $app, sub {
        my $res = shift->(HTTP::Request->new(POST => '/', $head));
        is $res->content, "POST (POST)", "Should not support $meth";
    };
}
##############################################################################
# Now modify the param and the header.
ok $app = Plack::Middleware::MethodOverride->wrap(
    $base_app,
    param => 'x-do-this',
    header => 'X-Do-It-Man',
), 'Create MethodOverride app with no param and header params';

$uri->query_form('x-do-this' => 'PUT');
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => $uri));
    is $res->content, 'PUT (POST)', 'Should get PUT for custom param'
};

$head = ['X-Do-It-man' => 'DELETE'];
test_psgi $app, sub {
    my $res = shift->(HTTP::Request->new(POST => '/', $head));
    is $res->content, 'DELETE (POST)', 'Should send DELETE over POST via custom header';
};