geod24.LocalRest

Provides utilities to mock a network in unittests

This module is based on the idea that D interfaces can be used to represent a server's API, and that D class inheriting this interface are used to define the server's business code, abstracting away the communication layer.

For example, a server that exposes an API to concatenate two strings would define the following code:

interface API { public string concat (string a, string b); }
class Server : API
{
    public override string concat (string a, string b)
    {
        return a ~ b;
    }
}

Then one can use "generators" to define how multiple process communicate together. One such generator, that pioneered this design is vibe.web.rest, which allows to expose such servers as REST APIs.

localrest is another generator, which uses message passing and threads to create a local "network". The motivation was to create a testing library that could be used to model a network at a much cheaper cost than spawning processes (and containers) would be, when doing integration tests.

More...

Members

Aliases

BindChn
alias BindChn = C.Channel!Connection

Channel type that the nodes will listen for new Connections

Classes

ClientException
class ClientException

Thrown when the sent request is faulty (e.g. 4xx HTTP error types)

RemoteAPI
class RemoteAPI(API, alias S = VibeJSONSerializer!())

A reference to an alread-instantiated node

Timer
class Timer

Simple timer

Functions

runTask
void runTask(void delegate() dg)

Provide eventloop-like functionalities

setTimer
Timer setTimer(Duration timeout, void delegate() dg, bool periodic)

Run an asynchronous task after a given time.

sleep
void sleep(Duration timeout)

Provide eventloop-like functionalities

Structs

Listener
struct Listener(API)

A reference to the "listening" connection of a remote thread

Detailed Description

Control Interface

When instantiating a RemoteAPI, one has the ability to call foreign implementations through auto-generated overrides of the interface. In addition to that, as this library is intended for testing, a few extra functionalities are offered under a control interface, accessible under the ctrl namespace in the instance. The control interface allows to make the node unresponsive to one or all methods, for some defined time or until unblocked, as well as trigger shutdowns or restart. See the methods for more details. The withTimeout control method can be used to spawn a scoped copy of the RemoteAPI with a custom configured timeout. The user-provided delegate will be called with this scoped copy that uses the new timeout.

Shutdown: The control interface has a shutdown method that can be used to terminate a node gracefully. When the shutdown request is handled by the node, the event loop will exit and the thread will terminate. While the destructor of the node will be called, it might not usable for some actions, for example because D destructors may not allocate GC memory, or one may want to perform some test-specific operations, such a logging some data in case of failure. Therefore, you may provide a shutdown routine in the call to shutdown. It must accept a single argument of the interface type, and will be called with the implementation object just before the node is destroyed. If this routine throws, LocalRest will log the error in the console and proceed with destroying the stack-allocated node. Note that control requests are asynchronous, hence requests from the node might be processed / send by the node until the request is actually processed. There is also a restart method which accepts the same callback argument.

Event Loop

Server process usually needs to perform some action in an asynchronous way. Additionally, some actions needs to be completed at a semi-regular interval, for example based on a timer. For those use cases, a node should call runTask or sleep, respectively. Note that this has the same name (and purpose) as Vibe.d's core primitives. Users should only ever call Vibe's runTask / sleep with vibe.web.rest, or only call LocalRest's runTask / sleep with RemoteAPI.

Implementation: In order for tests to simulate an asynchronous system accurately, multiple nodes need to be able to run concurrently and asynchronously.

There are two common solutions to this, to use either fibers or threads. Fibers have the advantage of being simpler to implement and predictable. Threads have the advantage of more accurately describing an asynchronous system and thus have the ability to uncover more issues.

When spawning a node, a thread is spawned, a node is instantiated with the provided arguments, and an event loop waits for messages sent to the Tid. Messages consist of the sender's Tid, the mangled name of the function to call (to support overloading) and the arguments, serialized as a JSON string.

Note: While this module's original motivation was to test REST nodes, the only dependency to Vibe.d is actually to it's JSON module, as Vibe.d is the only available JSON module known to the author to provide an interface to deserialize composite types. This dependency is however not enforced by the dub project file, as users can provide their own serializer (see geod24.Serialization). If the default parameter for serialization is never used, one's project need not depend on vibe-d:data.

Meta

Authors

Mathias 'Geod24' Lang

License

MIT (See LICENSE.txt)