### ----------------------------------------------------
### -- MongoDB
### -- Licenses: Artistic-2.0
### -- Authors: Marcel Timmerman, Paweł Pabian
### -- File: xt/Tests/Basic/060-header.rakutest
### ----------------------------------------------------
use Test;

use BSON;
use BSON::Document;
use MongoDB;
use MongoDB::Header;

#-------------------------------------------------------------------------------
my Str $log-path = @*ARGS[0];
my Version $version .= new(@*ARGS[1]);
my @server-ports = @*ARGS[2..*];
#note "args: $log-path, $version, @server-ports";

#-------------------------------------------------------------------------------
drop-send-to('mongodb');
drop-send-to('screen');
#modify-send-to( 'screen', :level(MongoDB::MdbLoglevels::Trace));
my $handle = $log-path.IO.open( :mode<wo>, :append);
add-send-to( 'mdb', :to($handle), :min-level(MongoDB::MdbLoglevels::Debug));
#set-filter(|<ObserverEmitter Timer Socket>);

info-message("Test $?FILE start");

#-------------------------------------------------------------------------------
subtest "Header encode/decode", {

  my MongoDB::Header $header .= new;

  my BSON::Document $d .= new: ( doc => 'info');
#info-message($d);
  my Buf $b = $d.encode;
#info-message($b);


  ( my Buf $h,
    my Int $req-id
  ) = $header.encode-message-header( $b.elems, OP-QUERY);
#info-message($h);

  is $h.elems, 4*4, 'Size of header is 16';
  is $req-id, 0, 'First request encoding';

  my $index = 0;
  my BSON::Document $dh = $header.decode-message-header( $h, $index);

  is $b.elems + 4*4, $dh<message-length>, 'Message length received 16';
  is $dh<request-id>, 0, "First request is $dh<request-id>";
  is $dh<op-code>, OP-QUERY.value, "Operation code is $dh<op-code>";

  ( my Buf $q-encode, $req-id) =
    $header.encode-query(
      'users.files', $d,
      :flags(C-QF-SLAVEOK.value +| C-QF-NOCURSORTIMOUT.value)
    );

  is $q-encode.elems,
     ([+] 4, 12, 4, 4, $b.elems, 4 * 4),
     "Total encoded size is {$q-encode.elems}";
  is $req-id, 1, 'Second request encoding';
};

#-------------------------------------------------------------------------------
subtest "query/reply",  {
  my MongoDB::Header $header .= new;
  my BSON::Document $d .= new: ( doc => 'info');

  my Buf $hand-made-buf .= new(
    0x29, 0x00, 0x00, 0x00,             # size 41 bytes
    0x01, 0x00, 0x00, 0x00,             # Req id = 1 (1st request in this test)
    0x0A, 0x00, 0x00, 0x00,             # resp to 10
    0x01, 0x00, 0x00, 0x00,             # OP-REPLY

    0x00, 0x00, 0x00, 0x00,             # no flags
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,             # cursor id = 0
    0x00, 0x00, 0x00, 0x00,             # cursor at 0
    0x01, 0x00, 0x00, 0x00,             # one document
    0x05, 0x00, 0x00, 0x00,             # empty document
    0x00
  );
#info-message($hand-made-buf);

  my BSON::Document $rd = $header.decode-reply($hand-made-buf);
  is $rd<message-header><request-id>, 1, 'Request id is 1';
  is $rd<number-returned>, 1, 'Number of docs is 1';
  is $rd<documents>[0].elems, 0, 'Empty document';
};

#-------------------------------------------------------------------------------
subtest "encoding get more",  {
  my MongoDB::Header $header .= new;
  my BSON::Document $d .= new;

  ( my Buf $encoded-get-more, my Int $req-id) = $header.encode-get-more(
    'testdb.testcoll',
    Buf.new( 0x02, 0x01, 0x03, 0x04, 0x03, 0x0f, 0x0e, 0x0a),
    :number-to-return(100)
  );
  is $req-id, 2, 'Third encoded request';

  my Buf $hand-made-buf .= new(
    0x30, 0x00, 0x00, 0x00,             # size 48 bytes
    0x02, 0x00, 0x00, 0x00,             # Req id = 2 (2nd request in this test)
    0x00, 0x00, 0x00, 0x00,             # resp to 0
    0xd5, 0x07, 0x00, 0x00,             # OP-GET-MORE

    0x00, 0x00, 0x00, 0x00,             # 0, reserved
    0x74, 0x65, 0x73, 0x74, 0x64, 0x62, # 'testdb.testcoll'
    0x2e, 0x74, 0x65, 0x73, 0x74, 0x63,
    0x6f, 0x6c, 0x6c, 0x00,
    0x64, 0x00, 0x00, 0x00,             # 100, number to return
    0x02, 0x01, 0x03, 0x04,
    0x03, 0x0f, 0x0e, 0x0a              # cursor id
  );

  is-deeply $encoded-get-more, $hand-made-buf, 'Encoded get more request';
};

#-------------------------------------------------------------------------------
subtest "encoding query",  {
  my MongoDB::Header $header .= new;
  my BSON::Document $qdoc .= new: (:serverStatus(1),);
  my BSON::Document $projection .= new;

  my Str $full-collection-name = 'db.cl';
  my ( $encoded-query, $request-id) = $header.encode-query(
    $full-collection-name, $qdoc, $projection,
    :flags(0), :number-to-skip(0), :number-to-return(1)
  );
  info-message("e=$encoded-query.gist(), rid=$request-id");

  my Buf $hand-made-buf .= new(
    0x3e, 0x00, 0x00, 0x00,             # size 62 bytes
    0x03, 0x00, 0x00, 0x00,             # Req id = 3 (3rd request in this test)
    0x00, 0x00, 0x00, 0x00,             # resp to 0
    0xd4, 0x07, 0x00, 0x00,             # OP-QUERY

    0x00, 0x00, 0x00, 0x00,             # 0, query flags
    0x64, 0x62, 0x2E, 0x63, 0x6C, 0x00, # 'db.cl'
    0x00, 0x00, 0x00, 0x00,             # 0, numberToSkip
    0x01, 0x00, 0x00, 0x00,             # 1, number to return
    0x17, 0x00, 0x00, 0x00,             # doc size
    0x10, 0x73, 0x65, 0x72, 0x76, 0x65, # doc
    0x72, 0x53, 0x74, 0x61, 0x74, 0x75,
    0x73, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x00,                               # end doc
    0x05, 0x00, 0x00, 0x00,             # size projection
    0x00,                               # end empty projection
  );

  is-deeply $encoded-query, $hand-made-buf, 'Encoded query';
};

#-------------------------------------------------------------------------------
subtest "encoding msg",  {
  subtest 'simple message', {
    my MongoDB::Header $header .= new;
    my BSON::Document $qdoc .= new: (:serverStatus(1),);
    my ( $encoded-msg, $request-id) = $header.encode-msg( $qdoc, :flags(0));
    info-message("e=$encoded-msg.gist(), rid=$request-id");

    my Buf $hand-made-buf .= new(
      0x3A, 0x00, 0x00, 0x00,             # size 49 bytes
      0x04, 0x00, 0x00, 0x00,             # Req id = 4 (4th request)
      0x00, 0x00, 0x00, 0x00,             # resp to 0
      0xdd, 0x07, 0x00, 0x00,             # OP-MSG

      0x00, 0x00, 0x00, 0x00,             # 0, msg flags
      0x00,                               # kind is 0: single document
      0x25, 0x00, 0x00, 0x00,             # doc size
      0x10,                               # int = 0x10
      0x73, 0x65, 0x72, 0x76, 0x65,       # serverStatus + 0x00
      0x72, 0x53, 0x74, 0x61, 0x74,
      0x75, 0x73, 0x00,
      0x01, 0x00, 0x00, 0x00,             # value 1
      0x02, 0x24, 0x64, 0x62, 0x00,       # '$db' key + 0x00
      0x05, 0x00, 0x00, 0x00,             # str size
      0x24, 0x63, 0x6D, 0x64, 0x00,       # '$cmd' = db name + 0x00
      0x00,                               # end doc
    );

    is-deeply $encoded-msg, $hand-made-buf, 'Encoded message';
  
    # TODO; decode
  }

  subtest 'complex message', {
    my MongoDB::Header $header .= new;
    my BSON::Document $qdoc .= new: (
      :insert<cll>,
      :documents([
        BSON::Document.new((:a(10),:b(11))),
        BSON::Document.new((:a(10),:b(11))),
      ]),
    );
    info-message($qdoc);

    my Str $database-name = 'db';
    my ( $encoded-msg, $request-id) = $header.encode-msg(
      $qdoc, :$database-name, :flags(0)
    );
    info-message($encoded-msg);

    my Buf $hand-made-buf .= new(
      0x2c, 0x00, 0x00, 0x00,             # size 44 bytes
      0x04, 0x00, 0x00, 0x00,             # Req id = 4 (4th request)
      0x00, 0x00, 0x00, 0x00,             # resp to 0
      0xdd, 0x07, 0x00, 0x00,             # OP-MSG

      0x00, 0x00, 0x00, 0x00,             # 0, msg flags
      0x00,                               # kind is 0: single document
      0x17, 0x00, 0x00, 0x00,             # doc size
      0x10, 0x73, 0x65, 0x72, 0x76, 0x65, # doc
      0x72, 0x53, 0x74, 0x61, 0x74, 0x75,
      0x73, 0x00, 0x01, 0x00, 0x00, 0x00,

      0x00,                               # end doc
    );

    is-deeply $encoded-msg, $hand-made-buf, 'Encoded message';
  
    # TODO; decode
  }
  

};

#-------------------------------------------------------------------------------
# Cleanup
#
done-testing;
exit(0);


