#include "helpers.h"

#include <saas/library/searchserver/http_status_config.h>
#include <saas/searchproxy/configs/searchproxyconfig.h>

#include <saas/library/daemon_base/config/daemon_config.h>
#include <saas/library/searchmap/searchmap.h>
#include <saas/library/searchmap/parsers/json/json.h>

#include <library/cpp/testing/unittest/registar.h>

#include <kernel/qtree/request/reqattrlist.h>

#include <util/charset/wide.h>
#include <util/datetime/base.h>
#include <util/folder/dirut.h>
#include <util/stream/str.h>
#include <util/system/defaults.h>

class TSearchProxyConfigTest: public TTestBase {
private:
    UNIT_TEST_SUITE(TSearchProxyConfigTest)
        UNIT_TEST(TestDefaultHttpServerOptions)
        UNIT_TEST(TestComplexHttpServerOptions)
        UNIT_TEST(TestSearchMap)
        UNIT_TEST(TestDefaultSearchConfig)
        UNIT_TEST(TestPatientSearchConfig)
        UNIT_TEST(TestComplexSearchConfigUpper)
        UNIT_TEST(TestComplexSearchConfigService)
        UNIT_TEST(TestComplexBuiltinSearchPageTemplate)
        UNIT_TEST(TestComplexSearchConfigUnknownService)
        UNIT_TEST(TestDefaultHttpStatusManagerConfig)
        UNIT_TEST(TestComplexHttpStatusManagerConfig)
        UNIT_TEST(TestWizardConfig)
        UNIT_TEST(TestGlobalHttpStatuses)
        UNIT_TEST(TestGlobalAllowDynamicWeights)
        UNIT_TEST(TestEnableMetaPRSOps)
        UNIT_TEST(TestGlobalMisspell)
        UNIT_TEST(TestServiceExceptions)
        UNIT_TEST(TestDaemonConfig)
        UNIT_TEST(TestCgiSectionsOverride)
    UNIT_TEST_SUITE_END();

private:
    typedef NSearchMapParser::TSearchMap TSearchMap;

    TSimpleSharedPtr<TSearchProxyConfig> GetSearchProxyConfig(
        const TSearchMap& searchMap = TSearchMap())
    {
        TConfigPatcher cp;
        return new TSearchProxyConfig(::GetSearchProxyConfig(9000).data(),
            TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
    }

    TSimpleSharedPtr<TSearchProxyConfig> GetPatientSearchProxyConfig(
        const TSearchMap& searchMap = TSearchMap())
    {
        TConfigPatcher cp;
        return new TSearchProxyConfig(::GetPatientSearchProxyConfig(9000).data(),
            TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
    }

    TSimpleSharedPtr<TSearchProxyConfig> GetComplexSearchProxyConfig() {
        TString searchMapString = GetSearchMap({
            GetSearchMapLine("test", 17777), GetSearchMapLine("test2", 18777)
        });
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        TConfigPatcher cp;
        return new TSearchProxyConfig(
            "<SearchProxy>\n"
            "\tHost: ya.ru\n"
            "\tPort: 17000\n"
            "\tThreads: 5\n"
            "\tMaxQueueSize: 10\n"
            "\tMaxConnections: 15\n"

            "\t<SearchConfig>\n"
            "\t\tWorkDir: /usr/\n"
            "\t\tThreads: 20\n"
            "\t\tConnectTimeout: 25ms\n"
            "\t\tGlobalTimeout: 30\n"
            "\t\tSwitchToSlaveTimeout: 35\n"
            "\t\tTwoStepQuery: true\n"
            "\t\tEventLog: event.log\n"
            "\t\t<QueryLanguage>\n"
            "\t\t\tatata: ATTR_LITERAL\n"
            "\t\t</QueryLanguage>\n"
            "\t\t<SearchPageTemplate>\n"
            "\t\t\tMethod: perl\n"
            "\t\t\tModule: report.phtml\n"
            "\t\t</SearchPageTemplate>\n"
            "\t\t<TWizardMain>\n"
            "\t\t\tWorkDir: /var/\n"
            "\t\t</TWizardMain>\n"
            "\t\t<UserParams>\n"
            "\t\t\tAnotherParameter: AnotherValue\n"
            "\t\t</UserParams>\n"
            "\t</SearchConfig>\n"

            "\t<Service>\n"
            "\t\tName: test\n"
            "\t\t<QueryCache>\n"
            "\t\t\tLifeTime: 100\n"
            "\t\t</QueryCache>\n"
            "\t\t<QueryLanguage>\n"
            "\t\t\t51: ZONE\n"
            "\t\t</QueryLanguage>\n"
            "\t\t<SearchPageTemplate>\n"
            "\t\t\tMethod: binary\n"
            "\t\t\tModule: libsearchproxy-logreport.so\n"
            "\t\t\tOptions: SomeOption\n"
            "\t\t</SearchPageTemplate>\n"
            "\t\t<UserParams>\n"
            "\t\t\tSomeParameter: SomeValue\n"
            "\t\t</UserParams>\n"
            "\t\t<HttpStatuses>\n"
            "\t\t\tIncompleteStatus: 502\n"
            "\t\t\tSyntaxErrorStatus: 400\n"
            "\t\t\tEmptySetStatus: 201\n"
            "\t\t</HttpStatuses>\n"
            "\t\t<Misspell>\n"
            "\t\t\tHost: misc-spell.yandex.net\n"
            "\t\t\tPort: 19031\n"
            "\t\t</Misspell>\n"
            "\t</Service>\n"

            "\t<Service>\n"
            "\t\tName: test2\n"
            "\t\t<SearchPageTemplate>\n"
            "\t\t\tMethod: builtin\n"
            "\t\t\tModule: searchproxy\n"
            "\t\t</SearchPageTemplate>\n"
            "\t\t<Misspell>\n"
            "\t\t\tHost: erratum-test.yandex.ru\n"
            "\t\t\tPort: 19036\n"
            "\t\t\tConnectionTimeout: 42\n"
            "\t\t\tInteractionTimeout: 69\n"
            "\t\t\tEnablePorno: true\n"
            "\t\t</Misspell>\n"
            "\t\t<TWizardMain>\n"
            "\t\t\tWorkDir: /lib/\n"
            "\t\t</TWizardMain>\n"
            "\t</Service>\n"
            "\t<Logger>\n"
            "\t\tInfoLog: info.log\n"
            "\t\tErrorLog: error.log\n"
            "\t</Logger>\n"
            "</SearchProxy>",
            TDaemonConfig(GetDaemonConfig(), true), parser.GetSearchMap(), cp);
    }

    void SearchPageTemplateExceptions(const TString& data, const TString& text)
    {
        TString searchMapString(GetSearchMapRule("test", 17777));
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        try {
            TConfigPatcher cp;
            TSearchProxyConfig config(TString::Join(
                    "<SearchProxy>\n"
                    "\tHost: ya.ru\n"
                    "\tPort: 9000\n"
                    "\t<Service>\n"
                    "\t\tName: test\n"
                    "\t\t<SearchPageTemplate>\n",
                    data,
                    "\t\t</SearchPageTemplate>\n"
                    "\t</Service>\n"
                    "</SearchProxy>\n").data(),
                TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
        } catch (const yexception& exc) {
            UNIT_ASSERT_VALUES_EQUAL(exc.what(), text);
            return;
        }
        UNIT_FAIL("exception expected with text: " + text.Quote());
    }

    void FileBasedConfigExceptions(const TString& data, const TString& text)
    {
        try {
            TSearchProxyConfig(TServerConfigConstructorParams((
                "<SearchProxy>\n" +
                data +
                "</SearchProxy>\n" +
                GetDaemonConfig()).data()
            ));
        } catch (const yexception& exc) {
            UNIT_ASSERT_VALUES_EQUAL(exc.what(), text);
            return;
        }
        UNIT_FAIL("exception expected with text: " + text.Quote());
    }

    void ServiceExceptions(const TString& data, const TString& text)
    {
        TString searchMapString(GetSearchMapRule("test", 17777));
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        try {
            TConfigPatcher cp;
            TSearchProxyConfig config(TString::Join(
                    "<SearchProxy>\n"
                    "\tHost: ya.ru\n"
                    "\tPort: 9000\n"
                    "\t<Service>\n",
                    data,
                    "\t</Service>\n"
                    "</SearchProxy>\n").data(),
                TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
        } catch (const yexception& exc) {
            UNIT_ASSERT_VALUES_EQUAL(exc.what(), text);
            return;
        }
        UNIT_FAIL("exception expected with text: " + text.Quote());
    }

public:
    void TestDefaultHttpServerOptions()
    {
        TSimpleSharedPtr<TSearchProxyConfig> config = GetSearchProxyConfig();
        const THttpServerOptions& options = config->GetHttpServerOptions();
        UNIT_ASSERT_VALUES_EQUAL(options.Host, "localhost");
        UNIT_ASSERT_VALUES_EQUAL(options.Port, 9000);
        UNIT_ASSERT_VALUES_EQUAL(options.nThreads, ui32(0));
        UNIT_ASSERT_VALUES_EQUAL(options.MaxQueueSize, ui32(0));
        UNIT_ASSERT_VALUES_EQUAL(options.MaxConnections, ui32(0));
        UNIT_ASSERT_EQUAL(config->GetLoggerConfig().Get(), NULL);
    }

    void TestComplexHttpServerOptions()
    {
        TSimpleSharedPtr<TSearchProxyConfig> config = GetComplexSearchProxyConfig();
        const THttpServerOptions& options = config->GetHttpServerOptions();
        UNIT_ASSERT_VALUES_EQUAL(options.Host, "ya.ru");
        UNIT_ASSERT_VALUES_EQUAL(options.Port, 17000);
        UNIT_ASSERT_VALUES_EQUAL(options.nThreads, ui32(5));
        UNIT_ASSERT_VALUES_EQUAL(options.MaxQueueSize, ui32(10));
        UNIT_ASSERT_VALUES_EQUAL(options.MaxConnections, ui32(15));
    }

    void TestSearchMap()
    {
        TString searchMapString(GetSearchMapRule("test", 17777));
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        TSimpleSharedPtr<TSearchProxyConfig> config = GetSearchProxyConfig(parser.GetSearchMap());
        NSearchMapParser::TSearchMap searchMap = config->GetSearchMap();
        UNIT_ASSERT_VALUES_EQUAL(searchMap.GetServiceMap().size(), size_t(1));
        NSearchMapParser::TSearchMap::TServiceMap::value_type front = *searchMap.GetServiceMap().begin();
        UNIT_ASSERT_VALUES_EQUAL(front.first, "test");
        NSearchMapParser::TSearchMap::TServiceMap::mapped_type cluster = front.second;
        NSearchMapParser::TSearchReplica& replica = cluster.Replicas[0];
        UNIT_ASSERT_VALUES_EQUAL(replica.begin()->first.GetMin(), 0);
        UNIT_ASSERT_VALUES_EQUAL(replica.begin()->first.GetMax(), 65533);
        UNIT_ASSERT_VALUES_EQUAL(replica.begin()->second.size(), size_t(1));
        NSearchMapParser::TSearchInformation searchInformation =
            replica.begin()->second.front();
        UNIT_ASSERT_VALUES_EQUAL(searchInformation.Name, "localhost");
        UNIT_ASSERT_VALUES_EQUAL(searchInformation.SearchPort, 17777);
    }

    void TestDefaultSearchConfig()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetSearchProxyConfig();
        TSearchServerConfig config =
            *searchProxyConfig->GetServiceConfig("test").GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(config.WorkDir,
            NFs::CurrentWorkingDirectory() + GetDirectorySeparator());
        UNIT_ASSERT_VALUES_EQUAL(config.TwoStepQuery, false);
        UNIT_ASSERT_VALUES_EQUAL(config.CommonSourceOptions.TwoStepQuery,
            false);
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetRequestThreads(), ui32(0));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            size(), size_t(1));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            front(), TDuration::MilliSeconds(30));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.TimeoutTable.
            front(), TDuration::MilliSeconds(100));
        UNIT_ASSERT_VALUES_EQUAL(config.IncompleteTasksCheckInterval,
            TDuration::MilliSeconds(50));
    }

    void TestPatientSearchConfig()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetPatientSearchProxyConfig();
        TServiceConfig serviceConfig = searchProxyConfig->GetServiceConfig("abracadabra");
        TSearchServerConfig config =
            *serviceConfig.GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(config.WorkDir,
            NFs::CurrentWorkingDirectory() + GetDirectorySeparator());
        UNIT_ASSERT_VALUES_EQUAL(config.TwoStepQuery, false);
        UNIT_ASSERT_VALUES_EQUAL(config.CommonSourceOptions.TwoStepQuery,
            false);
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetRequestThreads(), ui32(64));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            size(), size_t(1));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            front(), TDuration::MilliSeconds(10));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.TimeoutTable.
            front(), TDuration::MilliSeconds(30000));
        UNIT_ASSERT_VALUES_EQUAL(config.IncompleteTasksCheckInterval,
            TDuration::MilliSeconds(15000));
        UNIT_ASSERT_EQUAL(config.MisspellCorrectorConfig.Get(), NULL);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().IncompleteStatus, 502);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().SyntaxErrorStatus, 400);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().EmptySetStatus, 404);
    }

    void TestComplexSearchConfigUpper()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        TSearchServerConfig config =
            *searchProxyConfig->GetServiceConfig("").GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(config.TwoStepQuery, true);
        UNIT_ASSERT_VALUES_EQUAL(config.CommonSourceOptions.TwoStepQuery,
            true);
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetRequestThreads(), ui32(20));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            size(), size_t(1));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            front(), TDuration::MilliSeconds(25));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.TimeoutTable.
            front(), TDuration::MilliSeconds(30));
        UNIT_ASSERT_VALUES_EQUAL(config.IncompleteTasksCheckInterval,
            TDuration::MilliSeconds(35));
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportMethod(), "perl");
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportOptions(), "");
        UNIT_ASSERT(TReqAttrList::IsAttr(
            config.ReqAttrList->GetType(u"atata")));
        UNIT_ASSERT_EQUAL(config.MisspellCorrectorConfig.Get(), NULL);
    }

    void TestComplexSearchConfigService()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        TSearchServerConfig config =
            *searchProxyConfig->GetServiceConfig("test").GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(config.TwoStepQuery, true);
        UNIT_ASSERT_VALUES_EQUAL(config.CommonSourceOptions.TwoStepQuery,
            true);
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetRequestThreads(), ui32(20));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            size(), size_t(1));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            front(), TDuration::MilliSeconds(25));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.TimeoutTable.
            front(), TDuration::MilliSeconds(30));
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportMethod(), "binary");
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportOptions(), "SomeOption");
        UNIT_ASSERT_VALUES_EQUAL(config.CacheOptions.Main().UserTimeout, 6000);
        UNIT_ASSERT(TReqAttrList::IsZone(
            config.ReqAttrList->GetType(u"51")));
        UNIT_ASSERT(config.UserDirectives.contains("SomeParameter"));
        UNIT_ASSERT_VALUES_EQUAL(config.UserDirectives.Get("SomeParameter"),
            "SomeValue");
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.Host,
            "misc-spell.yandex.net");
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.Port, 19031);
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.ConnectionTimeout,
            TDuration::MilliSeconds(30));
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.InteractionTimeout,
            TDuration::MilliSeconds(150));
        UNIT_ASSERT_VALUES_EQUAL(config.MisspellCorrectorConfig->EnablePorno,
            false);
    }

    void TestComplexBuiltinSearchPageTemplate()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        TSearchServerConfig config = *searchProxyConfig->GetServiceConfig("test2").GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportMethod(), "builtin");
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportModuleName(), "searchproxy");
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportOptions(), "");
        UNIT_ASSERT(config.UserDirectives.contains("AnotherParameter"));
        UNIT_ASSERT_VALUES_EQUAL(config.UserDirectives.Get("AnotherParameter"),
            "AnotherValue");
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.Host,
            "erratum-test.yandex.ru");
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.Port, 19036);
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.ConnectionTimeout,
            TDuration::MilliSeconds(42));
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.InteractionTimeout,
            TDuration::MilliSeconds(69));
        UNIT_ASSERT_VALUES_EQUAL(config.MisspellCorrectorConfig->EnablePorno,
            true);
    }

    void TestComplexSearchConfigUnknownService()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        TSearchServerConfig config =
            *searchProxyConfig->GetServiceConfig("abracadabra").GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(config.TwoStepQuery, true);
        UNIT_ASSERT_VALUES_EQUAL(config.CommonSourceOptions.TwoStepQuery,
            true);
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetRequestThreads(), ui32(20));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            size(), size_t(1));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.ConnectTimeouts.
            front(), TDuration::MilliSeconds(25));
        UNIT_ASSERT_VALUES_EQUAL(config.DefaultScatterOptions.TimeoutTable.
            front(), TDuration::MilliSeconds(30));
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportMethod(), "perl");
        UNIT_ASSERT_VALUES_EQUAL(config.ProtoCollection_.GetSearchPageTemplate().GetReportOptions(), "");
    }

    void TestDefaultHttpStatusManagerConfig()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetSearchProxyConfig();
        TServiceConfig serviceConfig = searchProxyConfig->GetServiceConfig("test");
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().IncompleteStatus, Default<THttpStatusManagerConfig>().IncompleteStatus);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().SyntaxErrorStatus, Default<THttpStatusManagerConfig>().SyntaxErrorStatus);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().EmptySetStatus, Default<THttpStatusManagerConfig>().EmptySetStatus);
    }

    void TestComplexHttpStatusManagerConfig()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        TServiceConfig serviceConfig = searchProxyConfig->GetServiceConfig("test");
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().IncompleteStatus, 502);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().SyntaxErrorStatus, 400);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().EmptySetStatus, 201);
    }

    void TestWizardConfig()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        UNIT_ASSERT_VALUES_EQUAL(searchProxyConfig->GetServiceConfig(
            "test").GetMetaSearchConfig()->WizardConfig->GetWorkDir().GetPath(), "/var/");
        UNIT_ASSERT_VALUES_EQUAL(searchProxyConfig->GetServiceConfig(
            "test2").GetMetaSearchConfig()->WizardConfig->GetWorkDir().GetPath(), "/lib/");
    }

    void TestGlobalHttpStatuses()
    {
        TString searchMapString(GetSearchMapRule("test", 17777));
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        TConfigPatcher cp;
        const TSearchProxyConfig& searchProxyConfig =
            TSearchProxyConfig(
                "<SearchProxy>\n"
                "\tHost: ya.ru\n"
                "\tPort: 9000\n"
                "\t<SearchConfig>\n"
                "\t\t<HttpStatuses>\n"
                "\t\t\tIncompleteStatus: 502\n"
                "\t\t\tSyntaxErrorStatus: 400\n"
                "\t\t</HttpStatuses>\n"
                "\t</SearchConfig>\n"
                "</SearchProxy>\n",
            TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
        TServiceConfig serviceConfig = searchProxyConfig.GetServiceConfig("");
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().IncompleteStatus, 502);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().SyntaxErrorStatus, 400);
        UNIT_ASSERT_VALUES_EQUAL(
            serviceConfig.GetHttpStatusManagerConfig().EmptySetStatus, 404);
    }

    void TestGlobalAllowDynamicWeights()
    {
        TString searchMapString(GetSearchMapRule("test", 17777));
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        TConfigPatcher cp;
        const TSearchProxyConfig& searchProxyConfig =
            TSearchProxyConfig(
                "<SearchProxy>\n"
                "\tHost: ya.ru\n"
                "\tPort: 9000\n"
                "\t<SearchConfig>\n"
                "\t\tAllowDynamicWeights: true\n"
                "\t</SearchConfig>\n"
                "</SearchProxy>\n",
            TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
        TServiceConfig serviceConfig = searchProxyConfig.GetServiceConfig("");
        TSourcesConfig searchConfig = serviceConfig.GetSourcesConfig(/*metaservice=*/false);
        UNIT_ASSERT(searchConfig.CommonSourceOptions.AllowDynamicWeights);
    }

    void TestEnableMetaPRSOps()
    {
        TString searchMapString = GetSearchMap({
            GetSearchMapLine("test", 17777), GetSearchMapLine("test2", 18777)
        });
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        TConfigPatcher cp;
        const TSearchProxyConfig& searchProxyConfig =
            TSearchProxyConfig(
            "<SearchProxy>\n"
            "\tHost: ya.ru\n"
            "\tPort: 17000\n"
            "\tThreads: 5\n"
            "\tMaxQueueSize: 10\n"
            "\tMaxConnections: 15\n"
            "\t<Service>\n"
            "\t\tName: test\n"
            "\t\tEnableMetaPRSOps: true\n"
            "\t</Service>\n"
            "\t<Service>\n"
            "\t\tName: test2\n"
            "\t</Service>\n"
            "\t<Logger>\n"
            "\t\tInfoLog: info.log\n"
            "\t\tErrorLog: error.log\n"
            "\t</Logger>\n"
            "</SearchProxy>", TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);

        const TSearchServerConfig& config1 =
            *searchProxyConfig.GetServiceConfig("test").GetMetaSearchConfig();
        UNIT_ASSERT(config1.EnableMetaPrsOps);

        const TSearchServerConfig& config2 =
            *searchProxyConfig.GetServiceConfig("test2").GetMetaSearchConfig();
        UNIT_ASSERT(!config2.EnableMetaPrsOps);
    }


    TServiceConfig::TExtraCgi::TParameters FindExtraCgiParams(const TServiceConfig::TExtraCgi& extraCgi,
        const TString& name, const TServiceConfig::TExtraCgi::EPolicy& policy)
    {
        TServiceConfig::TExtraCgi::TParameters res;
        std::copy_if(extraCgi.Get().begin(), extraCgi.Get().end(), std::back_inserter(res),
            [&name, policy](const TServiceConfig::TExtraCgi::TParameter& p) {
                return p.Name == name && p.Policy == policy;
            }
        );
        return res;
    }

    void TestCgiSectionsOverride()
    {
        TString searchMapString = GetSearchMap({
            GetSearchMapLine("test", 17777), GetSearchMapLine("test2", 18777)
        });
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        TConfigPatcher cp;
        const TSearchProxyConfig& searchProxyConfig =
            TSearchProxyConfig(
            "<SearchProxy>\n"
            "\tHost: ya.ru\n"
            "\tPort: 17000\n"
            "\tThreads: 5\n"
            "\tMaxQueueSize: 10\n"
            "\tMaxConnections: 15\n"
            "\t<SearchConfig>\n"
            "\t\t<SoftCgiParams>\n"
            "\t\t\trwr: on:PlainNumbers\n"
            "\t\t\twizextra: usextsyntax=da\n"
            "\t\t</SoftCgiParams>\n"
            "\t</SearchConfig>\n"
            "\t<Service>\n"
            "\t\tName: test\n"
            "\t\t<CgiParams>\n"
            "\t\t\twizextra: usesoftness=da\n"
            "\t\t</CgiParams>\n"
            "\t</Service>\n"
            "\t<Service>\n"
            "\t\tName: test2\n"
            "\t\t<SoftCgiParams>\n"
            "\t\t\twizextra: usesoftness=da\n"
            "\t\t</SoftCgiParams>\n"
            "\t</Service>\n"
            "\t<Logger>\n"
            "\t\tInfoLog: info.log\n"
            "\t\tErrorLog: error.log\n"
            "\t</Logger>\n"
            "</SearchProxy>", TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
        {
            const TServiceConfig& config1 = searchProxyConfig.GetServiceConfig("test");

            const TServiceConfig::TExtraCgi& extraCgi = config1.GetExtraCgi();

            TServiceConfig::TExtraCgi::TParameters rwrParams = FindExtraCgiParams(extraCgi,
                "rwr", TServiceConfig::TExtraCgi::EPolicy::AddIfEmpty);
            UNIT_ASSERT_EQUAL(rwrParams.size(), 1);
            UNIT_ASSERT_EQUAL(rwrParams[0].Values.size(), 1);
            UNIT_ASSERT_EQUAL(rwrParams[0].Values[0], "on:PlainNumbers");

            TServiceConfig::TExtraCgi::TParameters wizextraParams = FindExtraCgiParams(extraCgi,
                "wizextra", TServiceConfig::TExtraCgi::EPolicy::AddIfEmpty);
            UNIT_ASSERT_EQUAL(wizextraParams.size(), 1);
            UNIT_ASSERT_EQUAL(wizextraParams[0].Values.size(), 1);
            UNIT_ASSERT_EQUAL(wizextraParams[0].Values[0], "usextsyntax=da");

            TServiceConfig::TExtraCgi::TParameters wizextraParams2 = FindExtraCgiParams(extraCgi,
                "wizextra", TServiceConfig::TExtraCgi::EPolicy::Replace);
            UNIT_ASSERT_EQUAL(wizextraParams2.size(), 1);
            UNIT_ASSERT_EQUAL(wizextraParams2[0].Values.size(), 1);
            UNIT_ASSERT_EQUAL(wizextraParams2[0].Values[0], "usesoftness=da");
        }

        {
            const TServiceConfig& config2 = searchProxyConfig.GetServiceConfig("test2");

            const TServiceConfig::TExtraCgi& extraCgi = config2.GetExtraCgi();

            TServiceConfig::TExtraCgi::TParameters rwrParams = FindExtraCgiParams(extraCgi,
                "rwr", TServiceConfig::TExtraCgi::EPolicy::AddIfEmpty);
            UNIT_ASSERT_EQUAL(rwrParams.size(), 0);

            TServiceConfig::TExtraCgi::TParameters wizextraParams = FindExtraCgiParams(extraCgi,
                "wizextra", TServiceConfig::TExtraCgi::EPolicy::AddIfEmpty);
            UNIT_ASSERT_EQUAL(wizextraParams.size(), 1);
            UNIT_ASSERT_EQUAL(wizextraParams[0].Values.size(), 1);
            UNIT_ASSERT_EQUAL(wizextraParams[0].Values[0], "usesoftness=da");

            TServiceConfig::TExtraCgi::TParameters wizextraParams2 = FindExtraCgiParams(extraCgi,
                "wizextra", TServiceConfig::TExtraCgi::EPolicy::Replace);
            UNIT_ASSERT_EQUAL(wizextraParams2.size(), 0);
        }
    }

    void TestGlobalMisspell()
    {
        TString searchMapString(GetSearchMapRule("test", 17777));
        TStringInput stringInput(searchMapString);
        NSearchMapParser::TJsonSearchMapParser parser(stringInput);
        const NSearchMapParser::TSearchMap& searchMap = parser.GetSearchMap();
        TConfigPatcher cp;
        const TSearchProxyConfig& searchProxyConfig =
            TSearchProxyConfig(
                "<SearchProxy>\n"
                "\tHost: ya.ru\n"
                "\tPort: 9000\n"
                "\t<SearchConfig>\n"
                "\t\t<Misspell>\n"
                "\t\t\tHost: misc-spell.yandex.net\n"
                "\t\t\tPort: 19031\n"
                "\t\t</Misspell>\n"
                "\t</SearchConfig>\n"
                "</SearchProxy>\n",
            TDaemonConfig(GetDaemonConfig(), true), searchMap, cp);
        const TSearchServerConfig& config =
            *searchProxyConfig.GetServiceConfig("").GetMetaSearchConfig();
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.Host,
            "misc-spell.yandex.net");
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.Port, 19031);
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.ConnectionTimeout,
            TDuration::MilliSeconds(30));
        UNIT_ASSERT_VALUES_EQUAL(
            config.MisspellCorrectorConfig->Connection.InteractionTimeout,
            TDuration::MilliSeconds(150));
        UNIT_ASSERT_VALUES_EQUAL(config.MisspellCorrectorConfig->EnablePorno,
            false);
    }

    void TestServiceExceptions()
    {
        ServiceExceptions("", "No name specified for service");
        ServiceExceptions("Name: abracadabra\n",
                "Service \"abracadabra\" not defined in searchmap");
        ServiceExceptions("Name: test\n</Service>\n<Service>\nName: test\n",
                "Service \"test\" defined twice");
    }

    void TestDaemonConfig()
    {
        TSimpleSharedPtr<TSearchProxyConfig> searchProxyConfig = GetComplexSearchProxyConfig();
        const TString value = searchProxyConfig->GetDaemonConfig().GetPidFileName();
        const TString etalon =
            TDaemonConfig(GetDaemonConfig(), true).GetPidFileName();
        UNIT_ASSERT_VALUES_EQUAL(value, etalon);
    }
};

UNIT_TEST_SUITE_REGISTRATION(TSearchProxyConfigTest)

