reynard

Automated Fault Injection Testing for Service-Oriented Distributed Systems

https://github.com/reynard-testing/reynard

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (10.9%) to scientific vocabulary
Last synced: 6 months ago · JSON representation ·

Repository

Automated Fault Injection Testing for Service-Oriented Distributed Systems

Basic Info
  • Host: GitHub
  • Owner: reynard-testing
  • License: apache-2.0
  • Language: Java
  • Default Branch: main
  • Homepage:
  • Size: 1.51 MB
Statistics
  • Stars: 1
  • Watchers: 1
  • Forks: 0
  • Open Issues: 3
  • Releases: 6
Created about 1 year ago · Last pushed 8 months ago
Metadata Files
Readme Citation

README.md

Logo Reynard

GitHub License GitHub Tag

Reynard is an automated fault injection testing tool for Microservice or Service-Oriented Architectures. It allows for the automatic exploration of the effect of combinations of partial failures on a system interaction.

Reynard consists of the following primary components:

  • Instrumentation to allow deterministic fault injection and inspect inter-service communication: a reverse proxy is placed as a side-car to services of interest, while a controller exposes an API for fault injection testing.
  • A testing library to automatically explore relevant combinations of faults. This is implemented as a Java JUnit 5 decorator.

Example

Considere a webshop designed as a Microservice Architecture. In a test environment, we have added the required instrumentation of Reynard. For the checkout endpoint, we want to ensure that failures do not cause a payment for an order that is never shipped. Using Reynard, we can create the following test suite:

```java @FiTest void testCheckout(TrackedFaultload faultload) throws IOException { // Given - a user with a cart int userId = newUser(); addItemToCart(userId, productId, quantity);

Request request = faultload.newRequestBuilder() .url("http://localhost:5000/checkout?userId=" + userId) .build();

// When - we perform a checkout for the user try (Response response = client.newCall(request).execute()) { // Given - the system state after the interaction boolean paymentProcessed = PaymentService.getPaymentProcessedFor(userId); boolean orderShipped = ShippingService.getOrderShippedFor(userId);

  // Then - either the user payed, and the order is shipped, or neither ocurred
  // Otherwise a user would be paying for nothing, or a order is shipped without payment
  assertTrue((paymentProcessed && orderShipped) || (!paymentProcessed && !orderShipped));

} } ```

Reynard will automatically plan distinct combinations of faults to verify the system's behaviour.

Capabilities

Precise Fault Injection

Reynard's instrumentation can precisely identify events in the system and injection faults for those events. For example, we can inject faults based on temporal and causal relation, e.g., the event when service X sends a second request to a specific endpoint of service Y when called by service Z.

Broad Applicablility

Reynard can function in any system that supports modern distributed tracing, can be configured with a reverse-proxy side-card pattern (similar to another common tool in MA: service meshes), and uses HTTP or gRPC for inter-service communication.

Automated Exhaustive Testing

Reynard is capable of automatically discovering all fault injection points, and dynamically analyzes the system behaviour to cover all distinct combinations of faults that can input the outcome of a test scenario.

Installation

Adding Instrumentation

To correctly use Reynard, we require the addition of instrumentation to the the system's runtime. In particular we require two types of instrumentation:

  • The addition of OpenTelemetry for each service of interest, or any tracing solution that allows for both the propagation of the traceparent and tracecontext headers as defined by the W3C.
  • The addition of reverse proxies as a sidecar to each service of interest.
  • The addition of a fault injection controller process.
  • (Optionally) the addition of Jaeger to collect and visualize traces for debugging purposes.

Distributed Tracing:

We recommend OpenTelemetry as a distributed tracing solution as, in many cases, it can be automatically added. Keep in mind that adding distributed tracing will introduce some overhead. Our tooling does not require that every span is exported, but it does require that context is correctly propagated. Exporting spans to a tool like Jaeger will aid in debugging in case bugs are found using Reynard.

Proxies:

The reverse proxies are available as a Docker image. The exact deployment of the proxies depends on the system configuration. In most cases, you want to route traffic targeted to a service of interest through a proxy, without changing the services. We provide a converter utility for Docker compose. The supported communication protocols are HTTP1.1, HTTP2 and gRPC.

Controller:

The controller service is available as a Docker image. It must be configured to be aware of all deployed proxies to function correctly.

Local development:

A local tagged version can be build using the make build-all command. This results in two images: fit-controller:latest and fit-proxy:latest.

Using the Test Library

The testing library is available as a Maven package under the dev.reynard.junit namespace. To create a Reynard test suite, simply add the @FiTest to a test function that has a single argument of type TrackedFaultload:

java @FiTest void testSuite(TrackedFaultload faultload) { ... }

The faultload defines the current set of faults (starting with no faults), and the headers required for the instrumentation to understand how to inject these faults.

The test case must have the properties:

  • It should be repeatable, as the test case is run repeatedly and it should be independent of previous executions.
  • An interaction that can be subject to faults must include the headers defined in the tracked faultload. If you use okhttp, you can use the utility function TrackedFaultload.newRequestBuilder to automatically add the right headers.
  • Only one system interaction should use the faultload headers, as not to confuse the testing library. The test suite can make multiple calls to the system to prepare the system state.

Limits

Reynard can work in a variety of scenarios, but is not complete or sound in the general case. As Reynard performs dynamic grey-box testing, it builds an understanding of the system's interaction, observing the effect of events. As services can behave in any kind of way, there are limits to what Reynard can understand. Below are a number of known limitations:

  • Reynard can handle concurrency and asynchronicity, but only if each request is uniquely identifiable. Otherwise, two events might swap identifiers and their effects will be wrongly attributed.
  • Reynard assumes that the effect of an event is deterministic. For example, if a service performs two requests on an endpoint invocation, and Reynard observes that a failure of the first request omits the second request, then it assumes this always holds. Note that concurrency can result in nondeterministic behaviour of effects.
  • Reynard assumes that responses with the same status code will result in the same behaviour. Systems that use the response body to indicate specific failures will result in an unsound exploration.
  • Reynard does not correctly understand asynchronous communication patterns (such as message queues), as it does not capture the creation of an event, nor when an event is fully consumed. It will only be able to reason about events that occurred during the synchronous request to the system.

If a system does not conform to these limits, then Reynard can test combinations of faults that are infeasible to combine (hence redundant), or might omit cases. Still, it will be able to explore interesting combinations of faults.

Please see the readme for the testing library what can be configured to work around some of these limitations.

Owner

  • Name: Reynard
  • Login: reynard-testing
  • Kind: organization

Automated Fault Injection Testing for MA and SOA

Citation (CITATION.cff)

cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
  - family-names: Flipse
    given-names: Delano
    orcid: https://orcid.org/0009-0008-4163-4743
title: "Reynard: Practical Network-Level Fault Injection Testing of Service-Oriented Distributed Systems"
version: 0.1.3
url: "https://github.com/reynard-testing/reynard"
date-released: 2025-07-15
#preferred-citation:
#  type: proceedings
# identifiers:
#   - type: doi
#     value:

GitHub Events

Total
  • Release event: 4
  • Watch event: 1
  • Delete event: 15
  • Push event: 49
  • Pull request event: 21
  • Create event: 13
Last Year
  • Release event: 4
  • Watch event: 1
  • Delete event: 15
  • Push event: 49
  • Pull request event: 21
  • Create event: 13

Issues and Pull Requests

Last synced: 8 months ago

All Time
  • Total issues: 0
  • Total pull requests: 2
  • Average time to close issues: N/A
  • Average time to close pull requests: less than a minute
  • Total issue authors: 0
  • Total pull request authors: 1
  • Average comments per issue: 0
  • Average comments per pull request: 0.0
  • Merged pull requests: 1
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 2
  • Average time to close issues: N/A
  • Average time to close pull requests: less than a minute
  • Issue authors: 0
  • Pull request authors: 1
  • Average comments per issue: 0
  • Average comments per pull request: 0.0
  • Merged pull requests: 1
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
  • delanoflipse (15)
Top Labels
Issue Labels
Pull Request Labels

Dependencies

services/orchestrator/Dockerfile docker
  • python 3 build
services/proxy/Dockerfile docker
  • golang 1.23.4 build
services/proxy/go.mod go
  • golang.org/x/net v0.34.0
  • golang.org/x/text v0.21.0
services/proxy/go.sum go
  • golang.org/x/net v0.34.0
  • golang.org/x/text v0.21.0
lib/pom.xml maven
  • com.fasterxml.jackson.core:jackson-databind 2.18.1
  • org.apache.httpcomponents.client5:httpclient5-fluent 5.4
  • org.junit.jupiter:junit-jupiter-engine 5.10.0
  • org.junit.jupiter:junit-jupiter-params 5.10.0
  • org.testcontainers:junit-jupiter 1.20.4
  • org.testcontainers:testcontainers 1.20.4
services/orchestrator/requirements.txt pypi
  • Flask ==3.1.0
  • opentelemetry-proto ==1.29.0
  • protobuf ==5.29.2