#!perl

# Copyright (C) 2008, Sebastian Riedel.

use strict;
use warnings;

use Test::More tests => 160;

# When will I learn?
# The answer to life's problems aren't at the bottom of a bottle,
# they're on TV!
use_ok('Mojo::Cache::File');
use_ok('Mojo::Content');
use_ok('Mojo::Content::MultiPart');
use_ok('Mojo::Cookie::Request');
use_ok('Mojo::Cookie::Response');
use_ok('Mojo::Headers');
use_ok('Mojo::Message::Request');
use_ok('Mojo::Message::Response');

# Parse HTTP 1.1 start line, no headers and body
my $req = Mojo::Message::Request->new;
$req->parse("GET / HTTP/1.1\x0d\x0a\x0d\x0a");
is($req->state, 'done');
is($req->method, 'GET');
is($req->major_version, 1);
is($req->minor_version, 1);
is($req->url, '/');

# Parse HTTP 1.0 start line and headers, no body
$req = Mojo::Message::Request->new;
$req->parse("GET /foo/bar/baz.html HTTP/1.0\x0d\x0a");
$req->parse("Content-Type: text/plain\x0d\x0a");
$req->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
is($req->state, 'done');
is($req->method, 'GET');
is($req->major_version, 1);
is($req->minor_version, 0);
is($req->url, '/foo/bar/baz.html');
is($req->headers->content_type, 'text/plain');
is($req->headers->content_length, 0);

# Parse full HTTP 1.0 request
$req = Mojo::Message::Request->new;
$req->parse('GET /foo/bar/baz.html?fo');
$req->parse("o=13#23 HTTP/1.0\x0d\x0aContent");
$req->parse('-Type: text/');
$req->parse("plain\x0d\x0aContent-Length: 27\x0d\x0a\x0d\x0aHell");
$req->parse("o World!\n1234\nlalalala\n");
is($req->state, 'done');
is($req->method, 'GET');
is($req->major_version, 1);
is($req->minor_version, 0);
is($req->url, '/foo/bar/baz.html?foo=13#23');
is($req->headers->content_type, 'text/plain');
is($req->headers->content_length, 27);

# Parse HTTP 0.9 request
$req = Mojo::Message::Request->new;
$req->parse("GET /\x0d\x0a\x0d\x0a");
is($req->state, 'done');
is($req->method, 'GET');
is($req->major_version, 0);
is($req->minor_version, 9);
is($req->url, '/');

# Parse HTTP 1.1 chunked request
$req = Mojo::Message::Request->new;
$req->parse("POST /foo/bar/baz.html?foo=13#23 HTTP/1.1\x0d\x0a");
$req->parse("Content-Type: text/plain\x0d\x0a");
$req->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
$req->parse("4\x0d\x0a");
$req->parse("abcd\x0d\x0a");
$req->parse("9\x0d\x0a");
$req->parse("abcdefghi\x0d\x0a");
$req->parse("0\x0d\x0a");
is($req->state, 'done');
is($req->method, 'POST');
is($req->major_version, 1);
is($req->minor_version, 1);
is($req->url, '/foo/bar/baz.html?foo=13#23');
is($req->headers->content_type, 'text/plain');
is($req->content->cache->cache_length, 13);
is($req->content->cache->slurp, 'abcdabcdefghi');

# Parse HTTP 1.1 chunked request with trailing headers
$req = Mojo::Message::Request->new;
$req->parse("POST /foo/bar/baz.html?foo=13#23 HTTP/1.1\x0d\x0a");
$req->parse("Content-Type: text/plain\x0d\x0a");
$req->parse("Transfer-Encoding: chunked\x0d\x0a");
$req->parse("Trailer: X-Trailer1; X-Trailer2\x0d\x0a\x0d\x0a");
$req->parse("4\x0d\x0a");
$req->parse("abcd\x0d\x0a");
$req->parse("9\x0d\x0a");
$req->parse("abcdefghi\x0d\x0a");
$req->parse("0\x0d\x0a");
$req->parse("X-Trailer1: test\x0d\x0a");
$req->parse("X-Trailer2: 123\x0d\x0a\x0d\x0a");
is($req->state, 'done');
is($req->method, 'POST');
is($req->major_version, 1);
is($req->minor_version, 1);
is($req->url, '/foo/bar/baz.html?foo=13#23');
is($req->headers->content_type, 'text/plain');
is($req->headers->header('X-Trailer1'), 'test');
is($req->headers->header('X-Trailer2'), '123');
is($req->content->cache->cache_length, 13);
is($req->content->cache->slurp, 'abcdabcdefghi');

# Parse HTTP 1.1 multipart request
$req = Mojo::Message::Request->new;
$req->parse("GET /foo/bar/baz.html?foo=13#23 HTTP/1.1\x0d\x0a");
$req->parse("Content-Length: 814\x0d\x0a");
$req->parse('Content-Type: multipart/form-data; bo');
$req->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
$req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
$req->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
$req->parse("\x0d\x0ahallo welt test123\n");
$req->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
$req->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
$req->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
$req->parse('Content-Disposition: form-data; name="upload"; file');
$req->parse("name=\"hello.pl\"\x0d\x0a");
$req->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
$req->parse("#!/usr/bin/perl\n\n");
$req->parse("use strict;\n");
$req->parse("use warnings;\n\n");
$req->parse("print \"Hello World :)\\n\"\n");
$req->parse("\x0d\x0a------------0xKhTmLbOuNdArY--");
is($req->state, 'done');
is($req->method, 'GET');
is($req->major_version, 1);
is($req->minor_version, 1);
is($req->url, '/foo/bar/baz.html?foo=13#23');
like($req->headers->content_type, qr/multipart\/form-data/);
is(ref $req->content->parts->[0], 'Mojo::Content');
is(ref $req->content->parts->[1], 'Mojo::Content');
is(ref $req->content->parts->[2], 'Mojo::Content');
is($req->content->parts->[0]->cache->slurp, "hallo welt test123\n");

# Build minimal HTTP 1.1 request
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/');
is($req->build, "GET / HTTP/1.1\x0d\x0aHost: 127.0.0.1\x0d\x0a\x0d\x0a");

# Build HTTP 1.1 start line and header
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/foo/bar');
$req->headers->expect('100-continue');
is($req->build,
    "GET /foo/bar HTTP/1.1\x0d\x0a"
  . "Expect: 100-continue\x0d\x0a"
  . "Host: 127.0.0.1\x0d\x0a\x0d\x0a"
);

# Build full HTTP 1.1 request
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/foo/bar');
$req->headers->expect('100-continue');
$req->body("Hello World!\n");
is($req->build,
    "GET /foo/bar HTTP/1.1\x0d\x0a"
  . "Expect: 100-continue\x0d\x0a"
  . "Host: 127.0.0.1\x0d\x0a"
  . "Content-Length: 13\x0d\x0a\x0d\x0a"
  . "Hello World!\n"
);

# Build full HTTP 1.1 proxy request
my $backup = $ENV{HTTP_PROXY} || undef;
$ENV{HTTP_PROXY} = 'http://foo:bar@127.0.0.1:8080';
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/foo/bar');
$req->headers->expect('100-continue');
$req->body("Hello World!\n");
is($req->build,
    "GET http://127.0.0.1/foo/bar HTTP/1.1\x0d\x0a"
  . "Expect: 100-continue\x0d\x0a"
  . "Host: 127.0.0.1\x0d\x0a"
  . "Proxy-Authorization: Basic Zm9vOmJhcg==\x0d\x0a"
  . "Content-Length: 13\x0d\x0a\x0d\x0a"
  . "Hello World!\n"
);
$ENV{HTTP_PROXY} = $backup;

# Build HTTP 1.1 multipart request
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/foo/bar');
$req->content(Mojo::Content::MultiPart->new);
$req->headers->content_type('multipart/mixed; boundary=7am1X');
push @{$req->content->parts}, Mojo::Content->new;
$req->content->parts->[-1]->cache->add_chunk('Hallo Welt lalalala!');
my $content = Mojo::Content->new;
$content->cache->add_chunk("lala\nfoobar\nperl rocks\n");
$content->headers->content_type('text/plain');
push @{$req->content->parts}, $content;
is($req->build,
    "GET /foo/bar HTTP/1.1\x0d\x0a"
  . "Host: 127.0.0.1\x0d\x0a"
  . "Content-Length: 104\x0d\x0a"
  . "Content-Type: multipart/mixed; boundary=7am1X\x0d\x0a\x0d\x0a"
  . "\x0d\x0a--7am1X\x0d\x0a"
  . "Hallo Welt lalalala!"
  . "\x0d\x0a--7am1X\x0d\x0a"
  . "Content-Type: text/plain\x0d\x0a\x0d\x0a"
  . "lala\nfoobar\nperl rocks\n"
  . "\x0d\x0a--7am1X--"
);

# Build HTTP 1.1 chunked request
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1:8080/foo/bar');
$req->headers->transfer_encoding('chunked');
my $counter = 1;
$req->body(sub {
    my $self = shift;
    my $chunk = '';
    $chunk = "hello world!" if $counter == 1;
    $chunk = "hello world2!\n\n" if $counter == 2;
    $counter++;
    return $chunk;
});
is($req->build,
    "GET /foo/bar HTTP/1.1\x0d\x0a"
  . "Transfer-Encoding: chunked\x0d\x0a"
  . "Host: 127.0.0.1:8080\x0d\x0a\x0d\x0a"
  . "c\x0d\x0a"
  . "hello world!"
  . "\x0d\x0af\x0d\x0a"
  . "hello world2!\n\n"
  . "\x0d\x0a0\x0d\x0a"
);

# Build HTTP 1.1 chunked request with trailing headers
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/foo/bar');
$req->headers->transfer_encoding('chunked');
$req->headers->trailer('X-Test; X-Test2');
$counter = 1;
$req->body(sub {
    my $self = shift;
    my $chunk = Mojo::Headers->new;
    $chunk->header('X-Test', 'test');
    $chunk->header('X-Test2', '123');
    $chunk = "hello world!" if $counter == 1;
    $chunk = "hello world2!\n\n" if $counter == 2;
    $counter++;
    return $chunk;
});
is($req->build,
    "GET /foo/bar HTTP/1.1\x0d\x0a"
  . "Trailer: X-Test; X-Test2\x0d\x0a"
  . "Transfer-Encoding: chunked\x0d\x0a"
  . "Host: 127.0.0.1\x0d\x0a\x0d\x0a"
  . "c\x0d\x0a"
  . "hello world!"
  . "\x0d\x0af\x0d\x0a"
  . "hello world2!\n\n"
  . "\x0d\x0a0\x0d\x0a"
  . "X-Test: test\x0d\x0a"
  . "X-Test2: 123\x0d\x0a\x0d\x0a"
);

# Status code and message
my $res = Mojo::Message::Response->new;
is($res->code, 200);
is($res->default_message, 'OK');
is($res->message, undef);
$res->message('Test');
is($res->message, 'Test');
$res->code(500);
is($res->code, 500);
is($res->message, 'Test');
is($res->default_message, 'Internal Server Error');
$res = Mojo::Message::Response->new;
is($res->code(400)->default_message, 'Bad Request');

# Parse HTTP 1.1 response start line, no headers and body
$res = Mojo::Message::Response->new;
$res->parse("HTTP/1.1 200 OK\x0d\x0a\x0d\x0a");
is($res->state, 'done');
is($res->code, 200);
is($res->message, 'OK');
is($res->major_version, 1);
is($res->minor_version, 1);

# Parse HTTP 0.9 response
$res = Mojo::Message::Response->new;
$res->parse("HTT... this is just a document and valid HTTP 0.9\n\n");
is($res->state, 'done');
is($res->major_version, 0);
is($res->minor_version, 9);
is($res->body, "HTT... this is just a document and valid HTTP 0.9\n\n");

# Parse HTTP 1.0 response start line and headers but no body
$res = Mojo::Message::Response->new;
$res->parse("HTTP/1.0 404 Damn it\x0d\x0a");
$res->parse("Content-Type: text/plain\x0d\x0a");
$res->parse("Content-Length: 0\x0d\x0a\x0d\x0a");
is($res->state, 'done');
is($res->code, 404);
is($res->message, 'Damn it');
is($res->major_version, 1);
is($res->minor_version, 0);
is($res->headers->content_type, 'text/plain');
is($res->headers->content_length, 0);

# Parse full HTTP 1.0 response
$res = Mojo::Message::Response->new;
$res->parse("HTTP/1.0 500 Internal Server Error\x0d\x0a");
$res->parse("Content-Type: text/plain\x0d\x0a");
$res->parse("Content-Length: 27\x0d\x0a\x0d\x0a");
$res->parse("Hello World!\n1234\nlalalala\n");
is($res->state, 'done');
is($res->code, 500);
is($res->message, 'Internal Server Error');
is($res->major_version, 1);
is($res->minor_version, 0);
is($res->headers->content_type, 'text/plain');
is($res->headers->content_length, 27);

# Parse HTTP 1.1 chunked response
$res = Mojo::Message::Response->new;
$res->parse("HTTP/1.1 500 Internal Server Error\x0d\x0a");
$res->parse("Content-Type: text/plain\x0d\x0a");
$res->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
$res->parse("4\x0d\x0a");
$res->parse("abcd\x0d\x0a");
$res->parse("9\x0d\x0a");
$res->parse("abcdefghi\x0d\x0a");
$res->parse("0\x0d\x0a");
is($res->state, 'done');
is($res->code, 500);
is($res->message, 'Internal Server Error');
is($res->major_version, 1);
is($res->minor_version, 1);
is($res->headers->content_type, 'text/plain');
is($res->content->body_length, 13);

# Parse HTTP 1.1 multipart response
$res = Mojo::Message::Response->new;
$res->parse("HTTP/1.1 200 OK\x0d\x0a");
$res->parse("Content-Length: 814\x0d\x0a");
$res->parse('Content-Type: multipart/form-data; bo');
$res->parse("undary=----------0xKhTmLbOuNdArY\x0d\x0a\x0d\x0a");
$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
$res->parse("Content-Disposition: form-data; name=\"text1\"\x0d\x0a");
$res->parse("\x0d\x0ahallo welt test123\n");
$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
$res->parse("Content-Disposition: form-data; name=\"text2\"\x0d\x0a");
$res->parse("\x0d\x0a\x0d\x0a------------0xKhTmLbOuNdArY\x0d\x0a");
$res->parse('Content-Disposition: form-data; name="upload"; file');
$res->parse("name=\"hello.pl\"\x0d\x0a\x0d\x0a");
$res->parse("Content-Type: application/octet-stream\x0d\x0a\x0d\x0a");
$res->parse("#!/usr/bin/perl\n\n");
$res->parse("use strict;\n");
$res->parse("use warnings;\n\n");
$res->parse("print \"Hello World :)\\n\"\n");
$res->parse("\x0d\x0a------------0xKhTmLbOuNdArY--");
is($res->state, 'done');
is($res->code, 200);
is($res->message, 'OK');
is($res->major_version, 1);
is($res->minor_version, 1);
ok($res->headers->content_type =~ /multipart\/form-data/);
is(ref $res->content->parts->[0], 'Mojo::Content');
is(ref $res->content->parts->[1], 'Mojo::Content');
is(ref $res->content->parts->[2], 'Mojo::Content');
is($res->content->parts->[0]->cache->slurp, "hallo welt test123\n");

# Build HTTP 1.1 response start line with minimal headers
$res = Mojo::Message::Response->new;
$res->code(404);
$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT');
is($res->build,
    "HTTP/1.1 404 Not Found\x0d\x0a"
  . "Date: Sun, 17 Aug 2008 16:27:35 GMT\x0d\x0a\x0d\x0a"
);

# Build HTTP 1.1 response start line and header
$res = Mojo::Message::Response->new;
$res->code(200);
$res->headers->connection('keep-alive');
$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT');
is($res->build,
    "HTTP/1.1 200 OK\x0d\x0a"
  . "Connection: keep-alive\x0d\x0a"
  . "Date: Sun, 17 Aug 2008 16:27:35 GMT\x0d\x0a\x0d\x0a"
);

# Build full HTTP 1.1 response
$res = Mojo::Message::Response->new;
$res->code(200);
$res->headers->connection('keep-alive');
$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT');
$res->body("Hello World!\n");
is($res->build,
    "HTTP/1.1 200 OK\x0d\x0a"
  . "Connection: keep-alive\x0d\x0a"
  . "Date: Sun, 17 Aug 2008 16:27:35 GMT\x0d\x0a"
  . "Content-Length: 13\x0d\x0a\x0d\x0a"
  . "Hello World!\n"
);

# Build HTTP 0.9 response
$res = Mojo::Message::Response->new;
$res->major_version(0);
$res->minor_version(9);
$res->body("this is just a document and valid HTTP 0.9\nlalala\n");
is($res->build, "this is just a document and valid HTTP 0.9\nlalala\n");

# Build HTTP 1.1 multipart response
$res = Mojo::Message::Response->new;
$res->content(Mojo::Content::MultiPart->new);
$res->code(200);
$res->headers->content_type('multipart/mixed; boundary=7am1X');
$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT');
push @{$res->content->parts}, Mojo::Content->new(
    cache => Mojo::Cache::File->new
);
$res->content->parts->[-1]->cache->add_chunk('Hallo Welt lalalalalala!');
$content = Mojo::Content->new;
$content->cache->add_chunk("lala\nfoobar\nperl rocks\n");
$content->headers->content_type('text/plain');
push @{$res->content->parts}, $content;
is($res->build,
    "HTTP/1.1 200 OK\x0d\x0a"
  . "Date: Sun, 17 Aug 2008 16:27:35 GMT\x0d\x0a"
  . "Content-Length: 108\x0d\x0a"
  . "Content-Type: multipart/mixed; boundary=7am1X\x0d\x0a\x0d\x0a"
  . "\x0d\x0a--7am1X\x0d\x0a"
  . 'Hallo Welt lalalalalala!'
  . "\x0d\x0a--7am1X\x0d\x0a"
  . "Content-Type: text/plain\x0d\x0a\x0d\x0a"
  . "lala\nfoobar\nperl rocks\n"
  . "\x0d\x0a--7am1X--"
);

# Parse CGI like environment variables and a body
$req = Mojo::Message::Request->new;
$req->parse({
    HTTP_CONTENT_LENGTH => 11,
    HTTP_EXPECT         => '100-continue',
    PATH_INFO           => '/test/index.cgi/foo/bar',
    QUERY_STRING        => 'lalala=23&bar=baz',
    REQUEST_METHOD      => 'POST',
    SCRIPT_NAME         => '/test/index.cgi',
    SERVER_NAME         => 'localhost:8080',
    SERVER_PROTOCOL     => 'HTTP/1.0'
});
$req->parse('Hello World');
is($req->state, 'done');
is($req->method, 'POST');
is($req->headers->expect, '100-continue');
is($req->url->path, '/test/index.cgi/foo/bar');
is($req->url->base->path, '/test/index.cgi');
is($req->url->host, 'localhost');
is($req->url->port, 8080);
is($req->url->query, 'lalala=23&bar=baz');
is($req->minor_version, '0');
is($req->major_version, '1');
is($req->body, 'Hello World');

# Parse response with cookie
$res = Mojo::Message::Response->new;
$res->parse("HTTP/1.0 200 OK\x0d\x0a");
$res->parse("Content-Type: text/plain\x0d\x0a");
$res->parse("Content-Length: 27\x0d\x0a");
$res->parse("Set-Cookie: foo=bar; Version=1; Path=/test\x0d\x0a\x0d\x0a");
$res->parse("Hello World!\n1234\nlalalala\n");
is($res->state, 'done');
is($res->code, 200);
is($res->message, 'OK');
is($res->major_version, 1);
is($res->minor_version, 0);
is($res->headers->content_type, 'text/plain');
is($res->headers->content_length, 27);
is($res->headers->set_cookie, 'foo=bar; Version=1; Path=/test');
my @cookies = $res->cookies;
is($cookies[0]->name, 'foo');
is($cookies[0]->value, 'bar');
is($cookies[0]->version, 1);
is($cookies[0]->path, '/test');

# Build HTTP 1.1 response with 2 cookies
$res = Mojo::Message::Response->new;
$res->code(404);
$res->headers->date('Sun, 17 Aug 2008 16:27:35 GMT');
$res->cookies(
    Mojo::Cookie::Response->new({
        name  => 'foo',
        value => 'bar',
        path  => '/foobar'
    }),
    Mojo::Cookie::Response->new({
        name  => 'bar',
        value => 'baz',
        path  => '/test/23'
    })
);
is($res->build,
    "HTTP/1.1 404 Not Found\x0d\x0a"
  . "Date: Sun, 17 Aug 2008 16:27:35 GMT\x0d\x0a"
  . "Set-Cookie: foo=bar; Version=1; Path=/foobar\x0d\x0a"
  . "Set-Cookie: bar=baz; Version=1; Path=/test/23\x0d\x0a\x0d\x0a"
);

# Build full HTTP 1.1 request with cookies
$req = Mojo::Message::Request->new;
$req->method('GET');
$req->url->parse('http://127.0.0.1/foo/bar');
$req->headers->expect('100-continue');
$req->cookies(
    Mojo::Cookie::Request->new({
        name  => 'foo',
        value => 'bar',
        path  => '/foobar'
    }),
    Mojo::Cookie::Request->new({
        name  => 'bar',
        value => 'baz',
        path  => '/test/23'
    })
);
$req->body("Hello World!\n");
is($req->build,
    "GET /foo/bar HTTP/1.1\x0d\x0a"
  . "Expect: 100-continue\x0d\x0a"
  . "Host: 127.0.0.1\x0d\x0a"
  . "Content-Length: 13\x0d\x0a"
  . 'Cookie: $Version=1; foo=bar; $Path=/foobar; bar=baz; $Path=/test/23'
  . "\x0d\x0a\x0d\x0a"
  . "Hello World!\n"
);

# Parse full HTTP 1.0 request with cookies
$req = Mojo::Message::Request->new;
$req->parse('GET /foo/bar/baz.html?fo');
$req->parse("o=13#23 HTTP/1.0\x0d\x0aContent");
$req->parse('-Type: text/');
$req->parse("plain\x0d\x0a");
$req->parse('Cookie: $Version=1; foo=bar; $Path=/foobar; bar=baz; $Path=/t');
$req->parse("est/23\x0d\x0a");
$req->parse("Content-Length: 27\x0d\x0a\x0d\x0aHell");
$req->parse("o World!\n1234\nlalalala\n");
is($req->state, 'done');
is($req->method, 'GET');
is($req->major_version, 1);
is($req->minor_version, 0);
is($req->url, '/foo/bar/baz.html?foo=13#23');
is($req->headers->content_type, 'text/plain');
is($req->headers->content_length, 27);
@cookies = $req->cookies;
is($cookies[0]->name, 'foo');
is($cookies[0]->value, 'bar');
is($cookies[0]->version, 1);
is($cookies[0]->path, '/foobar');
is($cookies[1]->name, 'bar');
is($cookies[1]->value, 'baz');
is($cookies[1]->version, 1);
is($cookies[1]->path, '/test/23');