AnyEvent-MCP

 view release on metacpan or  search on metacpan

t/15_getoptions.t  view on Meta::CPAN

use common::sense;
use Test::More;
use File::Temp ();
use IPC::Open3 ();
use Symbol ();
use Getopt::Long ();
use AnyEvent::MCP::Config ();
use AnyEvent::MCP::Server::Simple;
use AnyEvent::MCP::Server::HTTP ();

Getopt::Long::Configure ("require_order");
my $have_websocket = eval {
   require AnyEvent::WebSocket::Server;
   require AnyEvent::WebSocket::Message;
   1
};

{
   local @ARGV = ("--name", "demo", "--bind", ":31740", "--token", "first",
      "--token", "second", "--stdio", "--custom", "yes", "positional");
   my $custom;
   my $options = mcp_getoptions ({ usage => "usage example\n" }, "custom=s" => \$custom);
   is $options->{name}, "demo", "shared name";
   is $options->{bind}, ":31740", "shared bind";
   is_deeply $options->{tokens}, ["first", "second"], "repeatable ordered tokens";
   ok $options->{stdio}, "shared stdio";
   is $custom, "yes", "Getopt::Long application option";
   is_deeply \@ARGV, ["positional"], "require_order leaves positional arguments";
}

{
   local @ARGV = ("--name", "memory", "--custom-instructions", "Use for lasting memories.");
   my $options = mcp_getoptions;
   is $options->{custom_instructions}, "Use for lasting memories.",
      "common custom-instructions option returns instance purpose";
}

{
   local @ARGV = ("--name", "example");
   my $options = mcp_getoptions ({ path => "/custom/", auth => "none" });
   is $options->{path}, "/custom/", "custom endpoint path is returned for startup";
   is $options->{auth}, "none", "explicit unauthenticated mode passed to startup";
}

{
   local @ARGV = ("--bad-option");
   my $error;
   {
      local *STDERR;
      open STDERR, ">", "/dev/null" or die "open /dev/null: $!";
      eval { mcp_getoptions ({ usage => "custom usage\n" }) };
      $error = $@;
   }
   like $error, qr/custom usage/, "usage shown on invalid options";
}

{
   local @ARGV = ("--name", "example");
   my $options = mcp_getoptions;
   is_deeply $options, { name => "example" }, "no unnecessary option defaults";
}

my $dir = File::Temp::tempdir (CLEANUP => 1);
local $ENV{XDG_CONFIG_HOME} = $dir;
my $config = AnyEvent::MCP::Config->new (file => "$dir/anyevent-mcp/global.ini");

sub command {
   my ($script, @argv) = @_;
   my $err = Symbol::gensym;
   my $pid = IPC::Open3::open3 (my $in, my $out, $err,
      $^X, "-Ilib", "-MAnyEvent::MCP::Server::Simple",
      "-e", $script,
      "--", @argv);
   close $in;
   local $/;
   my $stdout = <$out>;
   my $stderr = <$err>;
   waitpid $pid, 0;
   ($? >> 8, $stdout // "", $stderr // "")
}

my $script = 'mcp_getoptions ({ usage => "usage in subprocess\n", path => "/custom/" });';
my ($status, $out, $err) = command ($script, "--name", "demo", "--bind", ":31740",
   "--token", "first", "--token", "second", "--print-url");
is $status, 0, "shared print-url exits without starting a listener";
is $out, "http://127.0.0.1:31740/first/custom/\n", "print-url uses first CLI token and effective URL";
is $err, "", "print-url has no diagnostics";

($status, $out) = command ($script, "--name", "demo", "--help");
is $status, 0, "standard --help succeeds";
like $out, qr/\Ausage in subprocess\n\nCommon options:\n/,
   "shared help includes usage and common option heading";
like $out, qr/--name NAME\s+Select named configuration/,
   "common --name option documented";
like $out, qr/--token TOKEN\s+Set an accepted token/,
   "common repeatable token option documented";

unlike $out, qr/--listen/, "common parser does not advertise --listen";
($status, $out, $err) = command ($script, "--listen");
ok $status && $err =~ /Unknown option: listen/, "common parser rejects --listen";

my $rich = 'my $extra; mcp_getoptions ({'
   . 'usage => "Usage: fixture [OPTION]...\\n", '
   . 'description => "A test server.", '
   . 'option_heading => "Fixture options:", '
   . 'options => [["--extra VALUE", "A configurable value"]], '
   . 'examples => "  fixture --extra yes"}, '
   . '"extra=s" => \\$extra);';
($status, $out, $err) = command ($rich, "--help");
ok !$status && !$err && $out =~ /Fixture options:.*--extra VALUE.*A configurable value/s
   && $out =~ /Examples:\n  fixture --extra yes/, "caller option descriptions and examples appear";

($status, $out, $err) = command ($script, "--name", "demo", "--bind", ":0", "--print-url");
ok $status && $err =~ /port is not known/, "ephemeral port cannot be printed offline";

open my $fh, ">>", "$dir/anyevent-mcp/global.ini" or die $!;
print {$fh} "\n[instance named]\npath = /named/\n";
close $fh or die $!;



( run in 2.397 seconds using v1.01-cache-2.11-cpan-ac8be02dad9 )