or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

fixture-management.mdhost-configuration.mdindex.mdmode-management.mdproxy-system.md

fixture-management.mddocs/

0

# Fixture Management

1

2

Replay's fixture management system handles the recording, storage, and retrieval of HTTP response fixtures, providing fine-grained control over what gets recorded and how it's stored.

3

4

## Capabilities

5

6

### Fixtures Directory

7

8

Configure the directory where recorded HTTP responses are stored.

9

10

```javascript { .api }

11

/**

12

* Directory path where recorded HTTP responses are stored

13

* @type {string}

14

*/

15

Replay.fixtures: string;

16

```

17

18

**Usage Examples:**

19

20

```javascript

21

const Replay = require('replay');

22

23

// Set fixtures directory

24

Replay.fixtures = __dirname + '/test/fixtures';

25

Replay.fixtures = './fixtures/replay';

26

Replay.fixtures = '/absolute/path/to/fixtures';

27

28

// Get current fixtures directory

29

console.log('Fixtures stored in:', Replay.fixtures);

30

```

31

32

**Directory Structure:**

33

34

Fixtures are organized by hostname:

35

```

36

fixtures/

37

├── api.example.com/

38

│ ├── 20231201_120000_001

39

│ └── 20231201_120030_002

40

├── cdn.example.com/

41

│ └── 20231201_120100_001

42

└── localhost_3000/

43

└── 20231201_120200_001

44

```

45

46

### Header Configuration

47

48

Configure which HTTP headers are recorded and used for request matching.

49

50

```javascript { .api }

51

/**

52

* Array of regular expressions defining which headers to record/match

53

* @type {RegExp[]}

54

*/

55

Replay.headers: RegExp[];

56

```

57

58

**Default Headers:**

59

- Headers starting with `Accept` (e.g., Accept-Encoding, Accept-Language)

60

- `Authorization` header

61

- `Body` header (for POST request body matching)

62

- `Content-Type` header

63

- `Host` header

64

- Headers starting with `If-` (e.g., If-Modified-Since, If-None-Match)

65

- Headers starting with `X-` (e.g., X-Requested-With, X-API-Key)

66

67

**Usage Examples:**

68

69

```javascript

70

const Replay = require('replay');

71

72

// Add custom headers to record/match

73

Replay.headers.push(/^content-length/);

74

Replay.headers.push(/^user-agent/);

75

76

// Replace default headers with custom set

77

Replay.headers = [

78

/^accept/,

79

/^authorization/,

80

/^content-type/,

81

/^x-api-key/

82

];

83

84

// View current header patterns

85

console.log('Recording headers:', Replay.headers);

86

```

87

88

### Automatic Response Recording

89

90

Replay automatically records HTTP responses in `record` mode. All responses are recorded by default, with the recording behavior controlled by the current mode and host configuration.

91

92

**Recording Behavior by Mode:**

93

94

- **Record Mode**: All requests to non-configured hosts are recorded when responses are received

95

- **Replay Mode**: No recording occurs, only playback of existing fixtures

96

- **Cheat Mode**: No recording occurs, allows live requests without saving responses

97

- **Bloody Mode**: No recording occurs, all requests go live

98

99

**Response Storage:**

100

101

All successful HTTP responses (regardless of status code) are automatically saved to fixture files when in record mode, unless the host is configured for pass-through, drop, or localhost routing.

102

103

### Catalog API

104

105

Direct access to the internal catalog system for advanced fixture management.

106

107

```javascript { .api }

108

/**

109

* Find matching fixtures for a given host

110

* @param {string} host - Hostname to find fixtures for

111

* @returns {Matcher[] | null} Array of matcher functions or null if no fixtures found

112

*/

113

Replay.catalog.find(host: string): Matcher[] | null;

114

115

/**

116

* Save a captured response as a fixture

117

* @param {string} host - Hostname to save fixture for

118

* @param {ReplayRequest} request - Request object

119

* @param {ReplayResponse} response - Response object to save

120

* @param {Function} callback - Callback function called when save completes

121

*/

122

Replay.catalog.save(

123

host: string,

124

request: ReplayRequest,

125

response: ReplayResponse,

126

callback: (error?: Error) => void

127

): void;

128

129

/**

130

* Get current fixtures directory path

131

* @returns {string} Current fixtures directory path

132

*/

133

Replay.catalog.getFixturesDir(): string;

134

135

/**

136

* Set fixtures directory path and clear loaded fixtures

137

* @param {string} dir - New fixtures directory path

138

*/

139

Replay.catalog.setFixturesDir(dir: string): void;

140

```

141

142

**Usage Examples:**

143

144

```javascript

145

const Replay = require('replay');

146

147

// Find fixtures for a specific host

148

const matchers = Replay.catalog.find('api.example.com');

149

if (matchers) {

150

console.log(`Found ${matchers.length} fixtures for api.example.com`);

151

}

152

153

// Get current fixtures directory

154

const fixturesDir = Replay.catalog.getFixturesDir();

155

console.log('Fixtures stored in:', fixturesDir);

156

157

// Change fixtures directory

158

Replay.catalog.setFixturesDir('./new-fixtures');

159

160

// Manual fixture saving (advanced usage)

161

const request = {

162

url: new URL('http://api.example.com/users'),

163

method: 'GET',

164

headers: { 'accept': 'application/json' }

165

};

166

167

const response = {

168

version: '1.1',

169

statusCode: 200,

170

statusMessage: 'OK',

171

rawHeaders: ['Content-Type', 'application/json'],

172

headers: { 'content-type': 'application/json' },

173

body: [[Buffer.from('{"users": []}'), 'utf8']]

174

};

175

176

Replay.catalog.save('api.example.com', request, response, (error) => {

177

if (error) {

178

console.error('Failed to save fixture:', error);

179

} else {

180

console.log('Fixture saved successfully');

181

}

182

});

183

```

184

185

## Fixture File Format

186

187

Fixtures are stored as human-readable text files with a specific format:

188

189

### Format Structure

190

```

191

[HTTP_METHOD] [PATH]

192

[request-header-name]: [request-header-value]

193

[request-header-name]: [request-header-value]

194

195

HTTP/[VERSION] [STATUS_CODE] [STATUS_TEXT]

196

[response-header-name]: [response-header-value]

197

[response-header-name]: [response-header-value]

198

199

[response-body]

200

```

201

202

### Example Fixture File

203

```

204

GET /api/v1/users/123

205

Authorization: Bearer token123

206

Accept: application/json

207

Content-Type: application/json

208

209

HTTP/1.1 200 OK

210

Content-Type: application/json

211

Server: nginx/1.18.0

212

Date: Fri, 02 Dec 2023 12:30:45 GMT

213

Content-Length: 156

214

215

{

216

"id": 123,

217

"name": "John Doe",

218

"email": "john@example.com",

219

"created_at": "2023-12-01T10:00:00Z"

220

}

221

```

222

223

### Regular Expression Support

224

225

Fixtures can use regular expressions for URL matching by adding `REGEXP` between method and path:

226

227

```

228

GET REGEXP /\/users\/\d+/

229

Accept: application/json

230

231

HTTP/1.1 200 OK

232

Content-Type: application/json

233

234

{"id": 123, "name": "User"}

235

```

236

237

## Fixture Management Operations

238

239

### Manual Fixture Editing

240

241

Fixtures can be manually edited for testing specific scenarios:

242

243

```javascript

244

// Original fixture response might be:

245

// HTTP/1.1 200 OK

246

// {"status": "success", "data": [...]}

247

248

// Edit to test error handling:

249

// HTTP/1.1 500 Internal Server Error

250

// {"status": "error", "message": "Database connection failed"}

251

```

252

253

### Fixture File Naming

254

255

- Files are typically named with timestamps: `20231201_120000_001`

256

- Names can be changed to be more descriptive: `user-login-success`, `api-error-500`

257

- Directory structure is based on hostname and port

258

259

### Multiple Responses

260

261

Multiple fixture files in the same host directory allow testing different scenarios:

262

263

```

264

fixtures/api.example.com/

265

├── user-login-success # Successful login

266

├── user-login-invalid-creds # Invalid credentials

267

├── user-login-account-locked # Account locked

268

└── user-profile-not-found # User not found

269

```

270

271

## Environment Integration

272

273

### Debug Mode

274

275

Enable fixture-related debug logging:

276

277

```bash

278

DEBUG=replay npm test

279

```

280

281

This will log fixture loading, matching, and saving operations.

282

283

### Directory Resolution

284

285

Fixture directory paths are resolved relative to the current working directory unless absolute:

286

287

```javascript

288

// Relative paths

289

Replay.fixtures = './fixtures'; // Relative to cwd

290

Replay.fixtures = 'test/fixtures'; // Relative to cwd

291

292

// Absolute paths

293

Replay.fixtures = __dirname + '/fixtures'; // Relative to current file

294

Replay.fixtures = '/var/app/fixtures'; // Absolute path

295

```

296

297

## Error Handling

298

299

### Fixture Loading Errors

300

301

Errors that can occur during fixture loading and playback:

302

303

```javascript { .api }

304

// Thrown when fixture file is corrupted or malformed

305

class CorruptFixtureError extends Error {

306

code: 'CORRUPT FIXTURE';

307

syscall: 'connect';

308

message: string; // Details about the corruption

309

}

310

311

// Thrown when no fixture matches and not in record mode

312

class ConnectionRefusedError extends Error {

313

code: 'ECONNREFUSED';

314

errno: 'ECONNREFUSED';

315

syscall: 'connect';

316

message: 'Error: connect ECONNREFUSED';

317

}

318

```

319

320

**Usage Examples:**

321

322

```javascript

323

const Replay = require('replay');

324

325

// Handle fixture-related errors

326

Replay.on('error', (error) => {

327

if (error.code === 'CORRUPT FIXTURE') {

328

console.error('Fixture file is corrupted:', error.message);

329

// Delete the corrupt fixture file and re-record

330

}

331

332

if (error.code === 'ECONNREFUSED' && Replay.mode === 'replay') {

333

console.error('No fixture found for request. Switch to record mode to capture it.');

334

}

335

});

336

337

// Handle catalog save errors

338

Replay.catalog.save('api.example.com', request, response, (error) => {

339

if (error) {

340

if (error.code === 'ENOENT') {

341

console.error('Fixtures directory does not exist');

342

} else if (error.code === 'EACCES') {

343

console.error('Permission denied writing fixture file');

344

} else {

345

console.error('Failed to save fixture:', error.message);

346

}

347

}

348

});

349

```