#include <catch.hpp>

#include <cds-pubsub/stage_address.hh>

#include <stdexcept>

TEST_CASE( "A stage address splits into scheme and path" )
{
    auto stage = pub_sub::parse_stage_address( "tcp://127.0.0.1:9000" );
    REQUIRE( stage.scheme == "tcp" );
    REQUIRE( stage.path == "127.0.0.1:9000" );
    REQUIRE( stage.arguments.empty( ) );

    stage = pub_sub::parse_stage_address( "zstd://3" );
    REQUIRE( stage.scheme == "zstd" );
    REQUIRE( stage.path == "3" );
    REQUIRE( stage.arguments.empty( ) );

    stage =
        pub_sub::parse_stage_address( "multi://127.0.0.1/239.192.0.1:9000" );
    REQUIRE( stage.scheme == "multi" );
    REQUIRE( stage.path == "127.0.0.1/239.192.0.1:9000" );

    /* an empty path is a parse-level pass, the consumer rejects it */
    stage = pub_sub::parse_stage_address( "x://" );
    REQUIRE( stage.scheme == "x" );
    REQUIRE( stage.path == "" );
    REQUIRE( stage.arguments.empty( ) );
}

TEST_CASE( "A stage address parses query arguments" )
{
    auto stage = pub_sub::parse_stage_address(
        "tcp://127.0.0.1:9000?max_depth=16&label=a" );
    REQUIRE( stage.scheme == "tcp" );
    REQUIRE( stage.path == "127.0.0.1:9000" );
    REQUIRE( stage.arguments.size( ) == 2 );
    REQUIRE( stage.arguments.at( "max_depth" ) == "16" );
    REQUIRE( stage.arguments.at( "label" ) == "a" );

    stage = pub_sub::parse_stage_address( "tcp://h:1?max_depth=3" );
    REQUIRE( stage.arguments.size( ) == 1 );
    REQUIRE( stage.arguments.at( "max_depth" ) == "3" );
}

TEST_CASE( "Stage address values may contain = and ? and may be empty" )
{
    auto stage = pub_sub::parse_stage_address( "x://p?k=a=b?c" );
    REQUIRE( stage.path == "p" );
    REQUIRE( stage.arguments.at( "k" ) == "a=b?c" );

    stage = pub_sub::parse_stage_address( "x://p?k=" );
    REQUIRE( stage.arguments.at( "k" ) == "" );
}

TEST_CASE( "A bare trailing ? yields no arguments" )
{
    auto stage = pub_sub::parse_stage_address( "tcp://127.0.0.1:9000?" );
    REQUIRE( stage.scheme == "tcp" );
    REQUIRE( stage.path == "127.0.0.1:9000" );
    REQUIRE( stage.arguments.empty( ) );
}

TEST_CASE( "Malformed stage addresses throw" )
{
    /* no :// or an empty scheme */
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "no-scheme" ),
                       std::runtime_error );
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "://path" ),
                       std::runtime_error );
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "" ), std::runtime_error );

    /* malformed arguments */
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "x://p?a" ),
                       std::runtime_error );
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "x://p?=1" ),
                       std::runtime_error );
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "x://p?a=1&&b=2" ),
                       std::runtime_error );
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "x://p?a=1&" ),
                       std::runtime_error );

    /* duplicate keys */
    REQUIRE_THROWS_AS( pub_sub::parse_stage_address( "x://p?a=1&a=2" ),
                       std::runtime_error );
}
