Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# ourKairos

## Repository scaffolding

The repository includes merge-safe implementation templates for work that will land incrementally.

- `tooling/api-module-template` contains the baseline Express module structure for future API features.
- `docs/api-architecture.md` documents the intended module registration pattern.
Expand Down
13 changes: 13 additions & 0 deletions docs/failure-harness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Failure Harness

The repository includes a failure harness template for future Express environments.

## Use cases

- testing retry behavior
- exercising degraded UI states
- validating request timeout handling

## Current location

`tooling/failure-harness-template`
13 changes: 13 additions & 0 deletions tooling/failure-harness-template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Failure Harness Template

This template provides a controlled way to inject failures into future Express routes.

## Supported modes

- latency spike
- random 500
- timeout
- dropped response
- malformed JSON

The harness is designed to be opt-in and safe to merge before the API app exists.
48 changes: 48 additions & 0 deletions tooling/failure-harness-template/failure-harness.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { NextFunction, Request, Response } from "express";
import type { FailureMode, HarnessState } from "./failure-harness.types";

const pickRandom = <T>(items: T[]): T => items[Math.floor(Math.random() * items.length)] as T;

const applyFailure = (
mode: FailureMode,
request: Request,
response: Response,
next: NextFunction
) => {
switch (mode) {
case "latency-spike":
setTimeout(next, 1500);
return;
case "random-500":
response.status(500).json({ error: "Injected failure", mode, path: request.path });
return;
case "timeout":
setTimeout(() => response.end(), 20000);
return;
case "dropped-response":
request.socket.destroy();
return;
case "malformed-json":
response.type("application/json").send("{\"broken\":");
return;
}
};

export const createFailureHarness =
(state: HarnessState) =>
(request: Request, response: Response, next: NextFunction) => {
if (!state.enabled || !state.activeScenario) {
next();
return;
}

const rules = state.scenarios[state.activeScenario] ?? [];
const rule = rules.find((candidate) => new RegExp(candidate.pathPattern).test(request.path));

if (!rule || Math.random() > rule.probability) {
next();
return;
}

applyFailure(pickRandom(rule.modes), request, response, next);
};
23 changes: 23 additions & 0 deletions tooling/failure-harness-template/failure-harness.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Router } from "express";
import type { HarnessState } from "./failure-harness.types";

export const createFailureHarnessRouter = (state: HarnessState) => {
const router = Router();

router.get("/__sandbox/failure-harness/state", (_request, response) => {
response.json(state);
});

router.post("/__sandbox/failure-harness/enable", (request, response) => {
state.enabled = Boolean(request.body?.enabled);
response.json({ enabled: state.enabled });
});

router.post("/__sandbox/failure-harness/scenario", (request, response) => {
const scenario = request.body?.scenario;
state.activeScenario = typeof scenario === "string" ? scenario : null;
response.json({ activeScenario: state.activeScenario });
});

return router;
};
22 changes: 22 additions & 0 deletions tooling/failure-harness-template/failure-harness.state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { HarnessState } from "./failure-harness.types";

export const createHarnessState = (): HarnessState => ({
enabled: false,
activeScenario: null,
scenarios: {
"flaky-network": [
{
pathPattern: ".*",
probability: 0.2,
modes: ["latency-spike", "dropped-response"]
}
],
"partial-outage": [
{
pathPattern: "^/(?!health).*",
probability: 0.5,
modes: ["random-500", "timeout"]
}
]
}
});
18 changes: 18 additions & 0 deletions tooling/failure-harness-template/failure-harness.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type FailureMode =
| "latency-spike"
| "random-500"
| "timeout"
| "dropped-response"
| "malformed-json";

export interface HarnessRule {
pathPattern: string;
probability: number;
modes: FailureMode[];
}

export interface HarnessState {
enabled: boolean;
activeScenario: string | null;
scenarios: Record<string, HarnessRule[]>;
}
4 changes: 4 additions & 0 deletions tooling/failure-harness-template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./failure-harness.types";
export * from "./failure-harness.state";
export * from "./failure-harness.middleware";
export * from "./failure-harness.routes";
Loading