summary refs log tree commit diff
diff options
context:
space:
mode:
authorfranck cuny <franck@lumberjaph.net>2009-12-08 10:35:46 +0100
committerfranck cuny <franck@lumberjaph.net>2009-12-08 10:35:46 +0100
commita2b7ab98ccb7083ad6bdda0839a1e2e6e21ea847 (patch)
tree309219b95ca9846f53cc078a01baa2577d3399ab
parentChecking in changes prior to tagging of version 0.01. Changelog diff is: (diff)
parentsmall updates to tests (diff)
downloadmoosex-net-api-a2b7ab98ccb7083ad6bdda0839a1e2e6e21ea847.tar.gz
Merge branch 'topic/create_tests'
* topic/create_tests:
  small updates to tests
  add a catalyst app to tests
  add basic tests
  remove meta class and method
  move meta class and method to new file, add meta to handle tests
-rw-r--r--lib/MooseX/Net/API.pm141
-rw-r--r--lib/MooseX/Net/API/Meta/Class.pm34
-rw-r--r--lib/MooseX/Net/API/Meta/Method.pm18
-rw-r--r--lib/MooseX/Net/API/Test.pm120
-rw-r--r--t/00_compile.t4
-rw-r--r--t/01_basic.t3
-rw-r--r--t/10_mx_net_api_test.t43
-rw-r--r--t/lib/TestAPI.pm19
-rw-r--r--t/lib/TestApp.pm11
-rw-r--r--t/lib/TestApp/Controller/Root.pm16
10 files changed, 344 insertions, 65 deletions
diff --git a/lib/MooseX/Net/API.pm b/lib/MooseX/Net/API.pm
index e1ab774..4e1ae5e 100644
--- a/lib/MooseX/Net/API.pm
+++ b/lib/MooseX/Net/API.pm
@@ -1,13 +1,17 @@
 package MooseX::Net::API;
 
-use Carp;
 use URI;
-use HTTP::Request;
+use Carp;
 use Try::Tiny;
+use HTTP::Request;
+
+use Moose;
 use Moose::Exporter;
 use MooseX::Net::API::Error;
-use MooseX::Net::API::Role::Deserialize;
+use MooseX::Net::API::Meta::Class;
+use MooseX::Net::API::Meta::Method;
 use MooseX::Net::API::Role::Serialize;
+use MooseX::Net::API::Role::Deserialize;
 
 our $VERSION = '0.01';
 
@@ -30,6 +34,16 @@ my $reverse_content_type = {
 Moose::Exporter->setup_import_methods(
     with_caller => [qw/net_api_method net_api_declare/], );
 
+sub init_meta {
+    my ( $me, %options ) = @_;
+
+    my $for = $options{for_class};
+    Moose::Util::MetaRole::apply_metaclass_roles(
+        for_class       => $for,
+        metaclass_roles => ['MooseX::Net::API::Meta::Class'],
+    );
+}
+
 my ( $do_auth, $auth_method, $deserialize_method );
 
 sub net_api_declare {
@@ -39,18 +53,13 @@ sub net_api_declare {
 
     my $class = Moose::Meta::Class->initialize($caller);
 
-    if ( !$options{base_url} ) {
-        croak "base_url is missing in your api declaration";
-    }
-    else {
-        $class->add_attribute(
-            'api_base_url',
-            is      => 'ro',
-            isa     => 'Str',
-            lazy    => 1,
-            default => delete $options{base_url}
-        );
-    }
+    $class->add_attribute(
+        'api_base_url',
+        is      => 'rw',
+        isa     => 'Str',
+        lazy    => 1,
+        default => delete $options{base_url} || '',
+    );
 
     if ( !$options{format} ) {
         croak "format is missing in your api declaration";
@@ -180,37 +189,8 @@ sub net_api_method {
             my $format = $self->api_format();
             $url .= "." . $format if ( $self->api_format_mode() eq 'append' );
             my $uri = URI->new($url);
+            my $res = _request( $self, $format, \%options, $uri, \%args );
 
-            my $req;
-            my $method = $options{method};
-            if ( $method =~ /^(?:GET|DELETE)$/ || $options{params_in_url} ) {
-                $uri->query_form(%args);
-                $req = HTTP::Request->new( $method => $uri );
-            }
-            elsif ( $method =~ /^(?:POST|PUT)$/ ) {
-                $req = HTTP::Request->new( $method => $uri );
-                # XXX GNI
-                use JSON::XS;
-                $req->content( encode_json \%args );
-            }
-            else {
-                croak "$method is not defined";
-            }
-
-            # XXX check presence content type
-            $req->header( 'Content-Type' => $list_content_type->{$format} )
-                if $self->api_format_mode eq 'content-type';
-
-            if ($do_auth) {
-                if ($auth_method) {
-                    $req = $self->$auth_method($req);
-                }
-                else {
-                    $req = _do_authentication( $self, $req );
-                }
-            }
-
-            my $res          = $self->useragent->request($req);
             my $content_type = $res->headers->{"content-type"};
             $content_type =~ s/(;.+)$//;
 
@@ -249,6 +229,7 @@ sub net_api_method {
             %options,
         ),
     );
+    $class->_add_api_method($name);
 }
 
 sub _add_useragent {
@@ -277,6 +258,41 @@ sub _add_useragent {
     );
 }
 
+sub _request {
+    my ( $self, $format, $options, $uri, $args ) = @_;
+
+    my $req;
+    my $method = $options->{method};
+    if ( $method =~ /^(?:GET|DELETE)$/ || $options->{params_in_url} ) {
+        $uri->query_form(%$args);
+        $req = HTTP::Request->new( $method => $uri );
+    }
+    elsif ( $method =~ /^(?:POST|PUT)$/ ) {
+        $req = HTTP::Request->new( $method => $uri );
+
+        # XXX proper serialisation
+        use JSON::XS;
+        $req->content( encode_json $args );
+    }
+    else {
+        croak "$method is not defined";
+    }
+
+    $req->header( 'Content-Type' => $list_content_type->{$format} )
+        if $self->api_format_mode eq 'content-type';
+
+    if ($do_auth) {
+        if ($auth_method) {
+            $req = $self->$auth_method($req);
+        }
+        else {
+            $req = _do_authentication( $self, $req );
+        }
+    }
+
+    return $self->useragent->request($req);
+}
+
 sub _do_authentication {
     my ( $caller, $req ) = @_;
     $req->headers->authorization_basic( $caller->api_username,
@@ -305,23 +321,6 @@ sub _do_deserialization {
     }
 }
 
-package MooseX::Net::API::Meta::Method;
-
-use Moose;
-extends 'Moose::Meta::Method';
-
-has description => ( is => 'ro', isa => 'Str' );
-has path        => ( is => 'ro', isa => 'Str', required => 1 );
-has method      => ( is => 'ro', isa => 'Str', required => 1 );
-has params      => ( is => 'ro', isa => 'ArrayRef', required => 0 );
-has required    => ( is => 'ro', isa => 'ArrayRef', required => 0 );
-
-sub new {
-    my $class = shift;
-    my %args  = @_;
-    $class->SUPER::wrap(@_);
-}
-
 1;
 
 __END__
@@ -338,6 +337,7 @@ MooseX::Net::API - Easily create client for net API
 
   # we declare an API, the base_url is http://exemple.com/api
   # the format is json and it will be happened to the query
+  # You can set base_url later, calling $my_obj->api_base_url('http://..')
   net_api_declare my_api => (
     base_url   => 'http://exemple.com/api',
     format     => 'json',
@@ -426,6 +426,23 @@ HTTP method (GET, POST, PUT, DELETE)
 
 path of the query.
 
+If you defined your path and params like this
+
+  net_api_method user_comments => (
+    ...
+    path => '/user/$user/list/$date/',
+    params => [qw/user date foo bar/],
+    ...
+  );
+
+and you call
+
+  $obj->user_comments(user => 'franck', date => 'today', foo => 1, bar => 2);
+
+the url generetad will look like
+
+  /user/franck/list/today/?foo=1&bar=2
+
 =item B<params> [arrayref]
 
 list of params.
diff --git a/lib/MooseX/Net/API/Meta/Class.pm b/lib/MooseX/Net/API/Meta/Class.pm
new file mode 100644
index 0000000..80075f8
--- /dev/null
+++ b/lib/MooseX/Net/API/Meta/Class.pm
@@ -0,0 +1,34 @@
+package MooseX::Net::API::Meta::Class;
+
+use Moose::Role;
+use Moose::Meta::Class;
+use MooseX::Types::Moose qw(Str ArrayRef ClassName Object);
+
+has local_api_methods => (
+    traits     => ['Array'],
+    is         => 'ro',
+    isa        => ArrayRef [Str],
+    required   => 1,
+    default    => sub { [] },
+    auto_deref => 1,
+    handles    => { '_add_api_method' => 'push' },
+);
+has local_api_test_methods => (
+    traits     => ['Array'],
+    is         => 'ro',
+    isa        => ArrayRef [Str],
+    required   => 1,
+    default    => sub { [] },
+    auto_deref => 1,
+    handles    => { '_add_api_test_method' => 'push' },
+);
+
+sub _build_meta_class {
+    my $self = shift;
+    return Moose::Meta::Class->create_anon_class(
+        superclasses => [ $self->method_metaclass ],
+        cache        => 1,
+    );
+}
+
+1;
diff --git a/lib/MooseX/Net/API/Meta/Method.pm b/lib/MooseX/Net/API/Meta/Method.pm
new file mode 100644
index 0000000..74f9a07
--- /dev/null
+++ b/lib/MooseX/Net/API/Meta/Method.pm
@@ -0,0 +1,18 @@
+package MooseX::Net::API::Meta::Method;
+
+use Moose;
+extends 'Moose::Meta::Method';
+
+has description => ( is => 'ro', isa => 'Str' );
+has path        => ( is => 'ro', isa => 'Str', required => 1 );
+has method      => ( is => 'ro', isa => 'Str', required => 1 );
+has params      => ( is => 'ro', isa => 'ArrayRef', required => 0 );
+has required    => ( is => 'ro', isa => 'ArrayRef', required => 0 );
+
+sub new {
+    my $class = shift;
+    my %args  = @_;
+    $class->SUPER::wrap(@_);
+}
+
+1;
diff --git a/lib/MooseX/Net/API/Test.pm b/lib/MooseX/Net/API/Test.pm
index 2f2e428..e991b7f 100644
--- a/lib/MooseX/Net/API/Test.pm
+++ b/lib/MooseX/Net/API/Test.pm
@@ -1,13 +1,131 @@
 package MooseX::Net::API::Test;
 
+use lib ('t/lib');
+use Try::Tiny;
+
+use Test::More;
+use Moose;
 use Moose::Exporter;
+use MooseX::Net::API::Meta::Class;
+use MooseX::Net::API::Meta::Method;
+
+Moose::Exporter->setup_import_methods(
+    with_caller => [qw/test_api_method test_api_declare run/] );
+
+my $api_to_test;
+
+sub init_meta {
+    my ( $me, %options ) = @_;
+
+    my $for = $options{for_class};
+    Moose::Util::MetaRole::apply_metaclass_roles(
+        for_class       => $for,
+        metaclass_roles => ['MooseX::Net::API::Meta::Class'],
+    );
+}
+
+my $list_content_type = {
+    'json' => 'application/json',
+    'yaml' => 'text/x-yaml',
+    'xml'  => 'text/xml',
+};
+
+my $tests_count = 0;
+
+sub test_api_declare {
+    my $caller  = shift;
+    my $name    = shift;
+    my %options = @_;
+
+    unless ( Class::MOP::is_class_loaded($name) ) {
+        Class::MOP::load_class($name);
+    }
 
-Moose::Exporter->setup_import_methods( with_caller => [qw/test_api_method/] );
+    $api_to_test = $name;
+
+    if ( $options{catalyst} ) {
+        my $app = $options{catalyst_app_name};
+
+        Class::MOP::load_class("HTTP::Request");
+        Class::MOP::load_class("Catalyst::Test");
+
+        Catalyst::Test->import($app);
+
+        my $res = __PACKAGE__->meta->remove_method('_request');
+        MooseX::Net::API->meta->add_method(
+            '_request' => sub {
+                my ( $class, $format, $options, $uri, $args ) = @_;
+                my $method = $options->{method};
+
+                my $res;
+                if (   $method =~ /^(?:GET|DELETE)$/
+                    || $options->{params_in_url} )
+                {
+                    $uri->query_form(%$args);
+                    my $req = HTTP::Request->new( $method => $uri );
+                    $req->header(
+                        'Content-Type' => $list_content_type->{$format} );
+                    $res = request($req);
+                }
+                else {
+                    my $req = HTTP::Request->new( $method => $uri );
+                    $req->header(
+                        'Content-Type' => $list_content_type->{$format} );
+                    $req->header( 'Content' => Dump $args);
+                    $res = request($req);
+                }
+                return $res;
+            }
+        );
+    }
+}
 
 sub test_api_method {
     my $caller  = shift;
     my $name    = shift;
     my %options = @_;
+
+    my $meta   = $api_to_test->meta;
+    my $method = $meta->find_method_by_name($name);
+
+    if ( !$method ) {
+        die "method $name does not exists\n";
+    }
+
+    my $class = Moose::Meta::Class->initialize($caller);
+    foreach my $test_name ( keys %{ $options{tests} } ) {
+        foreach my $test ( @{ $options{tests}{$test_name} } ) {
+            __PACKAGE__->meta->add_method(
+                $test_name => sub {
+                    my $res    = $method->execute( $api_to_test->new );
+                    if (ref $test eq 'HASH') {
+                        my $action = $test->{test};
+                        my $result = $test->{expected};
+                        # XXX sucky sucky sucky
+                        if ( $action eq 'is_deeply' ) {
+                            is_deeply( $res, $result );
+                        }
+                    }else{
+                        if ($test eq 'ok') {
+                            ok $res;
+                        }
+                    }
+                }
+            );
+            $class->_add_api_test_method($test_name);
+        }
+    }
+}
+
+sub run {
+    my $caller = shift;
+
+    my $class = Moose::Meta::Class->initialize($caller);
+    my @test_methods = $class->local_api_test_methods();
+    foreach my $m (@test_methods) {
+        my $method = __PACKAGE__->meta->find_method_by_name($m);
+        $method->execute();
+    }
 }
 
 1;
diff --git a/t/00_compile.t b/t/00_compile.t
index 86438a0..10b03a4 100644
--- a/t/00_compile.t
+++ b/t/00_compile.t
@@ -1,4 +1,4 @@
 use strict;
-use Test::More tests => 1;
+use Test::More tests => 2;
 
-BEGIN { use_ok 'MooseX::Net::API' }
+BEGIN { use_ok 'MooseX::Net::API'; use_ok 'MooseX::Net::API::Test' }
diff --git a/t/01_basic.t b/t/01_basic.t
index 9c108d2..471f3dd 100644
--- a/t/01_basic.t
+++ b/t/01_basic.t
@@ -29,4 +29,7 @@ throws_ok {
 }
 qr/foo is not declared as a param/, "... check declared params";
 
+ok my @methods = $obj->meta->local_api_methods(), '... get api methods';
+is scalar @methods, 3, '... got 3 methods in our API';
+
 done_testing;
diff --git a/t/10_mx_net_api_test.t b/t/10_mx_net_api_test.t
new file mode 100644
index 0000000..96bf2be
--- /dev/null
+++ b/t/10_mx_net_api_test.t
@@ -0,0 +1,43 @@
+use strict;
+use warnings;
+use Test::More;
+
+BEGIN {
+    plan skip_all => 'requires Catalyst::Action::REST'
+        unless eval { require Catalyst::Action::REST };
+}
+
+{
+
+    package catalysttestapi;
+    use Moose;
+    use MooseX::Net::API::Test;
+
+    test_api_declare 'TestAPI' => (
+        catalyst          => 1,
+        catalyst_app_name => 'TestApp'
+    );
+
+    test_api_method foo => (
+        tests => {
+            simple => [
+                {
+                    # pouvoir surcharger
+                    test     => 'is_deeply',
+                    expected => { status => 1 }
+                },
+                'ok',
+            ]
+        }
+    );
+}
+
+#content_like    => [ { expected => qr/status: 1/ }, ],
+#action_ok       => [],
+#action_redirect => [],
+#action_notfound => [],
+#contenttype_is  => [],
+
+catalysttestapi->run();
+
+done_testing;
diff --git a/t/lib/TestAPI.pm b/t/lib/TestAPI.pm
new file mode 100644
index 0000000..1f58351
--- /dev/null
+++ b/t/lib/TestAPI.pm
@@ -0,0 +1,19 @@
+package TestAPI;
+use Moose;
+use MooseX::Net::API;
+
+net_api_declare fake_api => (
+    base_url               => 'http://localhost/root',
+    format                 => 'json',
+    format_mode            => 'content-type',
+    require_authentication => 0,
+);
+
+net_api_method foo => (
+    description => 'this does foo',
+    method      => 'GET',
+    path        => '/foo/',
+);
+
+1;
+
diff --git a/t/lib/TestApp.pm b/t/lib/TestApp.pm
new file mode 100644
index 0000000..69ec93d
--- /dev/null
+++ b/t/lib/TestApp.pm
@@ -0,0 +1,11 @@
+package TestApp;
+use strict;
+use warnings;
+
+use Catalyst;
+
+__PACKAGE__->config( name => 'TestApp', );
+
+__PACKAGE__->setup;
+
+1;
diff --git a/t/lib/TestApp/Controller/Root.pm b/t/lib/TestApp/Controller/Root.pm
new file mode 100644
index 0000000..7ea3f25
--- /dev/null
+++ b/t/lib/TestApp/Controller/Root.pm
@@ -0,0 +1,16 @@
+package TestApp::Controller::Root;
+
+use strict;
+use warnings;
+use base qw/Catalyst::Controller::REST/;
+
+sub foo : Local : ActionClass('REST') {
+    my ( $self, $c ) = @_;
+}
+
+sub foo_GET {
+    my ( $self, $c ) = @_;
+    $self->status_ok( $c, entity => { status => 1 } );
+}
+
+1;