package Test::Net::Async::Graphite;

use Test::Most;

use experimental 'signatures';

use Future;
use HTTP::Response;
use Mock::Sub;
use Net::Async::Graphite;

=head1 NAME

Test::Net::Async::Graphite - Tests &c. for Net::Async::Graphite

=head1 FUNCTIONS

=over

=item test_api(%args)

Unit-test a call against Graphite's API.

Passed the following, as I<named> arguments:

=over

=item content

The bytes which will be included as the HTTP content in the mocked
Graphite response.

=item endpoint

The fake Graphite endpoint.

=item method

The B<Graphite API> method to call, without a preceeding C</>. B<Not>
the name of a perl method.

=item param

A hashref of the parameters which should be included in the final URI.

=item target (aka. query)

Known as C<query> in metrics methods, the metric specification to
search for.

=back

=cut

sub test_api (%args) { subtest "Unit test $args{method}" => sub {
  plan tests => 11;

  my $want_method;
  if ($args{method} =~ m(^metrics/(index$)?)) {
    $want_method = $1 ? "$args{method}.json" : $args{method}
  } else {
    $want_method = 'render';
  }
  my $fxt_future  = Future->new();
  my $fxt_http    = HTTP::Response->new(200 => OK => [] => $args{content});
  my $mockery     = Mock::Sub->new;
  my $_download   = $mockery->mock('Net::Async::Graphite::_download',
                                   return_value => $fxt_future);

  # Perform a query
  my $nag         = Net::Async::Graphite->new(endpoint => $args{endpoint});
  my ($call, $type) = $args{method} =~ m(^metrics/(.*))
    ? (metrics => $1)
    : (render => $args{method});
  $call .= '_asperl' if $args{asperl};
  my $got_result  = $nag->$call($type => $args{target});

  is $_download->called_count, 1,         "_download() is called exactly once for '$args{method}'";
  cmp_deeply [ $_download->called_with ],
    [
      shallow($nag),
      obj_isa('URI'),
    ],
                                          '_download() is given a URI object to fetch';
  my $probably_breaks_an_rfc_nobody_follows =
    ($_download->called_with)[1]->scheme
    . '://'
    . ($_download->called_with)[1]->authority
    . '/';
  is $probably_breaks_an_rfc_nobody_follows, $args{endpoint},
                                          'URI endpoint is correct';
  my $path = ($_download->called_with)[1]->path;
  is $path, "/$want_method",              'URI path is correct';
  cmp_deeply { ($_download->called_with)[1]->query_form }, $args{param},
                                          'URI query parameters are correct';
  # TODO: does_ok? Preferably without Test::Moose.
  ok $got_result->DOES('Future'),         "$call() returns a Future object";
  # This is probably theoretically possibly [sic] not true, but it is
  # in this Future implementation.
  isnt $got_result, $fxt_future,          "$call() further processes _download()'s Future";
  ok ! $got_result->is_ready,             "$call()' Future is not immediately complete";

  # Simulate (and check for exceptions for good measure) the download completing.
  lives_ok {$fxt_future->done($fxt_http)} "Simulating _download()'s Future does not die";
  ok $got_result->is_done,                "Completing _download()'s Future completes $call()'s";
  my $want = $args{asperl} || $args{content};
  cmp_deeply [ $got_result->get ], [ $want ], ##
                                          "$call() correctly extracts and returns the http content";
} }

=back

=cut

1;
