@gremlin/failure-flags
Advanced tools
Comparing version 0.0.15 to 0.0.16
32
index.js
@@ -18,9 +18,9 @@ /* | ||
const effect = require('./src/fault.js'); | ||
const defaultBehavior = effect.delayedResponseOrException; | ||
const defaultBehavior = effect.delayedDataOrException; | ||
const ifExperimentActive = async ({name, labels, behavior = defaultBehavior, resultPrototype = null, debug = false}) => { | ||
if(debug) console.log('ifExperimentActive', name, labels); | ||
const ifExperimentActive = async ({name, labels, behavior = defaultBehavior, dataPrototype = null, debug = false}) => { | ||
if(debug) console.log('ifExperimentActive', name, labels, dataPrototype); | ||
if (typeof behavior != 'function') { | ||
if(debug) console.log('behavior is not a function'); | ||
return false; | ||
return resolveOrFalse(debug, dataPrototype); | ||
} | ||
@@ -33,3 +33,3 @@ | ||
if(debug) console.log('unable to fetch experiment', ignore); | ||
return false; | ||
return resolveOrFalse(debug, dataPrototype); | ||
} | ||
@@ -39,3 +39,3 @@ | ||
if(debug) console.log('no experiment for', name, labels); | ||
return false; | ||
return resolveOrFalse(debug, dataPrototype); | ||
} | ||
@@ -52,12 +52,12 @@ | ||
if(debug) console.log('probablistically skipped', behaviorError) | ||
return false; | ||
return resolveOrFalse(debug, dataPrototype); | ||
} | ||
try { | ||
if (resultPrototype) { | ||
return await behavior(experiment, resultPrototype); | ||
} else { | ||
await behavior(experiment); | ||
return true; | ||
} | ||
if (dataPrototype) { | ||
return await behavior(experiment, dataPrototype); | ||
} else { | ||
await behavior(experiment); | ||
return true; | ||
} | ||
} catch(behaviorError) { | ||
@@ -69,2 +69,8 @@ if(debug) console.log('provided behavior error', behaviorError) | ||
const resolveOrFalse = (debug, dataPrototype) => { | ||
let value = dataPrototype? dataPrototype : false; | ||
if(debug) console.log('returning', value); | ||
return value; | ||
} | ||
module.exports = exports = { ifExperimentActive, fetchExperiment, effect, defaultBehavior }; |
@@ -169,3 +169,3 @@ /* | ||
effect: { | ||
response: { | ||
data: { | ||
property2: "experiment value", | ||
@@ -349,3 +349,3 @@ property3: "experiment originated", | ||
test('ifExperimentActive true if prototype response unset', async () => { | ||
test('ifExperimentActive true if dataPrototype unset and experiment active', async () => { | ||
try { | ||
@@ -355,3 +355,3 @@ const response = await failureflags.ifExperimentActive({ | ||
labels: {a:'1',b:'2'}, | ||
behavior: failureflags.effect.response, // explicitly test the exception effect, not default | ||
behavior: failureflags.effect.data, // explicitly test the exception effect, not default | ||
debug: false}); | ||
@@ -362,13 +362,14 @@ expect(response).toBe(true); | ||
} | ||
// the 'response' behavior does not use setTimeout | ||
expect(setTimeout).toHaveBeenCalledTimes(0); | ||
}); | ||
test('ifExperimentActive returns derrived if prototype response set', async () => { | ||
let response; | ||
test('ifExperimentActive returns derrived if dataPrototype set and experiment active', async () => { | ||
let data = { property1: 'prototype value', property2: 'prototype value' }; | ||
try { | ||
response = await failureflags.ifExperimentActive({ | ||
data = await failureflags.ifExperimentActive({ | ||
name: 'alteredResponseValue', | ||
labels: {a:'1',b:'2'}, | ||
behavior: failureflags.effect.response, // explicitly test the exception effect, not default | ||
resultPrototype: {property1: 'prototype value', property2: 'prototype value'}, | ||
behavior: failureflags.effect.data, // explicitly test the exception effect, not default | ||
dataPrototype: data, | ||
debug: false}); | ||
@@ -380,5 +381,22 @@ } catch(e) { | ||
expect(setTimeout).toHaveBeenCalledTimes(0); | ||
expect(data).toHaveProperty('property1', 'prototype value'); | ||
expect(data).toHaveProperty('property2', 'experiment value'); | ||
expect(data).toHaveProperty('property3', 'experiment originated'); | ||
}); | ||
test('ifExperimentActive returns dataPrototype if dataPrototype is set and no experiment active', async () => { | ||
let response = {property1: "prototype value"}; | ||
try { | ||
response = await failureflags.ifExperimentActive({ | ||
name: 'doesnotexist', | ||
labels: {a:'1',b:'2'}, | ||
behavior: (experiment) => { return false; }, // explicitly destroy the prototype | ||
dataPrototype: {property1: 'prototype value', property2: 'prototype value'}, | ||
debug: false}); | ||
} catch(e) { | ||
console.dir(e); | ||
expect(true).toBe(false); // always reject if this line is reached. | ||
} | ||
expect(setTimeout).toHaveBeenCalledTimes(0); | ||
expect(response).toHaveProperty('property1', 'prototype value'); | ||
expect(response).toHaveProperty('property2', 'experiment value'); | ||
expect(response).toHaveProperty('property3', 'experiment originated'); | ||
}); | ||
@@ -385,0 +403,0 @@ |
{ | ||
"name": "@gremlin/failure-flags", | ||
"version": "0.0.15", | ||
"version": "0.0.16", | ||
"description": "Failure Flags is a node SDK for working with the Gremlin fault injection platform to build application-level chaos experiments and reliability tests. This library works in concert with Gremlin-Lambda, a Lambda extension, or Gremlin-Container a container sidecar agent. This architecture minimizes the impact to your application code, simplifies configuration, and makes adoption painless.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -177,20 +177,23 @@ # failure-flags | ||
### Advanced: Changing Response Values | ||
### Advanced: Changing Application Data | ||
Suppose you want to be able to experiment with mangled responses from your application's dependencies. You can do that with a little extra work. You need to provide a prototype response in your call to `ifExperimentActive`. | ||
Suppose you want to be able to experiment with mangled data. This might include responses from your application's dependencies. You can do that with a little extra work. You need to provide prototype data in your call to `ifExperimentActive`. | ||
```js | ||
... | ||
let response = await failureflags.ifExperimentActive({ | ||
name: 'flagname', // the name of your failure flag | ||
labels: {}, // additional attibutes about this invocation | ||
resultPrototype: {name: 'HTTPResponse'}); // this prototype only sets the name of the response type | ||
let myData = {name: 'HTTPResponse'}; // this is just example data, it could be anything | ||
myData = await failureflags.ifExperimentActive({ | ||
name: 'flagname', // the name of your failure flag | ||
labels: {}, // additional attibutes about this invocation | ||
dataPrototype: myData); // "myData" is some variable like a request or response | ||
// You could also pass in an object literal. | ||
... | ||
``` | ||
If the `resultPrototype` property is set then you can use the `response` property in the Effect statement: | ||
If the `dataPrototype` property is set then you can use the `data` property in the Effect statement: | ||
```json | ||
{ | ||
"response": { | ||
"data": { | ||
"statusCode": 404, | ||
@@ -202,3 +205,3 @@ "statusMessage": "Not Found" | ||
Any properties in the `response` object in this map will be copied into a new object created from the prototype you provided. In this example, if the experiment is not running then the `resposne` value will be false, and if it is running the `resposne` variable would contain the following object: | ||
Any properties in the `data` object in this map will be copied into a new object created from the prototype you provided. In this example, if the experiment is not running then `myData` will be returned unaltered, and if it is running it would have been altered to the following: | ||
@@ -205,0 +208,0 @@ ```json |
@@ -46,9 +46,11 @@ /* | ||
const response = async (experiment, prototype) => { | ||
if (!experiment.effect.response || typeof experiment.effect.response !== "object" || !prototype ) | ||
return; | ||
const data = async (experiment, prototype) => { | ||
if (!experiment.effect.data || typeof experiment.effect.data !== "object") | ||
return prototype; | ||
const response = experiment.effect.response; | ||
const res = Object.create(prototype); | ||
Object.assign(res, response); | ||
let toUse = prototype? prototype : {}; | ||
const data = experiment.effect.data; | ||
const res = Object.create(toUse); | ||
Object.assign(res, data); | ||
return res; | ||
@@ -66,8 +68,8 @@ } | ||
const delayedResponseOrException = async (e, responsePrototype) => { | ||
const delayedDataOrException = async (e, dataPrototype) => { | ||
await latency(e); | ||
exception(e); | ||
return response(e, responsePrototype); | ||
return data(e, dataPrototype); | ||
} | ||
module.exports = exports = { latency, exception, response, delayedException, delayedResponseOrException }; | ||
module.exports = exports = { latency, exception, data, delayedException, delayedDataOrException }; |
40453
558
232