mgmt, main: add strategy choice management and deprecate fib/set-strategy

refs: #1311

Change-Id: Icc88f201f3c13a5a25ec1bdadec35f93c76b2b94
diff --git a/daemon/main.cpp b/daemon/main.cpp
index d9867fe..541d8ff 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -11,6 +11,7 @@
 #include "mgmt/fib-manager.hpp"
 #include "mgmt/face-manager.hpp"
 #include "mgmt/local-control-header-manager.hpp"
+#include "mgmt/strategy-choice-manager.hpp"
 #include "face/tcp-factory.hpp"
 
 #ifdef HAVE_UNIX_SOCKETS
@@ -45,6 +46,7 @@
 static FibManager* g_fibManager;
 static FaceManager* g_faceManager;
 static LocalControlHeaderManager* g_localControlHeaderManager;
+static StrategyChoiceManager* g_strategyChoiceManager;
 static TcpFactory* g_tcpFactory;
 static shared_ptr<TcpChannel> g_tcpChannel;
 static shared_ptr<InternalFace> g_internalFace;
@@ -217,6 +219,9 @@
     new LocalControlHeaderManager(bind(&Forwarder::getFace, g_forwarder, _1),
                                   g_internalFace);
 
+  g_strategyChoiceManager = new StrategyChoiceManager(g_forwarder->getStrategyChoice(),
+                                                      g_internalFace);
+
   shared_ptr<fib::Entry> entry = g_forwarder->getFib().insert("/localhost/nfd").first;
   entry->addNextHop(g_internalFace, 0);
 }
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 27707d7..8e27114 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -52,12 +52,6 @@
                      &FibManager::removeNextHop
                      ),
 
-    // Unsupported
-    // VerbAndProcessor(
-    //                  Name::Component("strategy"),
-    //                  &FibManager::strategy
-    //                  )
-
   };
 
 FibManager::FibManager(Fib& fib,
@@ -258,12 +252,6 @@
     }
 }
 
-void
-FibManager::strategy(const FibManagementOptions& options, ControlResponse& response)
-{
-
-}
-
 // void
 // FibManager::onConfig(ConfigFile::Node section, bool isDryRun)
 // {
diff --git a/daemon/mgmt/fib-manager.hpp b/daemon/mgmt/fib-manager.hpp
index 79be094..c7b7793 100644
--- a/daemon/mgmt/fib-manager.hpp
+++ b/daemon/mgmt/fib-manager.hpp
@@ -51,10 +51,6 @@
   removeNextHop(const FibManagementOptions& options,
                 ControlResponse& response);
 
-  void
-  strategy(const FibManagementOptions& options,
-           ControlResponse& response);
-
   bool
   extractOptions(const Interest& request,
                  FibManagementOptions& extractedOptions);
diff --git a/daemon/mgmt/strategy-choice-manager.cpp b/daemon/mgmt/strategy-choice-manager.cpp
new file mode 100644
index 0000000..00761dd
--- /dev/null
+++ b/daemon/mgmt/strategy-choice-manager.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "strategy-choice-manager.hpp"
+#include "table/strategy-choice.hpp"
+#include "mgmt/app-face.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("StrategyChoiceManager");
+
+const Name StrategyChoiceManager::COMMAND_PREFIX = "/localhost/nfd/strategy-choice";
+
+const size_t StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS =
+  StrategyChoiceManager::COMMAND_PREFIX.size() +
+  1 + // verb
+  1;  // verb options
+
+const size_t StrategyChoiceManager::COMMAND_SIGNED_NCOMPS =
+  StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS +
+  0; // No signed Interest support in mock, otherwise 4 (timestamp, nonce, signed info tlv, signature tlv)
+
+StrategyChoiceManager::StrategyChoiceManager(StrategyChoice& strategyChoice,
+                                             shared_ptr<AppFace> face)
+  : ManagerBase(face)
+  , m_strategyChoice(strategyChoice)
+{
+  face->setInterestFilter("/localhost/nfd/strategy-choice",
+                          bind(&StrategyChoiceManager::onStrategyChoiceRequest, this, _2));
+}
+
+StrategyChoiceManager::~StrategyChoiceManager()
+{
+
+}
+
+void
+StrategyChoiceManager::onStrategyChoiceRequest(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+      commandNComps < COMMAND_SIGNED_NCOMPS)
+    {
+      NFD_LOG_INFO("command result: unsigned verb: " << command);
+      sendResponse(command, 401, "Signature required");
+
+      return;
+    }
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+           !COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_INFO("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  onValidatedStrategyChoiceRequest(request.shared_from_this());
+}
+
+void
+StrategyChoiceManager::onValidatedStrategyChoiceRequest(const shared_ptr<const Interest>& request)
+{
+  static const Name::Component VERB_SET("set");
+  static const Name::Component VERB_UNSET("unset");
+
+  const Name& command = request->getName();
+
+  ndn::nfd::FibManagementOptions options;
+  if (!extractOptions(*request, options))
+    {
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
+  ControlResponse response;
+  if (verb == VERB_SET)
+    {
+      setStrategy(options, response);
+    }
+  else if (verb == VERB_UNSET)
+    {
+      unsetStrategy(options, response);
+    }
+  else
+    {
+      NFD_LOG_INFO("command result: unsupported verb: " << verb);
+      setResponse(response, 501, "Unsupported command");
+    }
+  sendResponse(command, response);
+}
+
+bool
+StrategyChoiceManager::extractOptions(const Interest& request,
+                                      ndn::nfd::FibManagementOptions& extractedOptions)
+{
+  const Name& command = request.getName();
+  const size_t optionCompIndex =
+    COMMAND_PREFIX.size() + 1;
+
+  try
+    {
+      Block rawOptions = request.getName()[optionCompIndex].blockFromValue();
+      extractedOptions.wireDecode(rawOptions);
+    }
+  catch (const ndn::Tlv::Error& e)
+    {
+      NFD_LOG_INFO("Bad command option parse: " << command);
+      return false;
+    }
+
+  NFD_LOG_DEBUG("Options parsed OK");
+  return true;
+}
+
+void
+StrategyChoiceManager::setStrategy(const ndn::nfd::FibManagementOptions& options,
+                                   ControlResponse& response)
+{
+  const Name& prefix = options.getName();
+  const Name& selectedStrategy = options.getStrategy();
+
+  if (!m_strategyChoice.hasStrategy(selectedStrategy))
+    {
+      NFD_LOG_INFO("strategy-choice result: FAIL reason: unknown-strategy: "
+                   << options.getStrategy());
+      setResponse(response, 504, "Unsupported strategy");
+      return;
+    }
+
+  if (m_strategyChoice.insert(prefix, selectedStrategy))
+    {
+      setResponse(response, 200, "Success", options.wireEncode());
+    }
+  else
+    {
+      setResponse(response, 405, "Strategy not installed");
+    }
+}
+
+void
+StrategyChoiceManager::unsetStrategy(const ndn::nfd::FibManagementOptions& options,
+                                     ControlResponse& response)
+{
+  static const Name ROOT_PREFIX;
+
+  const Name& prefix = options.getName();
+  if (prefix == ROOT_PREFIX)
+    {
+      NFD_LOG_INFO("strategy-choice result: FAIL reason: unknown-prefix: "
+                   << options.getName());
+      setResponse(response, 403, "Cannot unset root prefix strategy");
+      return;
+    }
+
+  m_strategyChoice.erase(prefix);
+  setResponse(response, 200, "Success", options.wireEncode());
+}
+
+
+
+} // namespace nfd
+
+
diff --git a/daemon/mgmt/strategy-choice-manager.hpp b/daemon/mgmt/strategy-choice-manager.hpp
new file mode 100644
index 0000000..34046ac
--- /dev/null
+++ b/daemon/mgmt/strategy-choice-manager.hpp
@@ -0,0 +1,68 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_MGMT_STRATEGY_CHOICE_MANAGER_HPP
+#define NFD_MGMT_STRATEGY_CHOICE_MANAGER_HPP
+
+#include "mgmt/manager-base.hpp"
+
+#include <ndn-cpp-dev/management/nfd-fib-management-options.hpp>
+
+namespace nfd {
+
+const std::string STRATEGY_CHOICE_PRIVILEGE = "strategy-choice";
+
+class StrategyChoice;
+
+class StrategyChoiceManager : public ManagerBase
+{
+public:
+  StrategyChoiceManager(StrategyChoice& strategyChoice,
+                        shared_ptr<AppFace> face);
+
+  virtual
+  ~StrategyChoiceManager();
+
+  void
+  onStrategyChoiceRequest(const Interest& request);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  void
+  onValidatedStrategyChoiceRequest(const shared_ptr<const Interest>& request);
+
+  bool
+  extractOptions(const Interest& request,
+                   ndn::nfd::FibManagementOptions& extractedOptions);
+
+
+
+  void
+  setStrategy(const ndn::nfd::FibManagementOptions& options,
+              ControlResponse& response);
+
+  void
+  unsetStrategy(const ndn::nfd::FibManagementOptions& options,
+                ControlResponse& response);
+private:
+
+  StrategyChoice& m_strategyChoice;
+
+  static const Name COMMAND_PREFIX; // /localhost/nfd/strategy-choice
+
+  // number of components in an invalid, but not malformed, unsigned command.
+  // (/localhost/nfd/strategy-choice + verb + options) = 5
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed Interest.
+  // (see UNSIGNED_NCOMPS), 9 with signed Interest support.
+  static const size_t COMMAND_SIGNED_NCOMPS;
+
+};
+
+} // namespace nfd
+
+#endif // NFD_MGMT_STRATEGY_CHOICE_MANAGER_HPP
+
diff --git a/tests/mgmt/strategy-choice-manager.cpp b/tests/mgmt/strategy-choice-manager.cpp
new file mode 100644
index 0000000..e2285c2
--- /dev/null
+++ b/tests/mgmt/strategy-choice-manager.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "mgmt/strategy-choice-manager.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "table/name-tree.hpp"
+#include "table/strategy-choice.hpp"
+#include "fw/forwarder.hpp"
+#include "fw/strategy.hpp"
+#include "tests/face/dummy-face.hpp"
+
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("StrategyChoiceManagerTest");
+
+class DummyStrategy : public fw::Strategy
+{
+public:
+  DummyStrategy(Forwarder& forwarder, const Name& strategyName)
+    : fw::Strategy(forwarder, strategyName)
+  {
+
+  }
+
+  virtual
+  ~DummyStrategy()
+  {
+
+  }
+
+  virtual void
+  afterReceiveInterest(const Face& inFace,
+                       const Interest& interest,
+                       shared_ptr<fib::Entry> fibEntry,
+                       shared_ptr<pit::Entry> pitEntry)
+  {
+
+  }
+};
+
+class TestStrategyA : public DummyStrategy
+{
+public:
+  TestStrategyA(Forwarder& forwarder)
+    : DummyStrategy(forwarder, "/localhost/nfd/strategy/test-strategy-a")
+  {
+  }
+
+  virtual
+  ~TestStrategyA()
+  {
+
+  }
+};
+
+class TestStrategyB : public DummyStrategy
+{
+public:
+  TestStrategyB(Forwarder& forwarder)
+    : DummyStrategy(forwarder, "/localhost/nfd/strategy/test-strategy-b")
+  {
+  }
+
+  virtual
+  ~TestStrategyB()
+  {
+
+  }
+};
+
+class StrategyChoiceManagerFixture : protected BaseFixture
+{
+public:
+
+  StrategyChoiceManagerFixture()
+    : m_nameTree(1024)
+    , m_strategyChoice(m_nameTree, make_shared<TestStrategyA>(boost::ref(m_forwarder)))
+    , m_face(make_shared<InternalFace>())
+    , m_manager(m_strategyChoice, m_face)
+    , m_callbackFired(false)
+  {
+
+  }
+
+  void
+  validateControlResponseCommon(const Data& response,
+                                const Name& expectedName,
+                                uint32_t expectedCode,
+                                const std::string& expectedText,
+                                ControlResponse& control)
+  {
+    m_callbackFired = true;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    control.wireDecode(controlRaw);
+
+    NFD_LOG_DEBUG("received control response"
+                  << " Name: " << response.getName()
+                  << " code: " << control.getCode()
+                  << " text: " << control.getText());
+
+    BOOST_CHECK_EQUAL(response.getName(), expectedName);
+    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+    BOOST_CHECK_EQUAL(control.getText(), expectedText);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    if (!control.getBody().empty())
+      {
+        BOOST_FAIL("found unexpected control response body");
+      }
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText,
+                          const Block& expectedBody)
+  {
+    ControlResponse control;
+    validateControlResponseCommon(response, expectedName,
+                                  expectedCode, expectedText, control);
+
+    BOOST_REQUIRE(!control.getBody().empty());
+    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+                       expectedBody.value_size()) == 0);
+
+  }
+
+  bool
+  didCallbackFire()
+  {
+    return m_callbackFired;
+  }
+
+  void
+  resetCallbackFired()
+  {
+    m_callbackFired = false;
+  }
+
+  shared_ptr<InternalFace>&
+  getFace()
+  {
+    return m_face;
+  }
+
+  StrategyChoiceManager&
+  getManager()
+  {
+    return m_manager;
+  }
+
+  StrategyChoice&
+  getStrategyChoice()
+  {
+    return m_strategyChoice;
+  }
+
+protected:
+  Forwarder m_forwarder;
+  NameTree m_nameTree;
+  StrategyChoice m_strategyChoice;
+  shared_ptr<InternalFace> m_face;
+  StrategyChoiceManager m_manager;
+
+private:
+  bool m_callbackFired;
+};
+
+class AllStrategiesFixture : public StrategyChoiceManagerFixture
+{
+public:
+  AllStrategiesFixture()
+  {
+    m_strategyChoice.install(make_shared<TestStrategyB>(boost::ref(m_forwarder)));
+  }
+
+  virtual
+  ~AllStrategiesFixture()
+  {
+
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager, AllStrategiesFixture)
+
+BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+{
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this,  _1,
+         command->getName(), 400, "Malformed command");
+
+  getFace()->sendInterest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 400, "Malformed command");
+
+  getManager().onStrategyChoiceRequest(*command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unsupported");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 501, "Unsupported command");
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(BadOptionParse)
+{
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append("NotReallyOptions");
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 400, "Malformed command");
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategies)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setName("/test");
+  options.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 200, "Success", encodedOptions);
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
+
+  resetCallbackFired();
+  getFace()->onReceiveData.clear();
+}
+
+BOOST_AUTO_TEST_CASE(SetUnsupportedStrategy)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setName("/test");
+  options.setStrategy("/localhost/nfd/strategy/unit-test-doesnotexist");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("set");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 504, "Unsupported strategy");
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+  BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+}
+
+class DefaultStrategyOnlyFixture : public StrategyChoiceManagerFixture
+{
+public:
+  DefaultStrategyOnlyFixture()
+    : StrategyChoiceManagerFixture()
+  {
+
+  }
+
+  virtual
+  ~DefaultStrategyOnlyFixture()
+  {
+
+  }
+};
+
+
+/// \todo I'm not sure this code branch (code 405) can happen. The manager tests for the strategy first and will return 504.
+// BOOST_FIXTURE_TEST_CASE(SetNotInstalled, DefaultStrategyOnlyFixture)
+// {
+//   BOOST_REQUIRE(!getStrategyChoice().hasStrategy("/localhost/nfd/strategy/test-strategy-b"));
+//   ndn::nfd::FibManagementOptions options;
+//   options.setName("/test");
+//   options.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+//   Block encodedOptions(options.wireEncode());
+
+//   Name commandName("/localhost/nfd/strategy-choice");
+//   commandName.append("set");
+//   commandName.append(encodedOptions);
+
+//   shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+//   getFace()->onReceiveData +=
+//     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+//          command->getName(), 405, "Strategy not installed");
+
+//   getManager().onValidatedStrategyChoiceRequest(command);
+
+//   BOOST_REQUIRE(didCallbackFire());
+//   fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+//   BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+// }
+
+BOOST_AUTO_TEST_CASE(Unset)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setName("/test");
+
+  BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
+  BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                      "/localhost/nfd/strategy/test-strategy-b");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unset");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 200, "Success", encodedOptions);
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                    "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(UnsetRoot)
+{
+  ndn::nfd::FibManagementOptions options;
+  options.setName("/");
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName("/localhost/nfd/strategy-choice");
+  commandName.append("unset");
+  commandName.append(encodedOptions);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+  getFace()->onReceiveData +=
+    bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+         command->getName(), 403, "Cannot unset root prefix strategy");
+
+  getManager().onValidatedStrategyChoiceRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+
+  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+                    "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd