The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;

use Test::More;

use lib 't/lib';
use TestPR;
use TestPR2;
use TestDataHandler;
use Try::Tiny;
use HTTP::Response;
use HTTP::Request;
use HTTP::Message::PSGI;

my $dh = TestDataHandler->new;

my $auth_info = $dh->create_or_update_auth_info(
    client_id    => q{foo},
    user_id      => q{1},
    scope        => q{email},
    code         => q{code_bar},
);

my $access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
);

my $app = TestPR->new;

sub request {
    my $req = shift;
    my $res = try {
        HTTP::Response->from_psgi($app->($req->to_psgi));
    } catch {
        HTTP::Response->from_psgi([500, ["Content-Type" => "text/plain"], [ $_ ]]);
    };
    return $res;
}

my ($req, $res);
# LEGACY
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{OAuth %s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: 'email', claims: ["user_id","email"], is_legacy: '1'}}, 'successful response');

$req = HTTP::Request->new("POST" => q{http://example.org/});
$req->content_type('application/x-www-form-urlencoded');
$req->content(sprintf(q{oauth_token=%s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: 'email', claims: ["user_id","email"], is_legacy: '1'}}, 'successful response');

$req = HTTP::Request->new("GET" => sprintf(q{http://example.org/?oauth_token=%s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: 'email', claims: ["user_id","email"], is_legacy: '1'}}, 'successful response');

# RFC
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: 'email', claims: ["user_id","email"], is_legacy: '0'}}, 'successful response');

$req = HTTP::Request->new("POST" => q{http://example.org/});
$req->content_type('application/x-www-form-urlencoded');
$req->content(sprintf(q{access_token=%s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: 'email', claims: ["user_id","email"], is_legacy: '0'}}, 'successful response');

$req = HTTP::Request->new("GET" => sprintf(q{http://example.org/?access_token=%s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: 'email', claims: ["user_id","email"], is_legacy: '0'}}, 'successful response');

# no parammethod
$req = HTTP::Request->new("GET" => q{http://example.org/});
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 400, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_request"}, 'invalid request');

# no token
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => q{Bearer  ,});
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 400, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_request"}, 'invalid request');

# invalid token
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => q{Bearer invalid});
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_token"}, 'invalid token');

# invalid token (croak)
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => q{Bearer token_for_croak});
$res = &request($req);
ok(!$res->is_success, 'request should fail');
like($res->content, qr/OIDC::Lite::Server::DataHandler::get_access_token doesn't return OAuth::Lite2::Model::AccessToken/, 'croak');
is($res->code, 500, 'error code');
ok(!$res->header("WWW-Authenticate"), 'no WWW-Authenticate Header');

# expired token
$access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
    expires_in => -60,
);
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_token", error_description="The access token expired"}, 'invalid token');
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{OAuth %s}, $access_token->token));
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{OAuth realm="resource.example.org", error="expired_token"}, 'invalid token');

$auth_info->id($auth_info->id + 1);
$access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
);
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_token"}, 'invalid token');

$auth_info->id(99);
$access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
);
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok(!$res->is_success, 'request should fail');
like($res->content, qr/OIDC::Lite::Server::DataHandler::get_auth_info_by_id doesn't return OIDC::Lite::Model::AuthInfo/, 'croak');
is($res->code, 500, 'error code');
ok(!$res->header("WWW-Authenticate"), 'no WWW-Authenticate Header');

$auth_info = $dh->create_or_update_auth_info(
    client_id    => q{malformed},
    user_id      => q{1},
    scope        => q{email},
    code         => q{code_bar},
);
$access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
);
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_token"}, 'invalid token');

$auth_info = $dh->create_or_update_auth_info(
    client_id    => q{foo},
    user_id      => q{666},
    scope        => q{email},
    code         => q{code_bar},
);
$access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
);
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer realm="resource.example.org", error="invalid_token"}, 'invalid token');

# no scope
$auth_info = $dh->create_or_update_auth_info(
    client_id    => q{foo},
    user_id      => q{1},
    code         => q{code_bar},
);
$access_token = $dh->create_or_update_access_token(
    auth_info => $auth_info,
);
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => sprintf(q{Bearer %s}, $access_token->token));
$res = &request($req);
ok($res->is_success, 'request should not fail');
is($res->content, q{{user: '1', scope: '', claims: [], is_legacy: '0'}}, 'successful response');

# no realm and 
$app = TestPR2->new;
$req = HTTP::Request->new("GET" => q{http://example.org/});
$req->header("Authorization" => q{Bearer invalid});
$res = &request($req);
ok(!$res->is_success, 'request should fail');
ok(!$res->content, 'no response content');
is($res->code, 401, 'error code');
is($res->header("WWW-Authenticate"), q{Bearer error="invalid_token", error_uri="http://resource.example.org/error"}, 'invalid token');

done_testing;