view release on metacpan or search on metacpan
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
t/Plack-Middleware-APISchema-ResponseValidator.t
t/fixtures/author.def
t/fixtures/bmi.def
t/fixtures/boolean.def
t/fixtures/example-null.def
t/fixtures/family.def
t/fixtures/runtime-error.def
t/fixtures/status.def
t/fixtures/syntax-error.def
t/fixtures/user.def
t/test.pm
t/test/InheritedDocument.pm
t/test/fixtures.pm
META.yml
MANIFEST
"Text::Markdown::Hoedown" : "0",
"Text::MicroTemplate" : "0",
"Text::MicroTemplate::DataSection" : "0",
"Text::MicroTemplate::Extended" : "0",
"URI::Escape" : "0",
"URL::Encode" : "0",
"Valiemon" : "0.04",
"perl" : "5.014"
}
},
"test" : {
"requires" : {
"HTTP::Request::Common" : "0",
"Path::Class" : "0",
"Test::Class" : "0",
"Test::Deep" : "0",
"Test::Deep::JSON" : "0",
"Test::Fatal" : "0",
"Test::More" : "0.98"
}
}
requires 'URL::Encode';
requires 'HTML::Escape';
requires 'Text::MicroTemplate';
requires 'Text::MicroTemplate::Extended';
requires 'Text::MicroTemplate::DataSection';
requires 'Text::Markdown::Hoedown';
requires 'HTTP::Message';
requires 'Valiemon', '0.04';
requires 'URI::Escape';
on 'test' => sub {
requires 'Path::Class';
requires 'Test::More', '0.98';
requires 'Test::Class';
requires 'Test::Deep';
requires 'Test::Fatal';
requires 'Test::Deep::JSON';
requires 'HTTP::Request::Common';
};
t/APISchema-DSL.t view on Meta::CPAN
package t::APISchema::Generator::Router::Simple;
use lib '.';
use t::test;
use Encode qw(decode_utf8);
sub _require : Test(startup => 1) {
my ($self) = @_;
BEGIN{ use_ok 'APISchema::DSL'; }
}
sub no_global : Tests {
dies_ok {
filter {};
};
dies_ok {
title 'test';
};
dies_ok {
description 'test';
};
dies_ok {
resource 'test' => ();
};
dies_ok {
GET '/' => ();
};
dies_ok {
POST '/' => ();
};
t/APISchema-DSL.t view on Meta::CPAN
sub process : Tests {
lives_ok {
my $schema = APISchema::DSL::process {};
isa_ok $schema, 'APISchema::Schema';
};
dies_ok {
GET '/' => ();
};
subtest 'title, description' => sub {
lives_ok {
my $schema = APISchema::DSL::process {
title 'BMI API';
description 'The API to calculate BMI';
};
isa_ok $schema, 'APISchema::Schema';
is $schema->title, 'BMI API';
is $schema->description, 'The API to calculate BMI';
};
};
subtest 'Simple GET' => sub {
lives_ok {
my $schema = APISchema::DSL::process {
GET '/' => {
title => 'Simple GET',
destination => { some => 'property' },
};
};
isa_ok $schema, 'APISchema::Schema';
my $routes = $schema->get_routes;
is scalar @$routes, 1;
is $routes->[0]->route, '/';
is $routes->[0]->title, 'Simple GET';
is_deeply $routes->[0]->destination, { some => 'property' };
};
};
subtest 'Support PATCH' => sub {
lives_ok {
my $schema = APISchema::DSL::process {
PATCH '/my' => {
title => 'Update My BMI',
destination => {
controller => 'BMI',
action => 'update',
},
};
};
t/APISchema-DSL.t view on Meta::CPAN
is $routes->[0]->route, '/my';
is $routes->[0]->title, 'Update My BMI';
is $routes->[0]->method, 'PATCH';
is_deeply $routes->[0]->destination, {
controller => 'BMI',
action => 'update',
};
};
};
subtest 'Validation should be returned' => sub {
lives_ok {
my $schema = APISchema::DSL::process {
title 'BMI API';
description 'The API to calculate BMI';
resource figure => {
type => 'object',
description => 'Figure, which includes weight and height',
properties => {
weight => {
t/APISchema-Generator-Markdown-Formatter.t view on Meta::CPAN
package t::APISchema::Generator::Markdown::Formatter;
use lib '.';
use t::test;
use t::test::fixtures;
use APISchema::Generator::Markdown::Formatter ();
sub _type : Tests {
for my $case (
[{} => 'undefined'],
[{type => 'object'} => '`object`'],
[{type => ['object', 'number']} => '`"object"`|`"number"`'],
[{'$ref' => '#/resource/foo'} => '[`foo`](#resource-foo)'],
[{oneOf => [{ type =>'object'}, {type =>'number'}]} => '`object`|`number`'],
t/APISchema-Generator-Markdown.t view on Meta::CPAN
package t::APISchema::Generator::Markdown;
use lib '.';
use t::test;
use t::test::fixtures;
use utf8;
use APISchema::DSL;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema::Generator::Markdown';
}
sub instantiate : Tests {
my $generator = APISchema::Generator::Markdown->new;
isa_ok $generator, 'APISchema::Generator::Markdown';
}
sub generate : Tests {
subtest 'Simple' => sub {
my $schema = t::test::fixtures::prepare_bmi;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr{# BMI API};
like $markdown, qr{^\Q - [BMI API](#route-BMI%20API) - `POST` /bmi\E$}m;
like $markdown, qr!{"height":1.6,"weight":50}!;
like $markdown, qr!|`.` |`object` | | |`required["value"]` |Body mass index |!;
like $markdown, qr!|`.height` |`number` | |`1.6` | |Height(m) |!;
};
subtest 'Complex' => sub {
my $schema = t::test::fixtures::prepare_family;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr{# Family API};
like $markdown, qr!GET /person[?]name=Alice!;
like $markdown, qr!{"message":"OK","status":"success"}!;
like $markdown, qr![{"age":16,"name":"Alice"},{"age":14,"name":"Charlie"}]!;
my $text1 = <<EOS;
|`.` |`array` | |`[{"age":16,"name":"Alice"},{"age":14,"name":"Charlie"}]` | |Some people |
t/APISchema-Generator-Markdown.t view on Meta::CPAN
EOS
like $markdown, qr!\Q$text1\E!;
like $markdown, qr!|`.\[\]` |[`person`](#resource-person) | | | | |!;
my $text2 = <<EOS;
200 OK
[{"age":16,"name":"Alice"},{"age":14,"name":"Charlie"}]
EOS
like $markdown, qr!\Q$text2\E!;
};
subtest 'Status switch' => sub {
my $schema = t::test::fixtures::prepare_status;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr{#### Response `200 OK`};
like $markdown, qr{\nHTTP/1.1 200 OK\nSucceeded!\n};
like $markdown, qr{#### Response `400 Bad Request`};
};
subtest 'example with no containers' => sub {
my $schema = APISchema::DSL::process {
resource gender => {
enum => ['male', 'female', 'other'],
example => 'other',
};
};
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr/^"other"$/m;
};
subtest 'FETCH endpoint' => sub {
my $schema = APISchema::DSL::process {
FETCH '/' => {
title => 'Fetch',
destination => {},
};
};
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr{\Q- [Fetch](#route-Fetch) - `GET`, `HEAD` /\E}, 'FETCH expanded to GET and HEAD';
};
}
sub generate_utf8 : Tests {
subtest 'Simple' => sub {
my $schema = t::test::fixtures::prepare_user;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr!{\n "first_name" : "å°é£¼",\n "last_name" : "å¼¾"\n}!;
like $markdown, qr!\Q|`.last_name` |`string` | |`"å¼¾"` | |å |\E!;
};
}
sub boolean : Tests {
my $schema = t::test::fixtures::prepare_boolean;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr!\btrue\b!;
}
sub example_null : Tests {
my $schema = t::test::fixtures::prepare_example_null;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr!"value" : null!;
like $markdown, qr!\Q|`.value` |`null` | |`null` | |The Value |\E!;
}
t/APISchema-Generator-Router-Simple.t view on Meta::CPAN
package t::APISchema::Generator::Router::Simple;
use lib '.';
use t::test;
use t::test::fixtures;
use APISchema::Schema;
use Router::Simple;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema::Generator::Router::Simple';
}
sub instantiate : Tests {
my $generator = APISchema::Generator::Router::Simple->new;
isa_ok $generator, 'APISchema::Generator::Router::Simple';
}
sub generate : Tests {
my $schema = t::test::fixtures::prepare_bmi;
my $generator = APISchema::Generator::Router::Simple->new;
my $router = $generator->generate_router($schema);
isa_ok $router, 'Router::Simple';
cmp_deeply $router->match({ PATH_INFO => '/bmi', HTTP_HOST => 'localhost', REQUEST_METHOD => 'POST' } ), {
controller => 'BMI',
action => 'calculate',
};
note $router->as_string;
}
sub inject_routes : Tests {
my $schema = t::test::fixtures::prepare_bmi;
my $router = Router::Simple->new;
$router->connect('/', {controller => 'Root', action => 'show'});
my $generator = APISchema::Generator::Router::Simple->new;
my $returned_router = $generator->inject_routes($schema => $router);
is $returned_router, $router;
isa_ok $router, 'Router::Simple';
t/APISchema-JSON.t view on Meta::CPAN
package t::APISchema::JSON;
use lib '.';
use t::test;
sub _require : Test(startup => 1) {
my ($self) = @_;
BEGIN{ use_ok 'APISchema::JSON'; }
}
sub _encode_json_canonical : Tests {
is APISchema::JSON::encode_json_canonical({b => 2, c => 3, a => 1}), '{"a":1,"b":2,"c":3}', 'keys are sorted';
is APISchema::JSON::encode_json_canonical({nested => {b => 2, c => 3, a => 1}}), '{"nested":{"a":1,"b":2,"c":3}}', 'nested keys are sorted';
t/APISchema-Resource.t view on Meta::CPAN
package t::APISchema::Resource;
use lib '.';
use t::test;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema::Resource';
}
sub instantiate : Tests {
my $resource = APISchema::Resource->new(
title => 'Human',
t/APISchema-Route.t view on Meta::CPAN
package t::APISchema::Route;
use lib '.';
use t::test;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema::Route';
}
sub instantiate : Tests {
my $route = APISchema::Route->new(
route => '/bmi/',
t/APISchema-Route.t view on Meta::CPAN
controller => 'BMI',
action => 'calculate',
},
method => 'POST',
request_resource => 'health',
response_resource => 'bmi',
);
}
sub responsible_codes : Tests {
subtest 'when simple response resource' => sub {
my $route = APISchema::Route->new(
route => '/bmi/',
title => 'BMI API',
description => 'This API calculates your BMI.',
destination => {
controller => 'BMI',
action => 'calculate',
},
method => 'POST',
request_resource => 'health',
response_resource => 'bmi',
);
cmp_deeply $route->responsible_codes, [200];
is $route->default_responsible_code, 200;
ok ! $route->responsible_code_is_specified;
};
subtest 'when multiple response codes are specified' => sub {
my $route = APISchema::Route->new(
route => '/bmi/',
title => 'BMI API',
description => 'This API calculates your BMI.',
destination => {
controller => 'BMI',
action => 'calculate',
},
method => 'POST',
request_resource => 'health',
t/APISchema-Schema.t view on Meta::CPAN
package t::APISchema::Schema;
use lib '.';
use t::test;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema::Schema';
}
sub instantiate : Tests {
my $schema = APISchema::Schema->new;
isa_ok $schema, 'APISchema::Schema';
t/APISchema-Schema.t view on Meta::CPAN
);
is $schema->get_resource_by_name('not_user'), undef;
cmp_deeply $schema->get_resources, [
$schema->get_resource_by_name('user'),
];
}
sub route : Tests {
subtest 'Basic' => sub {
my $schema = APISchema::Schema->new;
cmp_deeply $schema->get_routes, [];
$schema->register_route(
route => '/bmi/',
description => 'This API calculates your BMI.',
destination => {
controller => 'BMI',
action => 'calculate',
},
t/APISchema-Schema.t view on Meta::CPAN
controller => 'BMI',
action => 'calculate',
},
method => 'POST',
request_resource => 'health',
response_resource => 'bmi',
),
];
};
subtest 'Naming' => sub {
my $schema = APISchema::Schema->new;
cmp_deeply $schema->get_routes, [];
$schema->register_route(
title => 'BMI API',
route => '/bmi/',
);
is $schema->get_routes->[0]->title, 'BMI API';
$schema->register_route(
t/APISchema-Validator.t view on Meta::CPAN
package t::Plack::Middleware::APISchema::ResponseValidator;
use lib '.';
use t::test;
use t::test::fixtures;
use JSON::XS qw(encode_json);
sub _require : Test(startup => 1) {
use_ok 'APISchema::Validator';
}
sub instantiate : Tests {
subtest 'For request' => sub {
my $validator = APISchema::Validator->for_request;
isa_ok $validator, 'APISchema::Validator';
is $validator->validator_class, 'Valiemon';
is $validator->fetch_resource_method, 'canonical_request_resource';
};
subtest 'For response' => sub {
my $validator = APISchema::Validator->for_response;
isa_ok $validator, 'APISchema::Validator';
is $validator->validator_class, 'Valiemon';
is $validator->fetch_resource_method, 'canonical_response_resource';
};
subtest 'Result' => sub {
my $r = APISchema::Validator::Result->new;
isa_ok $r, 'APISchema::Validator::Result';
my $valid = APISchema::Validator::Result->new_valid;
isa_ok $valid, 'APISchema::Validator::Result';
my $error = APISchema::Validator::Result->new_valid;
isa_ok $error, 'APISchema::Validator::Result';
};
}
sub result : Tests {
subtest 'empty' => sub {
my $r = APISchema::Validator::Result->new;
ok $r->is_valid;
is_deeply $r->errors, {};
};
subtest 'valid without target' => sub {
my $r = APISchema::Validator::Result->new_valid;
ok $r->is_valid;
is_deeply $r->errors, {};
};
subtest 'valid with targets' => sub {
my $r = APISchema::Validator::Result->new_valid(qw(foo));
ok $r->is_valid;
is_deeply $r->errors, {};
};
subtest 'error without target' => sub {
my $r = APISchema::Validator::Result->new_error;
ok !$r->is_valid;
};
subtest 'error without target' => sub {
my $r = APISchema::Validator::Result->new_error(foo => 'bar');
ok !$r->is_valid;
is_deeply $r->errors, { foo => 'bar' };
};
subtest 'merge' => sub {
my $r = APISchema::Validator::Result->new;
$r->merge(APISchema::Validator::Result->new_valid());
ok $r->is_valid;
is_deeply $r->errors, {};
$r->merge(APISchema::Validator::Result->new_valid(qw(foo)));
ok $r->is_valid;
is_deeply $r->errors, {};
t/APISchema-Validator.t view on Meta::CPAN
},
response_resource => {
encoding => { 'application/json' => 'json' },
map { $_ => 'bmi' } @$keys
},
);
return $schema;
}
sub validate_request : Tests {
subtest 'valid with emtpy schema' => sub {
my $schema = APISchema::Schema->new;
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { foo => 'bar' },
parameter => 'foo&bar',
body => '{"foo":"bar"}',
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'valid with empty target' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, [];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {}, $schema);
ok $result->is_valid;
};
subtest 'valid with some target and schema' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid with missing property' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('json') ];
};
subtest 'invalid without body' => sub {
for my $value ({}, '', undef) {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => $value,
}, $schema);
ok ! $result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
}
};
subtest 'invalid without parameter' => sub {
for my $value ({}, '', undef) {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['parameter'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
parameter => $value,
}, $schema);
ok ! $result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'parameter' ];
}
};
subtest 'invalid with wrong encoding' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('url_parameter') ];
};
subtest 'invalid with invalid encoding' => sub {
my $schema = _invalid_encoding_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{message} } values %{$result->errors} ],
[ ('Unknown decoding method: hoge') ];
};
subtest 'valid with forced encoding' => sub {
my $schema = _forced_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok $result->is_valid;
};
subtest 'valid with strict content-type check' => sub {
my $schema = _strict_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid with wrong content type' => sub {
my $schema = _strict_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $content_type = 'application/x-www-form-urlencoded';
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => $content_type,
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{message} } values %{$result->errors} ],
[ ("Wrong content-type: $content_type") ];
};
subtest 'valid parameter' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['parameter'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
parameter => 'weight=50&height=1.6',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid parameter' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['parameter'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
parameter => 'weight=50',
}, $schema);
ok !$result->is_valid;
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('url_parameter') ];
};
subtest 'valid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50, height => 1.6 },
}, $schema);
ok $result->is_valid;
};
subtest 'invalid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50 },
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'header' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('perl') ];
};
subtest 'all valid' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body', 'parameter', 'header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50, height => 1.6 },
parameter => 'weight=50&height=1.6',
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'many invalid' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body', 'parameter', 'header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50 },
parameter => 'weight=50',
body => encode_json({weight => 50}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is scalar keys %{$result->errors}, 3;
is_deeply [ sort keys %{$result->errors} ],
[ qw(body header parameter) ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') x 3 ];
is_deeply [ sort map { $_->{encoding} } values %{$result->errors} ],
[ ('json', 'perl', 'url_parameter') ];
};
}
sub validate_response : Tests {
subtest 'valid with emtpy schema' => sub {
my $schema = APISchema::Schema->new;
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => { foo => 'bar' },
body => '{"foo":"bar"}',
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'valid with empty target' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, [];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {}, $schema);
ok $result->is_valid;
};
subtest 'valid with some target and schema' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid with missing property' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({hoge => 'foo'}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('json') ];
};
subtest 'invalid with wrong encoding' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('url_parameter') ];
};
subtest 'invalid with invalid encoding' => sub {
my $schema = _invalid_encoding_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{message} } values %{$result->errors} ],
[ ('Unknown decoding method: hoge') ];
};
subtest 'valid with forced encoding' => sub {
my $schema = _forced_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok $result->is_valid;
};
subtest 'valid with strict content-type check' => sub {
my $schema = _strict_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid with wrong content type' => sub {
my $schema = _strict_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $content_type = 'application/x-www-form-urlencoded';
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => $content_type,
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{message} } values %{$result->errors} ],
[ ("Wrong content-type: $content_type") ];
};
subtest 'valid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => { value => 19.5 },
}, $schema);
ok $result->is_valid;
};
subtest 'invalid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => {},
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'header' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('perl') ];
};
subtest 'all valid' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body', 'header'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => { value => 19.5 },
body => encode_json({value => 19.5}),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'many invalid' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body', 'header'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => {},
body => encode_json({}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is scalar keys %{$result->errors}, 2;
is_deeply [ sort keys %{$result->errors} ],
[ qw(body header) ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') x 2 ];
is_deeply [ sort map { $_->{encoding} } values %{$result->errors} ],
[ ('json', 'perl') ];
};
subtest 'valid referenced resource' => sub {
my $schema = _forced_route t::test::fixtures::prepare_family, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('Children GET API' => {
body => encode_json([ {
name => 'Alice',
age => 16,
}, {
name => 'Charlie',
age => 14,
} ]),
content_type => 'application/json',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid referenced resource' => sub {
my $schema = _forced_route t::test::fixtures::prepare_family, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('Children GET API' => {
body => encode_json([ {
name => 'Alice',
age => 16,
}, {
age => 14,
} ]),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
};
SKIP: {
skip 'Recursive dereference is not implemented in Valiemon', 2;
subtest 'valid recursively referenced resource' => sub {
my $schema = _forced_route t::test::fixtures::prepare_family, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('Children GET API' => {
parameter => 'name=Bob',
}, $schema);
ok $result->is_valid;
};
subtest 'invalid recursively referenced resource' => sub {
my $schema = _forced_route t::test::fixtures::prepare_family, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('Children GET API' => {
parameter => 'person=Bob',
}, $schema);
ok !$result->is_valid;
};
};
}
sub status : Tests {
my $schema = t::test::fixtures::prepare_status;
my $validator = APISchema::Validator->for_response;
subtest 'Status 200 with valid body' => sub {
my $result = $validator->validate('Get API' => {
status_code => 200,
body => '200 OK',
}, $schema);
ok $result->is_valid;
};
subtest 'Status 200 with invalid body' => sub {
my $result = $validator->validate('Get API' => {
status_code => 200,
body => { status => 200, message => 'OK' },
}, $schema);
ok !$result->is_valid;
};
subtest 'Status 400 with valid body' => sub {
my $result = $validator->validate('Get API' => {
status_code => 400,
body => encode_json({ status => 400, message => 'Bad Request' }),
}, $schema);
ok $result->is_valid;
};
subtest 'Status 400 with invalid body' => sub {
my $result = $validator->validate('Get API' => {
status_code => 400,
body => '400 Bad Request',
}, $schema);
ok !$result->is_valid;
};
subtest 'Undefined status' => sub {
my $result = $validator->validate('Get API' => {
status_code => 599,
body => { foo => 'bar' },
}, $schema);
ok $result->is_valid;
};
}
t/APISchema.t view on Meta::CPAN
package t::APISchema;
use lib '.';
use t::test;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema';
}
sub version : Tests {
cmp_ok $APISchema::VERSION, '>', 0, 'has positive version';
}
t/Plack-App-APISchema-Document.t view on Meta::CPAN
package t::Plack::App::APISchema::Document;
use lib '.';
use t::test;
use t::test::fixtures;
use t::test::InheritedDocument;
use Plack::Test;
use HTTP::Request::Common;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'Plack::App::APISchema::Document';
}
sub instantiate : Tests {
my $schema = APISchema::Schema->new;
my $app = Plack::App::APISchema::Document->new(schema => $schema);
isa_ok $app, 'Plack::App::APISchema::Document';
is $app->schema, $schema;
}
sub serve_document : Tests {
my $schema = t::test::fixtures::prepare_bmi;
my $app = Plack::App::APISchema::Document->new(schema => $schema)->to_app;
subtest 'when valid request' => sub {
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/');
is $res->code, 200;
is $res->header('content-type'), 'text/html; charset=utf-8';
like $res->content, qr{<h3 id="toc_8"><a name="resource-figure"></a> <code>figure</code> : <code>object</code></h3>};
done_testing;
}
};
}
sub mojibake : Tests {
my $schema = t::test::fixtures::prepare_author;
my $app = Plack::App::APISchema::Document->new(schema => $schema)->to_app;
subtest 'when valid request' => sub {
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/');
is $res->code, 200;
is $res->header('content-type'), 'text/html; charset=utf-8';
like $res->content, qr{td>èè
</td>};
done_testing;
}
};
}
sub inheritable : Tests {
my $schema = t::test::fixtures::prepare_bmi;
my $app = t::test::InheritedDocument->new(schema => $schema)->to_app;
subtest 'Document is inheritable' => sub {
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/');
is $res->code, 200;
is $res->header('content-type'), 'text/html; charset=utf-8';
like $res->content, qr{pink};
done_testing;
};
};
}
t/Plack-App-APISchema-MockServer.t view on Meta::CPAN
package t::Plack::App::APISchema::MockServer;
use lib '.';
use t::test;
use t::test::fixtures;
use Plack::Test;
use HTTP::Request::Common;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'Plack::App::APISchema::MockServer';
}
sub instantiate : Tests {
my $schema = APISchema::Schema->new;
my $app = Plack::App::APISchema::MockServer->new(schema => $schema);
isa_ok $app, 'Plack::App::APISchema::MockServer';
is $app->schema, $schema;
isa_ok $app->router, 'Router::Simple';
}
sub serve_document_bmi : Tests {
my $schema = t::test::fixtures::prepare_bmi;
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
subtest 'when valid request' => sub {
test_psgi $app => sub {
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 200;
is $res->header('content-type'), 'application/json; charset=utf-8';
is $res->content, q!{"value":19.5}!;
}
};
subtest 'when invalid request' => sub {
test_psgi $app => sub {
my $server = shift;
my $res = $server->(POST '/notfound');
is $res->code, 404;
is $res->header('content-type'), 'text/plain; charset=utf-8';
is $res->content, q!not found!;
}
};
}
sub when_encoding_is_specified : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'POST',
route => '/bmi_force_json',
request_resource => {
encoding => 'json',
body => 'figure',
},
response_resource => {
encoding => 'json',
body => 'bmi',
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(POST '/bmi_force_json');
is $res->code, 200;
is $res->header('content-type'), 'application/json; charset=utf-8';
is $res->content, q!{"value":19.5}!;
}
}
sub with_wide_character : Tests {
my $schema = t::test::fixtures::prepare_author;
$schema->register_route(
method => 'GET',
route => '/author',
response_resource => {
encoding => 'json',
body => 'author',
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/author');
is $res->code, 200;
is $res->header('content-type'), 'application/json; charset=utf-8';
is $res->content, q!{"author_name":"èè
"}!;
};
}
sub one_of : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_resource(maybe_bmi => {
oneOf => [
{
type => 'object',
'$ref' => '#/resource/bmi',
},
{
type => 'null',
},
],
t/Plack-App-APISchema-MockServer.t view on Meta::CPAN
method => 'GET',
route => '/maybe_bmi',
response_resource => {
encoding => 'json',
body => 'maybe_bmi',
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/maybe_bmi');
is $res->code, 200;
is $res->header('content-type'), 'application/json; charset=utf-8';
is $res->content, q!{"value":19.5}!;
};
}
sub status_201 : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'PUT',
route => '/put_bmi',
request_resource => {
encoding => 'json',
body => 'figure',
},
response_resource => {
201 => {
encoding => 'json',
body => 'bmi',
},
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(PUT '/put_bmi');
is $res->content, q!{"value":19.5}!;
is $res->code, 201;
};
}
sub status_204 : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'GET',
route => '/empty',
response_resource => {
204 => {},
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/empty');
is $res->content, '';
is $res->code, 204;
};
}
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
package t::Plack::Middleware::APISchema::RequestValidator;
use lib '.';
use t::test;
use t::test::fixtures;
use Plack::Test;
use HTTP::Request::Common;
use HTTP::Status qw(:constants);
use JSON::XS qw(encode_json);
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'Plack::Middleware::APISchema::RequestValidator';
}
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
my $schema = APISchema::Schema->new;
my $middleware = Plack::Middleware::APISchema::RequestValidator->new(schema => $schema);
isa_ok $middleware, 'Plack::Middleware::APISchema::RequestValidator';
is $middleware->schema, $schema;
isa_ok $middleware->router, 'Router::Simple';
}
sub request_validator : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'POST',
route => '/bmi_strict',
request_resource => {
encoding => { 'application/json' => 'json' },
body => 'figure',
},
);
$schema->register_route(
method => 'POST',
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
request_resource => {
header => 'figure_header',
},
);
my $middleware = Plack::Middleware::APISchema::RequestValidator->new(schema => $schema);
$middleware->wrap(sub {
[200, [ 'Content-Type' => 'text/plain' ], [ 'dummy' ] ]
});
subtest 'when valid request' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json',
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, 200;
done_testing;
}
};
subtest 'when valid utf8 request' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json; charset=UTF-8',
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, 200;
done_testing;
}
};
subtest 'when invalid request' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json',
Content => encode_json({}),
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure'",
encoding => 'json',
actual => {},
expected => $schema->get_resource_by_name('figure')->definition,
},
});
done_testing;
}
};
subtest 'other endpoints are not affected' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(GET '/other/');
is $res->code, 200;
}
};
subtest 'when request is not a JSON' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json',
Content => 'aaa',
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
message => "Failed to parse json",
encoding => 'json',
},
});
done_testing;
}
};
subtest 'when content-type is incorrect' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $content_type = 'application/x-www-form-urlencoded';
my $res = $server->(
POST '/bmi',
Content_Type => $content_type,
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure'",
encoding => 'url_parameter',
actual => isa('HASH'),
# XXX: Hash order randomization
# actual => { "{\"weight\":50,\"height\":1.6}" => undef }
expected => $schema->get_resource_by_name('figure')->definition,
},
});
done_testing;
}
};
subtest 'when content-type is incorrect with forced encoding' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $content_type = 'application/x-www-form-urlencoded';
my $res = $server->(
POST '/bmi_force_json',
Content_Type => $content_type,
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, 200;
done_testing;
}
};
subtest 'when content-type is incorrect with strict content-type check' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $content_type = 'application/x-www-form-urlencoded';
my $res = $server->(
POST '/bmi_strict',
Content_Type => $content_type,
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, HTTP_UNSUPPORTED_MEDIA_TYPE;
cmp_deeply $res->content, json({
body => { message => "Wrong content-type: $content_type" },
});
done_testing;
}
};
subtest 'when valid request by parameter' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi_by_parameter?weight=50&height=1.6',
);
is $res->code, 200;
done_testing;
}
};
subtest 'when invalid request by parameter' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi_by_parameter?weight=50',
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
parameter => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure'",
encoding => 'url_parameter',
actual => {
weight => 50,
},
expected => $schema->get_resource_by_name('figure')->definition,
},
});
done_testing;
}
};
subtest 'when valid request by header' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi_by_header',
X_Weight => 50,
X_Height => 1.6,
);
is $res->code, 200;
done_testing;
}
};
subtest 'when invalid request by header' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi_by_header',
X_Weight => 50,
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
header => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
encoding => 'perl',
actual => {
content_length => 0,
x_weight => 50,
content_type => "application/x-www-form-urlencoded",
host => "localhost",
},
expected => $schema->get_resource_by_name('figure_header')->definition,
},
});
done_testing;
}
};
}
sub request_validator_with_utf8 : Tests {
my $schema = t::test::fixtures::prepare_user;
$schema->register_route(
method => 'POST',
route => '/user',
request_resource => {
body => 'user',
},
);
my $middleware = Plack::Middleware::APISchema::RequestValidator->new(schema => $schema);
$middleware->wrap(sub {
[200, [ 'Content-Type' => 'text/plain' ], [ 'dummy' ] ]
});
subtest 'when invalid request' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/user',
Content_Type => 'application/json',
Content => encode_json({
first_name => 'Bill',
last_name => [],
}),
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
actual => [],
attribute => 'Valiemon::Attributes::Type',
position => '/$ref/properties/last_name/type',
expected => $schema->get_resource_by_name('user')->definition->{properties}->{last_name},
encoding => 'json',
message => "Contents do not match resource 'user'",
},
});
done_testing;
};
};
}
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
package t::Plack::Middleware::APISchema::ResponseValidator;
use lib '.';
use t::test;
use t::test::fixtures;
use Plack::Test;
use Plack::Request;
use HTTP::Request::Common;
use JSON::XS qw(encode_json);
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'Plack::Middleware::APISchema::ResponseValidator';
}
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
my $schema = APISchema::Schema->new;
my $middleware = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
isa_ok $middleware, 'Plack::Middleware::APISchema::ResponseValidator';
is $middleware->schema, $schema;
isa_ok $middleware->router, 'Router::Simple';
}
sub response_validator : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'POST',
route => '/bmi_strict',
response_resource => {
encoding => { 'application/json' => 'json' },
body => 'bmi',
},
);
$schema->register_route(
method => 'POST',
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
);
my $content_type;
my $json;
my $middleware = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
my $header = [];
$middleware->wrap(sub {
[200, [ 'Content-Type' => $content_type, @$header ], [ $json ] ]
});
subtest 'when valid response' => sub {
test_psgi $middleware => sub {
$content_type = 'application/json';
$json = encode_json({value => 19.5});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 200;
done_testing;
}
};
subtest 'when invalid response' => sub {
test_psgi $middleware => sub {
$content_type = 'application/json';
$json = encode_json({value => 'aaa'});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => {
attribute => "Valiemon::Attributes::Type",
position => '/$ref/properties/value/type',
expected => $schema->get_resource_by_name('bmi')->definition->{properties}->{value},
actual => 'aaa',
message => "Contents do not match resource 'bmi'",
encoding => 'json',
},
});
done_testing;
}
};
subtest 'when wrong content-type' => sub {
test_psgi $middleware => sub {
$content_type = 'text/plain';
$json = encode_json({value => 19.5});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => { message => "Wrong content-type: text/plain" },
});
done_testing;
}
};
subtest 'when response is not a JSON' => sub {
test_psgi $middleware => sub {
$content_type = 'application/json';
$json = 'aaa';
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => {
message => "Failed to parse json",
encoding => 'json',
},
});
done_testing;
}
};
subtest 'when content-type is incorrect' => sub {
test_psgi $middleware => sub {
$content_type = 'application/x-www-form-urlencoded';
$json = encode_json({value => 19.5});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'bmi'",
encoding => 'url_parameter',
actual => {
'{"value":19.5}' => undef,
},
expected => $schema->get_resource_by_name('bmi')->definition,
},
});
done_testing;
}
};
subtest 'when content-type is incorrect with forced encoding' => sub {
test_psgi $middleware => sub {
$content_type = 'application/x-www-form-urlencoded';
$json = encode_json({value => 19.5});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi_force_json');
is $res->code, 200;
done_testing;
}
};
subtest 'when content-type is incorrect with strict content-type check' => sub {
test_psgi $middleware => sub {
$content_type = 'application/x-www-form-urlencoded';
$json = encode_json({value => 19.5});
my $server = shift;
my $res = $server->(POST '/bmi_strict');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => { message => "Wrong content-type: $content_type" },
});
done_testing;
}
};
subtest 'other endpoints are not affected' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(GET '/other/');
is $res->code, 200;
}
};
subtest 'when valid response by header' => sub {
test_psgi $middleware => sub {
$content_type = 'application/json';
$json = encode_json({});
$header = [ 'X-Value' => 19.5 ];
my $server = shift;
my $res = $server->(POST '/bmi_by_header');
is $res->code, 200;
done_testing;
}
};
subtest 'when invalid response by header' => sub {
test_psgi $middleware => sub {
$content_type = 'application/json';
$json = encode_json({});
$header = [ 'X-Foo' => 1 ];
my $server = shift;
my $res = $server->(POST '/bmi_by_header');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
header => {
attribute => "Valiemon::Attributes::Required",
position => '/$ref/required',
message => "Contents do not match resource 'bmi_header'",
encoding => 'perl',
expected => $schema->get_resource_by_name('bmi_header')->definition,
actual => {
content_type => "application/json",
"x_foo" => 1,
},
},
});
done_testing;
}
};
}
sub status : Tests {
my $schema = t::test::fixtures::prepare_status;
my $middleware_ok = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
$middleware_ok->wrap(sub {
my $env = shift;
my $req = Plack::Request->new($env);
if ($req->parameters->{success}) {
return [ 200, [ 'Content-Type' => 'text/plain', ], [ 'OK' ] ];
} elsif ($req->parameters->{undefined}) {
return [ 599, [ 'Content-Type' => 'application/json', ], [
encode_json({ status => 599, message => 'Something wrong' }),
] ];
} else {
return [ 400, [ 'Content-Type' => 'application/json', ], [
encode_json({ status => 400, message => 'Bad Request' }),
] ];
}
});
subtest 'Status 200 with valid body' => sub {
test_psgi $middleware_ok => sub {
my $server = shift;
my $res = $server->(GET '/get?success=1');
is $res->code, 200;
done_testing;
};
};
subtest 'Status 400 with valid body' => sub {
test_psgi $middleware_ok => sub {
my $server = shift;
my $res = $server->(GET '/get');
is $res->code, 400;
done_testing;
};
};
subtest 'Undefined status' => sub {
test_psgi $middleware_ok => sub {
my $server = shift;
my $res = $server->(GET '/get?undefined=1');
is $res->code, 599;
done_testing;
};
};
my $middleware_ng = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
$middleware_ng->wrap(sub {
my $env = shift;
my $req = Plack::Request->new($env);
return [ 400, [ 'Content-Type' => 'text/plain', ], [ 'OK' ] ];
});
subtest 'Status 400 with invalid body' => sub {
test_psgi $middleware_ng => sub {
my $server = shift;
my $res = $server->(GET '/get');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({ body => {
message => 'Failed to parse json',
encoding => 'json',
} });
done_testing;
};
};
}
sub response_validator_with_utf8 : Tests {
my $schema = t::test::fixtures::prepare_user;
$schema->register_route(
method => 'GET',
route => '/user',
response_resource => {
body => 'user',
},
);
my $middleware = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
$middleware->wrap(sub {
[200, [ 'Content-Type' => 'application/json; charset=utf-8' ], [ encode_json({ first_name => 'Bill', last_name => []}) ] ]
});
subtest 'invalid response with utf8' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(GET '/user');
is $res->code, 500;
cmp_deeply $res->content, json({
body => {
attribute => "Valiemon::Attributes::Type",
position => '/$ref/properties/last_name/type',
expected => $schema->get_resource_by_name('user')->definition->{properties}->{last_name},
actual => [],
message => "Contents do not match resource 'user'",
encoding => 'json',
},
});
done_testing;
};
};
}
t/fixtures/syntax-error.def view on Meta::CPAN
description 'API definition with a syntax error';
resource value => {
type => 'number',
description => 'value' # , missing here
example => 1,
};
GET '/value' => {
title => 'Value API',
description => 'Endpoint for test.',
response => 'value',
};
package t::test;
use strict;
use warnings;
use Path::Class;
use lib file(__FILE__)->dir->parent->subdir('lib')->stringify;
use lib lib => 't/lib' => glob 'modules/*/lib';
sub import {
package $package;
use strict;
use warnings;
use parent qw(Test::Class);
use Test::More;
use Test::Fatal qw(lives_ok dies_ok exception);
use Test::Deep;
use Test::Deep::JSON;
END { $package->runtests }
];
eval $code;
die $@ if $@;
}
1;
t/test/InheritedDocument.pm view on Meta::CPAN
package t::test::InheritedDocument;
use strict;
use warnings;
use parent qw(Plack::App::APISchema::Document);
1;
__DATA__
@@ template.mt
? my ($title, $body) = @_;
<!DOCTYPE html>
<html>
t/test/fixtures.pm view on Meta::CPAN
package t::test::fixtures;
use strict;
use warnings;
use APISchema::DSL;
sub prepare_bmi {
APISchema::DSL::process {
include 't/fixtures/bmi.def';
};
}