Giter VIP home page Giter VIP logo

replicant's Introduction

This project has moved to a new home on github.com/unbabel/replicant


Replicant

Go Report Card GoDoc Docker Cloud Automated build

Replicant is a synthetic transaction execution framework named after the bioengineered androids from Blade Runner. (all synthetics came from Blade Runner :)

It allows web application testing using chromedp, and api application testing using Go or Javascript. Provides a test manager, execution scheduler, api and facilities for emitting result data to external systems.

Status

Under heavy development and API changes are expected. Please file an issue if anything breaks.

Requirements

  • Go 1.13
  • External URL for API tests that require webhook based callbacks
  • Chrome with remote debugging (CDP) either in headless mode or in foreground (useful for testing)

Examples

Running the replicant server locally with docker

Using example config from the project root dir.

docker stack deploy -c $PWD/docker-compose.yaml replicant

This will deploy the replicant server and 2 chrome-headless nodes for web tests, persisting data under /data.

Web application testing (local development)

Web application testing support is based on the FQL (Ferret Query Language), documentation.

Test definition (can be also in json format)

POST http://127.0.0.1:8080/api/v1/run
content-type: application/yaml

name: duckduckgo-web-search
driver: web
schedule: '@every 60s'
timeout: 50s
retry_count: 2
inputs:
  url: "https://duckduckgo.com"
  user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36"
  timeout: 5000000
  text: "blade runner"
metadata:
  transaction: website-search
  application: duckduckgo
  environment: production
  component: website
script: |
  LET doc = DOCUMENT('{{ index . "url" }}', { driver: "cdp", userAgent: "{{ index . "user_agent" }}"})
  INPUT(doc, '#search_form_input_homepage', "{{ index . "text" }}")
  CLICK(doc, '#search_button_homepage')
  WAIT_NAVIGATION(doc)
  LET result = ELEMENT(doc, '#r1-0 > div > div.result__snippet.js-result-snippet').innerText
  RETURN {
    failed: result == "",
    message: "search result",
    data: result,
  }

Response

{
  "data": [
    {
      "uuid": "01DSSR5GH2BPX4G5FFCEVPEBKK",
      "name": "duckduckgo-web-search",
      "driver": "web",
      "failed": true,
      "message": "",
      "data": "",
      "time": "2019-11-16T09:19:39.554976Z",
      "metadata": {
        "application": "duckduckgo",
        "component": "website",
        "environment": "production",
        "transaction": "website-search"
      },
      "retry_count": 0,
      "with_callback": false,
      "duration_seconds": 6.967938203,
      "error": "operation timed out: WAIT_NAVIGATION(doc) at 4:0"
    }
  ]
}

API testing (local development)

Using the javascript driver

The following API is exposed by the javascript driver in order to perform HTTP calls and logging:

  • replicant.Log(string) log messages from the javascript test on the replicant server log.
  • replicant.NewResponse() create a new response object to be returned as a result of the test, which should be modified accordingly to reflect the test result. The response must be returned as a serialized JSON object by calling its bounded method Response.JSON, E.g. return response.JSON().

Response type attributes:

{
		Data: "",
		Message: "",
		Failed: false,
}
  • replicant.http.NewRequest() creates a new HTTP request object for performing HTTP calls.

HttpRequest attributes:

{
		URL: "",
		Method: "",
		Body: "",
		Header: {},
		Params: {},
		FormData: {},
		SSLSkipVerify: false,
  • `replicant.http.Do(HttpRequest) performs a HTTP request and returns its response.

HttpResponse attributes:

{
	Status: ""
	StatusCode: 200
	Protocol: ""
	Body: ""
	Header: {}
	Error: ""
}

Test definition (can be also in JSON format)

POST http://127.0.0.1:8080/api/v1/run
content-type: application/yaml

name: duckduckgo-api-search
driver: javascript
schedule: '@every 60s'
timeout: 60s
retry_count: 2
inputs:
  url: "https://api.duckduckgo.com"
  text: "blade runner"
metadata:
  transaction: api-search
  application: duckduckgo
  environment: production
  component: api
script: |
  function Run(ctx) {
    req = replicant.http.NewRequest()
    req.URL = "{{ index . "url" }}"
    req.Params.q = "{{ index . "text" }}"
    req.Params.format = "json"
    req.Params.no_redirect = "1"
    resp = replicant.http.Do(req)
    data = JSON.parse(resp.Body)
    rr = replicant.NewResponse()
    switch(data.RelatedTopics && data.RelatedTopics.length > 0) {
      case true:
        rr.Data = data.RelatedTopics[0].Text
        rr.Message = resp.Status
        rr.Failed = false
        break
      case false:
        rr.Data = JSON.stringify(data)
        rr.Message = resp.Status
        rr.Failed = true
        break
    }
    return rr.JSON()
  }
Using the Go driver

Standard Go code can be used to create tests using following rules:

  • The package name must be transaction
  • The test function must implement the following signature: func Run(ctx context.Context) (message string, data string, err error).

Keep in mind that unlike the javascript driver which doesn't expose any I/O or lower level functionality for accessing the underlying OS, the Go driver currently exposes all of the Go standard library. Only use this driver if you are absolutely sure of what you are doing. This is planned to change in the future.

Test definition (can be also in JSON format)

POST http://127.0.0.1:8080/api/v1/run
content-type: application/yaml

name: duckduckgo-api-search
driver: go
schedule: '@every 60s'
timeout: 60s
retry_count: 2
inputs:
  url: "https://api.duckduckgo.com/"
  text: "blade runner"
metadata:
  transaction: api-search
  application: duckduckgo
  environment: production
  component: api
script: |
  package transaction
  import "bytes"
  import "context"
  import "fmt"
  import "net/http"
  import "io/ioutil"
  import "net/http"
  import "regexp"
  func Run(ctx context.Context) (m string, d string, err error) {
    req, err := http.NewRequest(http.MethodGet, "{{ index . "url" }}", nil)
      if err != nil {
        return "request build failed", "", err
    }
    req.Header.Add("Accept-Charset","utf-8")
    q := req.URL.Query()
    q.Add("q", "{{ index . "text" }}")
    q.Add("format", "json")
    q.Add("pretty", "1")
    q.Add("no_redirect", "1")
    req.URL.RawQuery = q.Encode()
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
      return "failed to send request", "", err
    }
    buf, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      return "failed to read response", "", err
    }
    rx, err := regexp.Compile(`"Text"\s*:\s*"(.*?)"`)
    if err != nil {
      return "failed to compile regexp", "", err
    }
    s := rx.FindSubmatch(buf)
    if len(s) < 2 {
      return "failed to find data", "", fmt.Errorf("no match")
    }
    return "search result", fmt.Sprintf("%s", s[1]), nil
  }

Response

{
  "data": [
    {
      "uuid": "01DSSR7ST5Q1Y2Y7HDSQDNS7Y7",
      "name": "duckduckgo-api-search",
      "driver": "go",
      "failed": false,
      "message": "search result",
      "data": "Blade Runner A 1982 American neo-noir science fiction film directed by Ridley Scott, written by Hampton...",
      "time": "2019-11-16T09:20:54.597852Z",
      "metadata": {
        "application": "duckduckgo",
        "component": "api",
        "environment": "production",
        "transaction": "api-search"
      },
      "retry_count": 0,
      "with_callback": false,
      "duration_seconds": 0.486582328,
      "error": ""
    }
  ]
}

API

Method Resource Action
POST /v1/transaction Add a managed transaction
GET /v1/transaction Get all managed transaction definitions
GET /v1/transaction/:name Get a managed transaction definition by name
DELETE /v1/transaction/:name Remove a managed transaction
POST /v1/run Run an ad-hoc transaction
POST /v1/run/:name Run a managed transaction by name
GET /v1/result Get all managed transaction last execution results
GET /v1/result/:name Get the latest result for a managed transaction by name
GET /metrics Get metrics (prometheus emitter must be enabled)
GET /debug/pprof Get available runtime profile data (debug enabled)
GET /debug/pprof/:profile Get profile data (for pprof, debug enabled)

TODO

  • Tests
  • Developer and user documentation
  • Add support for more conventional persistent stores
  • Vault integration for secrets (inputs)
  • Architecture and API documentation
  • Javascript driver transaction support

Acknowledgements

Contact

Bruno Moura [email protected]

License

Replicant source code is available under the Apache Version 2.0 License

replicant's People

Contributors

brunotm avatar gisson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

replicant's Issues

Add tests

The only tests implemented as of now are against stores.

Missing tests:

  • transaction
  • transaction drivers
  • transaction manager
  • server and api endpoints
  • emitters
  • e2e using replicant to test itself

Add load testing capabilities

Add support for running load tests. The tests implementation should be the same, but having options for number of tests to run and concurrency level.

Test result data must deliver statistics about runs.

  • Latency distribution
  • Error rate and distribution
  • ...

Improve documentation

Missing documentation:

  • Architecture
  • Deployment
  • Writing transaction drivers
  • Writing tests for each driver

Improve logging

I have pushed the boundaries with the absence of proper logging.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.