Langertha-Knarr

 view release on metacpan or  search on metacpan

t/60_psgi.t  view on Meta::CPAN

use strict;
use warnings;
use Test2::V0;

BEGIN {
  eval { require Plack::Test; require HTTP::Request; require HTTP::Request::Common; 1 }
    or plan skip_all => 'Plack::Test required for this test';
}
use Plack::Test;
use HTTP::Request::Common;
use JSON::MaybeXS;

use Langertha::Knarr;
use Langertha::Knarr::Handler::Code;
use Langertha::Knarr::PSGI;

my $json = JSON::MaybeXS->new( utf8 => 1, canonical => 1 );

my $sb = Langertha::Knarr->new(
  handler => Langertha::Knarr::Handler::Code->new(
    code => sub { 'psgi-said-hi' },
    stream_code => sub { my @p = ('a','b','c'); sub { @p ? shift @p : undef } },
  ),
  port => 0,
);

my $app = Langertha::Knarr::PSGI->new( knarr => $sb )->to_app;
my $test = Plack::Test->create($app);

# Non-streaming OpenAI
{
  my $res = $test->request(
    POST '/v1/chat/completions',
      Content_Type => 'application/json',
      Content => $json->encode({
        model => 'm',
        messages => [ { role => 'user', content => 'hi' } ],
      }),
  );
  is( $res->code, 200, 'psgi non-stream 200' );
  my $d = $json->decode($res->decoded_content);
  is( $d->{choices}[0]{message}{content}, 'psgi-said-hi', 'psgi content' );
}

# Buffered streaming OpenAI
{
  my $res = $test->request(
    POST '/v1/chat/completions',
      Content_Type => 'application/json',
      Content => $json->encode({
        model => 'm',
        messages => [ { role => 'user', content => 'hi' } ],
        stream => JSON::MaybeXS::true(),
      }),
  );
  is( $res->code, 200, 'psgi stream 200' );
  is( $res->header('Content-Type'), 'text/event-stream', 'SSE content type' );
  my $body = $res->decoded_content;
  like( $body, qr/"a"/, 'chunk a' );
  like( $body, qr/"b"/, 'chunk b' );
  like( $body, qr/"c"/, 'chunk c' );
  like( $body, qr/\[DONE\]/, 'done marker' );
}

# Models endpoint
{
  my $res = $test->request( GET '/v1/models' );
  is( $res->code, 200, 'models 200' );
  my $d = $json->decode($res->decoded_content);
  is( $d->{object}, 'list', 'list object' );
}

# Ollama buffered stream
{
  my $res = $test->request(
    POST '/api/chat',
      Content_Type => 'application/json',
      Content => $json->encode({
        model => 'm',
        messages => [ { role => 'user', content => 'hi' } ],
      }),
  );
  is( $res->code, 200, 'ollama 200' );
  is( $res->header('Content-Type'), 'application/x-ndjson', 'ndjson type' );
  my @lines = grep { length } split /\n/, $res->decoded_content;
  ok( scalar(@lines) >= 4, 'multiple ndjson lines (3 chunks + done)' );
}

done_testing;



( run in 1.378 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )