Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mrak/stubby4net

A small configurable server for stubbing external systems during development.
https://github.com/mrak/stubby4net

Last synced: 3 months ago
JSON representation

A small configurable server for stubbing external systems during development.

Awesome Lists containing this project

README

        

stubby4net
===========

## Table of Contents

* [Installation](#installation)
* [Requirements](#requirements)
* [Starting the Server(s)](#starting-the-servers)
* [Command-line Switches](#command-line-switches)
* [Endpoint Configuration](#endpoint-configuration)
* [The Admin Portal](#the-admin-portal)
* [The Stubs Portal](#the-stubs-portal)
* [Programmatic API](#programmatic-api)
* [Running Tests](#running-tests)
* [See Also](#see-also)
* [TODO](#todo)
* [Wishful Thinkings](#wishful-thinkings)
* [NOTES](#notes)

## Installation

### As a command-line tool via [Chocolatey](http://chocolatey.org/)

cinst stubby

This will install `stubby` as a command in your `PATH`.

### As a project dependency via [NuGet](http://nuget.org/)

PM> Install-Package stubby

## Requirements

* .NET Framework 4+

Testing with Mono has not been explicitly performed but adherence to Mono-compatible APIs was attempted.

### Packaged

* [YamlDotNet](https://github.com/aaubry/YamlDotNet) for loading yaml files.
* [CommandLineParser](https://github.com/gsscoder/commandline) for handling cli arguments.

### Optional (for development)

* [NUnit](http://nunit.org)

## Starting the Server(s)

Some systems require you to `sudo` before running services on port certain ports (like 80)

[sudo] stubby

## Command-line Switches

```
stubby [-a ] [-s ] [-t ]
[-l ] [-d ] [-v] [-w] [-m]

-a, --admin Port for admin portal. Defaults to 8889.
-s, --stubs Port for stubs portal. Defaults to 8882.
-t, --tls Port for https stubs portal. Defaults to 7443.
-l, --location Hostname at which to bind stubby.
-d, --data Data file to pre-load endoints. YAML or JSON format.
-w, --watch Auto-reload data file when edits are made.
-m, --mute Prevent stubby from printing to the console.
-v, --version Prints stubby's version number.
--help This help text.
```

## Endpoint Configuration

This section explains the usage, intent and behavior of each property on the `request` and `response` objects.

Here is a fully-populated, unrealistic endpoint:
```yaml
- request:
url: ^/your/awesome/endpoint$
method: POST
query:
exclamation: post requests can have query strings!
headers:
content-type: application/xml
post: >




file: tryMyFirst.xml
response:
status: 200
latency: 5000
headers:
content-type: application/xml
server: stubbedServer/4.2
body: >




file: responseData.xml
```

### request

This object is used to match an incoming request to stubby against the available endpoints that have been configured.

#### url (required)

* is a full-fledged __regular expression__
* This is the only required property of an endpoint.
* signify the url after the base host and port (i.e. after `localhost:8882`).
* must begin with ` / `.
* any query paramters are stripped (so don't include them, that's what `query` is for).
* `/url?some=value&another=value` becomes `/url`
* no checking is done for URI-encoding compliance.
* If it's invalid, it won't ever trigger a match.

This is the simplest you can get:
```yaml
- request:
url: /
```

A demonstration using regular expressions:
```yaml
- request:
url: ^/has/to/begin/with/this/

- request:
url: /has/to/end/with/this/$

- request:
url: ^/must/be/this/exactly/with/optional/trailing/slash/?$
```

#### method

* defaults to `GET`.
* case-insensitive.
* can be any of the following:
* HEAD
* GET
* POST
* PUT
* POST
* DELETE
* etc.

```yaml
- request:
url: /anything
method: GET
```

* it can also be an array of values.

```yaml
- request:
url: /anything
method: [GET, HEAD]

- request:
url: ^/yonder
method:
- GET
- HEAD
- POST
```

#### query

* if ommitted, stubby ignores query parameters for the given url.
* a yaml hashmap of variable/value pairs.
* allows the query parameters to appear in any order in a uri

* The following will match either of these:
* `/with/parameters?search=search+terms&filter=month`
* `/with/parameters?filter=month&search=search+terms`

```yaml
- request:
url: ^/with/parameters$
query:
search: search terms
filter: month
```

#### post

* if ommitted, any post data is ignored.
* the body contents of the server request, such as form data.

```yaml
- request:
url: ^/post/form/data$
post: name=John&[email protected]
```

#### file

* if supplied, replaces `post` with the contents of the locally given file.
* paths are relative from where stubby was executed.
* if the file is not found when the request is made, falls back to `post` for matching.
* allows you to split up stubby data across multiple files

```yaml
- request:
url: ^/match/against/file$
file: postedData.json
post: '{"fallback":"data"}'
```

postedData.json
```json
{"fileContents":"match against this if the file is here"}
```

* if `postedData.json` doesn't exist on the filesystem when `/match/against/file` is requested, stubby will match post contents against `{"fallback":"data"}` (from `post`) instead.

#### headers

* if ommitted, stubby ignores headers for the given url.
* case-insensitive matching of header names.
* a hashmap of header/value pairs similar to `query`.

The following endpoint only accepts requests with `application/json` post values:

```yaml
- request:
url: /post/json
method: post
headers:
content-type: application/json
```

### response

Assuming a match has been made against the given `request` object, data from `response` is used to build the stubbed response back to the client.

#### status

* the HTTP status code of the response.
* integer or integer-like string.
* defaults to `200`.

```yaml
- request:
url: ^/im/a/teapot$
method: POST
response:
status: 420
```

#### body

* contents of the response body
* defaults to an empty content body

```yaml
- request:
url: ^/give/me/a/smile$
response:
body: ':)'
```

#### file

* similar to `request.file`, but the contents of the file are used as the `body`.

```yaml
- request:
url: /
response:
file: extremelyLongJsonFile.json
```

#### headers

* similar to `request.headers` except that these are sent back to the client.

```yaml
- request:
url: ^/give/me/some/json$
response:
headers:
content-type: application/json
body: >
[{
"name":"John",
"email":"[email protected]"
},{
"name":"Jane",
"email":"[email protected]"
}]
```

#### latency

* time to wait, in milliseconds, before sending back the response
* good for testing timeouts, or slow connections

```yaml
- request:
url: ^/hello/to/jupiter$
response:
latency: 800000
body: Hello, World!
```

## The Admin Portal

The admin portal is a RESTful(ish) endpoint running on `localhost:8889`. Or wherever you described through stubby's options.

### Supplying Endpoints to Stubby

Submit `POST` requests to `localhost:8889` or load a data-file (-d) with the following structure for each endpoint:

* `request`: describes the client's call to the server
* `method`: GET/POST/PUT/DELETE/etc.
* `url`: the URI regex string. GET parameters should also be included inline here
* `query`: a key/value map of query string parameters included with the request
* `headers`: a key/value map of headers the server should respond to
* `post`: a string matching the textual body of the response.
* `file`: if specified, returns the contents of the given file as the request post. If the file cannot be found at request time, **post** is used instead
* `response`: describes the server's response to the client
* `headers`: a key/value map of headers the server should use in it's response
* `latency`: the time in milliseconds the server should wait before responding. Useful for testing timeouts and latency
* `file`: if specified, returns the contents of the given file as the response body. If the file cannot be found at request time, **body** is used instead
* `body`: the textual body of the server's response to the client
* `status`: the numerical HTTP status code (200 for OK, 404 for NOT FOUND, etc.)

#### YAML (file only)
```yaml
- request:
url: ^/path/to/something$
method: POST
headers:
authorization: "Basic usernamez:passwordinBase64"
post: this is some post data in textual format
response:
headers:
Content-Type: application/json
latency: 1000
status: 200
body: You're request was successfully processed!

- request:
url: ^/path/to/anotherThing
query:
a: anything
b: more
method: GET
headers:
Content-Type: application/json
post:
response:
headers:
Content-Type: application/json
Access-Control-Allow-Origin: "*"
status: 204
file: path/to/page.html

- request:
url: ^/path/to/thing$
method: POST
headers:
Content-Type: application/json
post: this is some post data in textual format
response:
headers:
Content-Type: application/json
status: 304
```

#### JSON (file or POST/PUT)
```json
[
{
"request": {
"url": "^/path/to/something$",
"post": "this is some post data in textual format",
"headers": {
"authorization": "Basic usernamez:passwordinBase64"
},
"method": "POST"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"latency": 1000,
"body": "You're request was successfully processed!"
}
},
{
"request": {
"url": "^/path/to/anotherThing",
"query": {
"a": "anything",
"b": "more"
},
"headers": {
"Content-Type": "application/json"
},
"post": null,
"method": "GET"
},
"response": {
"status": 204,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
"file": "path/to/page.html"
}
},
{
"request": {
"url": "^/path/to/thing$",
"headers": {
"Content-Type": "application/json"
},
"post": "this is some post data in textual format",
"method": "POST"
},
"response": {
"status": 304,
"headers": {
"Content-Type": "application/json"
}
}
}
]
```

If you want to load more than one endpoint via file, use either a JSON array or YAML list (-) syntax. On success, the response will contain `Location` in the header with the newly created resources' location

### Getting the Current List of Stubbed Endpoints

Performing a `GET` request on `localhost:8889` will return a JSON array of all currently saved responses. It will reply with `204 : No Content` if there are none saved.

Performing a `GET` request on `localhost:8889/` will return the JSON object representing the response with the supplied id.

#### The Status Page

You can also view the currently configured endpoints by going to `localhost:8889/status`

### Changing Existing Endpoints

Perform `PUT` requests in the same format as using `POST`, only this time supply the id in the path. For instance, to update the response with id 4 you would `PUT` to `localhost:8889/4`.

### Deleting Endpoints

Send a `DELETE` request to `localhost:8889/`

## The Stubs Portal

Requests sent to any url at `localhost:8882` (or wherever you told stubby to run) will search through the available endpoints and, if a match is found, respond with that endpoint's `response` data

### How Endpoints Are Matched

For a given endpoint, stubby only cares about matching the properties of the request that have been defined in the YAML. The exception to this rule is `method`; if it is omitted it is defaulted to `GET`.

For instance, the following will match any `POST` request to the root url:

```yaml
- request:
url: /
method: POST
response: {}
```

The request could have any headers and any post body it wants. It will match the above.

Pseudocode:

```
for each of stored endpoints {

for each of {
if . != .
next endpoint
}

return
}
```

## Programmatic API

### Referencing Stubby in Your Project

Add `stubby` as a reference within your project:

```
PM> Install-Package stubby
```

### The Arugments Class

The `Arguments` class is a container for options used by the `Stubby` class during construction that correlate to the command-line options.

#### Public Properties

* `Admin` - Port for admin portal. Defaults to `8889`.
* `Stubs` - Port for stubs portal. Defaults to `8882`.
* `Tls` - Port for stubs https portal. Defaults to `7443`.
* `Location` - Hostname at which to bind stubby. Defaults to `localhost`.
* `Data` - Data file location to pre-load endpoints. YAML format.
* `Mute` - Prevent stubby from logging to the console. Defaults to `true`.
* `Watch` - Monitor supplied data file for changes and reload endpoints if necessary. Defaults to `false`.

Here is the constructor and default values of each (public) property.

```cs
public Arguments() {
Admin = 8889;
Stubs = 8882;
Tls = 7443;
Location = "localhost";
Data = null;
Mute = true;
Watch = false;
}
```

#### The Stubby Class

There is a single constructor that takes an instance of `Arguments` as it's parameter.

```cs
// Constructor
public Stubby(IArguments arguments);

//Start stubby's services
public void Start();

// Stop stubby's services
public void Stop();

// Get a listing of all of stubby's configured endpoints
public IList GetAll();

// Get an endpoint back by id
public Endpoint Get(uint id);

// Find an endpoint by it's matching Request
public Endpoint Find(Request request);

// Swap out the configuration of one of the endpoints.
// True if successful.
public bool Replace(uint id, Endpoint endpoint);

// Remove an endpoint by id
// True if the operation succeeded
public bool Delete(uint id);

// Remove all configured endpoints from stubby
public void DeleteAll();

// Add a new endpoint configuration
// out: the generated id
// True if successful
public bool Add(Endpoint endpoint, out uint id);

// Add many new endpoint configurations
// out: the generated ids
// True if successful
public bool Add(IEnumerable endpoints, out IList ids);
```

#### An Example (Pardon my NUnit)

```cs
[TestFixture]
public class MyIntegrationTest {
private readonly Stubby _stubby = new Stubby(new Arguments {
Admin = 9999,
Stubs = 9992,
Mute = true,
Data = "../../YAML/endpoints.yaml"
});

[TestFixtureSetUp]
public void Before() {
_stubby.Start();
}

[TestFixtureTearDown]
public void After() {
_stubby.Stop();
}

[Test]...
}
```

## See Also

* **[stubby4node](https://github.com/mrak/stubby4node):** A node.js implementation of stubby
* **[stubby4j](https://github.com/azagniotov/stubby4j):** A java implementation of stubby

## TODO

* `post` parameter as a hashmap under `request` for easy form-submission value matching

## NOTES

* __Copyright__ 2013 Eric Mrak, Alexander Zagniotov
* __License__ Apache v2.0