ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MCP/t/13_vim_stdio.t
Revision: 1.2
Committed: Sat Oct 10 10:29:39 2026 UTC (9 days, 15 hours ago) by root
Content type: application/x-troff
Branch: MAIN
Changes since 1.1: +1 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 use common::sense;
2     use Test::More;
3     use JSON::XS ();
4     use IPC::Open3 ();
5     use IO::Select ();
6     use IO::Socket::INET ();
7     use POSIX qw(WNOHANG);
8     use Time::HiRes ();
9     use File::Temp ();
10     use File::Path ();
11     use Symbol ();
12    
13     my $dir = File::Temp::tempdir (CLEANUP => 1);
14     File::Path::mkpath (["$dir/home", "$dir/config"], 0, 0700);
15     local $ENV{HOME} = "$dir/home";
16     local $ENV{XDG_CONFIG_HOME} = "$dir/config";
17    
18     sub free_port {
19     my $socket = IO::Socket::INET->new (
20     LocalAddr => "127.0.0.1", LocalPort => 0,
21     Proto => "tcp", Listen => 1,
22     ) or die "cannot allocate TCP test port: $!";
23     my $port = $socket->sockport;
24     close $socket;
25     $port
26     }
27    
28     sub start_child {
29     my ($use_env) = @_;
30     my $port = free_port;
31     local $ENV{PERL_ANYEVENT_MCP_MULTIBROKER_PROTOCOL} = $use_env ? "stdio" : "";
32     my $err = Symbol::gensym;
33     my $pid = IPC::Open3::open3 (my $in, my $out, $err,
34 root 1.2 $^X, "-Ilib", "bin/mcp-vim", "--bind", "127.0.0.1:$port", ($use_env ? () : ("--stdio")));
35 root 1.1 my $old = select $in;
36     $| = 1;
37     select $old;
38     ($pid, $in, $out, $err, $port)
39     }
40    
41     sub rpc {
42     my ($in, $out, $id, $method, $params) = @_;
43     print {$in} JSON::XS::encode_json ({
44     jsonrpc => "2.0", id => $id, method => $method, params => $params,
45     }), "\n" or die "cannot send JSONL test request: $!";
46     return unless IO::Select->new ($out)->can_read (5);
47     my $line = <$out>;
48     return unless defined $line;
49     eval { JSON::XS::decode_json $line }
50     }
51    
52     sub wait_exit {
53     my ($pid) = @_;
54     for (1 .. 150) {
55     my $w = waitpid ($pid, WNOHANG);
56     return $? >> 8 if $w == $pid;
57     Time::HiRes::sleep (0.02);
58     }
59     kill TERM => $pid;
60     waitpid ($pid, 0);
61     -1
62     }
63    
64     for my $use_env (0, 1) {
65     my $kind = $use_env ? "multibroker environment" : "explicit --stdio";
66     my ($pid, $in, $out, $err, $port) = start_child ($use_env);
67    
68     my $init = rpc ($in, $out, 1, "initialize", {
69     protocolVersion => "2025-11-25",
70     capabilities => {}, clientInfo => { name => "stdio-test", version => "0" },
71     });
72    
73     is $init->{id}, 1, "$kind: JSONL initialize response";
74     ok !$init->{error} && $init->{result}{serverInfo},
75     "$kind: valid MCP server metadata";
76    
77     my $tools = rpc ($in, $out, 2, "tools/list", {});
78     is $tools->{id}, 2, "$kind: JSONL tools/list response";
79     ok !$tools->{error} && grep ($_->{name} eq "vim-targets", @{$tools->{result}{tools} || []}),
80     "$kind: exposes the shared Vim toolset";
81    
82     # The stdio transport must not prevent the normal Vim/browser TCP listener.
83     my $sock;
84     for (1 .. 25) {
85     $sock = IO::Socket::INET->new (
86     PeerAddr => "127.0.0.1", PeerPort => $port,
87     Proto => "tcp", Timeout => 1,
88     );
89     last if $sock;
90     Time::HiRes::sleep (0.02);
91     }
92     ok $sock, "$kind: TCP listener remains available";
93     if ($sock) {
94     $sock->autoflush (1);
95     print {$sock}
96     "POST / HTTP/1.1\r\nHost: localhost\r\n"
97     . "Content-Type: application/json\r\nContent-Length: 2\r\n\r\n{}";
98     my $response = IO::Select->new ($sock)->can_read (3) ? <$sock> : undef;
99     like $response, qr{\AHTTP/1\.1 401\b},
100     "$kind: TCP listener still serves authenticated MCP HTTP";
101     close $sock;
102     } else {
103     fail "$kind: HTTP listener response";
104     }
105    
106     close $in;
107     is wait_exit ($pid), 0, "$kind: stdin EOF stops the child cleanly";
108     my $extra = <$out>;
109     ok !defined $extra, "$kind: no extraneous stdout after final MCP response";
110     close $out;
111     close $err;
112     }
113    
114     done_testing;