Files
SingularityViewer/indra/test/llhttpnode_tut.cpp
2010-04-02 02:48:44 -03:00

434 lines
12 KiB
C++

/**
* @file lliohttpnode_tut.cpp
* @date May 2006
* @brief HTTP server unit tests
*
* $LicenseInfo:firstyear=2006&license=viewergpl$
*
* Copyright (c) 2006-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "lltut.h"
#include "llhttpnode.h"
#include "llsdhttpserver.h"
namespace tut
{
struct HTTPNodeTestData
{
LLHTTPNode mRoot;
LLSD mContext;
const LLSD& context() { return mContext; }
std::string remainderPath()
{
std::ostringstream pathOutput;
bool addSlash = false;
LLSD& remainder = mContext["request"]["remainder"];
for (LLSD::array_const_iterator i = remainder.beginArray();
i != remainder.endArray();
++i)
{
if (addSlash) { pathOutput << '/'; }
pathOutput << i->asString();
addSlash = true;
}
return pathOutput.str();
}
void ensureRootTraversal(const std::string& path,
const LLHTTPNode* expectedNode,
const char* expectedRemainder)
{
mContext.clear();
const LLHTTPNode* actualNode = mRoot.traverse(path, mContext);
ensure_equals("traverse " + path + " node",
actualNode, expectedNode);
ensure_equals("traverse " + path + " remainder",
remainderPath(), expectedRemainder);
}
class Response : public LLHTTPNode::Response
{
public:
static LLPointer<Response> create() {return new Response();}
LLSD mResult;
void result(const LLSD& result) { mResult = result; }
void status(S32 code, const std::string& message) { }
void extendedResult(S32 code, const std::string& message, const LLSD& headers) { }
private:
Response() {;} // Must be accessed through LLPointer.
};
typedef LLPointer<Response> ResponsePtr;
LLSD get(const std::string& path)
{
mContext.clear();
const LLHTTPNode* node = mRoot.traverse(path, mContext);
ensure(path + " found", node != NULL);
ResponsePtr response = Response::create();
node->get(LLHTTPNode::ResponsePtr(response), mContext);
return response->mResult;
}
LLSD post(const std::string& path, const LLSD& input)
{
mContext.clear();
const LLHTTPNode* node = mRoot.traverse(path, mContext);
ensure(path + " found", node != NULL);
ResponsePtr response = Response::create();
node->post(LLHTTPNode::ResponsePtr(response), mContext, input);
return response->mResult;
}
void ensureMemberString(const std::string& name,
const LLSD& actualMap, const std::string& member,
const std::string& expectedValue)
{
ensure_equals(name + " " + member,
actualMap[member].asString(), expectedValue);
}
void ensureInArray(const LLSD& actualArray,
const std::string& expectedValue)
{
LLSD::array_const_iterator i = actualArray.beginArray();
LLSD::array_const_iterator end = actualArray.endArray();
for (; i != end; ++i)
{
std::string path = i->asString();
if (path == expectedValue)
{
return;
}
}
fail("didn't find " + expectedValue);
}
};
typedef test_group<HTTPNodeTestData> HTTPNodeTestGroup;
typedef HTTPNodeTestGroup::object HTTPNodeTestObject;
HTTPNodeTestGroup httpNodeTestGroup("http node");
template<> template<>
void HTTPNodeTestObject::test<1>()
{
// traversal of the lone node
ensureRootTraversal("", &mRoot, "");
ensureRootTraversal("/", &mRoot, "");
ensureRootTraversal("foo", NULL, "foo");
ensureRootTraversal("foo/bar", NULL, "foo/bar");
ensure_equals("root of root", mRoot.rootNode(), &mRoot);
}
template<> template<>
void HTTPNodeTestObject::test<2>()
{
// simple traversal of a single node
LLHTTPNode* helloNode = new LLHTTPNode;
mRoot.addNode("hello", helloNode);
ensureRootTraversal("hello", helloNode, "");
ensureRootTraversal("/hello", helloNode, "");
ensureRootTraversal("hello/", helloNode, "");
ensureRootTraversal("/hello/", helloNode, "");
ensureRootTraversal("hello/there", NULL, "there");
ensure_equals("root of hello", helloNode->rootNode(), &mRoot);
}
template<> template<>
void HTTPNodeTestObject::test<3>()
{
// traversal of mutli-branched tree
LLHTTPNode* greekNode = new LLHTTPNode;
LLHTTPNode* alphaNode = new LLHTTPNode;
LLHTTPNode* betaNode = new LLHTTPNode;
LLHTTPNode* gammaNode = new LLHTTPNode;
greekNode->addNode("alpha", alphaNode);
greekNode->addNode("beta", betaNode);
greekNode->addNode("gamma", gammaNode);
mRoot.addNode("greek", greekNode);
LLHTTPNode* hebrewNode = new LLHTTPNode;
LLHTTPNode* alephNode = new LLHTTPNode;
hebrewNode->addNode("aleph", alephNode);
mRoot.addNode("hebrew", hebrewNode);
ensureRootTraversal("greek/alpha", alphaNode, "");
ensureRootTraversal("greek/beta", betaNode, "");
ensureRootTraversal("greek/delta", NULL, "delta");
ensureRootTraversal("greek/gamma", gammaNode, "");
ensureRootTraversal("hebrew/aleph", alephNode, "");
ensure_equals("root of greek", greekNode->rootNode(), &mRoot);
ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
ensure_equals("root of hebrew", hebrewNode->rootNode(), &mRoot);
ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
}
template<> template<>
void HTTPNodeTestObject::test<4>()
{
// automatic creation of parent nodes and not overriding existing nodes
LLHTTPNode* alphaNode = new LLHTTPNode;
LLHTTPNode* betaNode = new LLHTTPNode;
LLHTTPNode* gammaNode = new LLHTTPNode;
LLHTTPNode* gamma2Node = new LLHTTPNode;
mRoot.addNode("greek/alpha", alphaNode);
mRoot.addNode("greek/beta", betaNode);
mRoot.addNode("greek/gamma", gammaNode);
mRoot.addNode("greek/gamma", gamma2Node);
LLHTTPNode* alephNode = new LLHTTPNode;
mRoot.addNode("hebrew/aleph", alephNode);
ensureRootTraversal("greek/alpha", alphaNode, "");
ensureRootTraversal("greek/beta", betaNode, "");
ensureRootTraversal("greek/delta", NULL, "delta");
ensureRootTraversal("greek/gamma", gammaNode, "");
ensureRootTraversal("hebrew/aleph", alephNode, "");
ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
}
class IntegerNode : public LLHTTPNode
{
public:
virtual void get(ResponsePtr response, const LLSD& context) const
{
int n = context["extra"]["value"];
LLSD info;
info["value"] = n;
info["positive"] = n > 0;
info["zero"] = n == 0;
info["negative"] = n < 0;
response->result(info);
}
virtual bool validate(const std::string& name, LLSD& context) const
{
int n;
std::istringstream i_stream(name);
i_stream >> n;
if (i_stream.fail() || i_stream.get() != EOF)
{
return false;
}
context["extra"]["value"] = n;
return true;
}
};
class SquareNode : public LLHTTPNode
{
public:
virtual void get(ResponsePtr response, const LLSD& context) const
{
int n = context["extra"]["value"];
response->result(n*n);
}
};
template<> template<>
void HTTPNodeTestObject::test<5>()
{
// wildcard nodes
LLHTTPNode* miscNode = new LLHTTPNode;
LLHTTPNode* iNode = new IntegerNode;
LLHTTPNode* sqNode = new SquareNode;
mRoot.addNode("test/misc", miscNode);
mRoot.addNode("test/<int>", iNode);
mRoot.addNode("test/<int>/square", sqNode);
ensureRootTraversal("test/42", iNode, "");
ensure_equals("stored integer",
context()["extra"]["value"].asInteger(), 42);
ensureRootTraversal("test/bob", NULL, "bob");
ensure("nothing stored",
context()["extra"]["value"].isUndefined());
ensureRootTraversal("test/3/square", sqNode, "");
ResponsePtr response = Response::create();
sqNode->get(LLHTTPNode::ResponsePtr(response), context());
ensure_equals("square result", response->mResult.asInteger(), 9);
}
class AlphaNode : public LLHTTPNode
{
public:
virtual bool handles(const LLSD& remainder, LLSD& context) const
{
LLSD::array_const_iterator i = remainder.beginArray();
LLSD::array_const_iterator end = remainder.endArray();
for (; i != end; ++i)
{
std::string s = i->asString();
if (s.empty() || s[0] != 'a')
{
return false;
}
}
return true;
}
};
template<> template<>
void HTTPNodeTestObject::test<6>()
{
// nodes that handle remainders
LLHTTPNode* miscNode = new LLHTTPNode;
LLHTTPNode* aNode = new AlphaNode;
LLHTTPNode* zNode = new LLHTTPNode;
mRoot.addNode("test/misc", miscNode);
mRoot.addNode("test/alpha", aNode);
mRoot.addNode("test/alpha/zebra", zNode);
ensureRootTraversal("test/alpha", aNode, "");
ensureRootTraversal("test/alpha/abe", aNode, "abe");
ensureRootTraversal("test/alpha/abe/amy", aNode, "abe/amy");
ensureRootTraversal("test/alpha/abe/bea", NULL, "abe/bea");
ensureRootTraversal("test/alpha/bob", NULL, "bob");
ensureRootTraversal("test/alpha/zebra", zNode, "");
}
template<> template<>
void HTTPNodeTestObject::test<7>()
{
// test auto registration
LLHTTPStandardServices::useServices();
LLHTTPRegistrar::buildAllServices(mRoot);
{
LLSD result = get("web/hello");
ensure_equals("hello result", result.asString(), "hello");
}
{
LLSD stuff = 3.14159;
LLSD result = post("web/echo", stuff);
ensure_equals("echo result", result, stuff);
}
}
template<> template<>
void HTTPNodeTestObject::test<8>()
{
// test introspection
LLHTTPRegistrar::buildAllServices(mRoot);
mRoot.addNode("test/misc", new LLHTTPNode);
mRoot.addNode("test/<int>", new IntegerNode);
mRoot.addNode("test/<int>/square", new SquareNode);
const LLSD result = get("web/server/api");
ensure("result is array", result.isArray());
ensure("result size", result.size() >= 2);
ensureInArray(result, "web/echo");
ensureInArray(result, "web/hello");
ensureInArray(result, "test/misc");
ensureInArray(result, "test/<int>");
ensureInArray(result, "test/<int>/square");
}
template<> template<>
void HTTPNodeTestObject::test<9>()
{
// test introspection details
LLHTTPRegistrar::buildAllServices(mRoot);
const LLSD helloDetails = get("web/server/api/web/hello");
ensure_contains("hello description",
helloDetails["description"].asString(), "hello");
ensure_equals("method name", helloDetails["api"][0].asString(), std::string("GET"));
ensureMemberString("hello", helloDetails, "output", "\"hello\"");
ensure_contains("hello __file__",
helloDetails["__file__"].asString(), "llsdhttpserver.cpp");
ensure("hello line", helloDetails["__line__"].isInteger());
const LLSD echoDetails = get("web/server/api/web/echo");
ensure_contains("echo description",
echoDetails["description"].asString(), "echo");
ensure_equals("method name", echoDetails["api"][0].asString(), std::string("POST"));
ensureMemberString("echo", echoDetails, "input", "<any>");
ensureMemberString("echo", echoDetails, "output", "<the input>");
ensure_contains("echo __file__",
echoDetails["__file__"].asString(), "llsdhttpserver.cpp");
ensure("echo", echoDetails["__line__"].isInteger());
}
}