swagger-parser-mcp-server 2.3.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -120
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -102
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +2 -283
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +0 -108
- package/dist/schemas.js.map +1 -1
- package/dist/utils/swaggerCache.d.ts +0 -4
- package/dist/utils/swaggerCache.d.ts.map +1 -1
- package/dist/utils/swaggerCache.js +0 -8
- package/dist/utils/swaggerCache.js.map +1 -1
- package/package.json +3 -11
- package/dist/application/snapshot/createSnapshotRuntime.d.ts +0 -2
- package/dist/application/snapshot/createSnapshotRuntime.d.ts.map +0 -1
- package/dist/application/snapshot/createSnapshotRuntime.js +0 -2
- package/dist/application/snapshot/createSnapshotRuntime.js.map +0 -1
- package/dist/application/snapshot/snapshotCaptureService.d.ts +0 -90
- package/dist/application/snapshot/snapshotCaptureService.d.ts.map +0 -1
- package/dist/application/snapshot/snapshotCaptureService.js +0 -394
- package/dist/application/snapshot/snapshotCaptureService.js.map +0 -1
- package/dist/application/snapshot/snapshotRepository.d.ts +0 -77
- package/dist/application/snapshot/snapshotRepository.d.ts.map +0 -1
- package/dist/application/snapshot/snapshotRepository.js +0 -2
- package/dist/application/snapshot/snapshotRepository.js.map +0 -1
- package/dist/domain/canonical/canonicalSnapshot.d.ts +0 -61
- package/dist/domain/canonical/canonicalSnapshot.d.ts.map +0 -1
- package/dist/domain/canonical/canonicalSnapshot.js +0 -300
- package/dist/domain/canonical/canonicalSnapshot.js.map +0 -1
- package/dist/domain/contracts/runtimeEnvironmentContract.d.ts +0 -21
- package/dist/domain/contracts/runtimeEnvironmentContract.d.ts.map +0 -1
- package/dist/domain/contracts/runtimeEnvironmentContract.js +0 -50
- package/dist/domain/contracts/runtimeEnvironmentContract.js.map +0 -1
- package/dist/domain/contracts/snapshotDiffContract.d.ts +0 -270
- package/dist/domain/contracts/snapshotDiffContract.d.ts.map +0 -1
- package/dist/domain/contracts/snapshotDiffContract.js +0 -99
- package/dist/domain/contracts/snapshotDiffContract.js.map +0 -1
- package/dist/domain/diff/endpointDiffClassifier.d.ts +0 -78
- package/dist/domain/diff/endpointDiffClassifier.d.ts.map +0 -1
- package/dist/domain/diff/endpointDiffClassifier.js +0 -317
- package/dist/domain/diff/endpointDiffClassifier.js.map +0 -1
- package/dist/http.d.ts +0 -3
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -52
- package/dist/http.js.map +0 -1
- package/dist/infrastructure/postgres/migrationRunner.d.ts +0 -14
- package/dist/infrastructure/postgres/migrationRunner.d.ts.map +0 -1
- package/dist/infrastructure/postgres/migrationRunner.js +0 -161
- package/dist/infrastructure/postgres/migrationRunner.js.map +0 -1
- package/dist/infrastructure/postgres/migrations/001_snapshot_schema.sql +0 -29
- package/dist/infrastructure/postgres/migrations/002_snapshot_change_history.sql +0 -32
- package/dist/infrastructure/postgres/postgresSnapshotRepository.d.ts +0 -25
- package/dist/infrastructure/postgres/postgresSnapshotRepository.d.ts.map +0 -1
- package/dist/infrastructure/postgres/postgresSnapshotRepository.js +0 -323
- package/dist/infrastructure/postgres/postgresSnapshotRepository.js.map +0 -1
- package/dist/infrastructure/postgres/runMigrations.d.ts +0 -3
- package/dist/infrastructure/postgres/runMigrations.d.ts.map +0 -1
- package/dist/infrastructure/postgres/runMigrations.js +0 -33
- package/dist/infrastructure/postgres/runMigrations.js.map +0 -1
- package/dist/infrastructure/runtime/createSnapshotRuntime.d.ts +0 -17
- package/dist/infrastructure/runtime/createSnapshotRuntime.d.ts.map +0 -1
- package/dist/infrastructure/runtime/createSnapshotRuntime.js +0 -38
- package/dist/infrastructure/runtime/createSnapshotRuntime.js.map +0 -1
- package/dist/tests/canonicalSnapshot.test.d.ts +0 -2
- package/dist/tests/canonicalSnapshot.test.d.ts.map +0 -1
- package/dist/tests/canonicalSnapshot.test.js +0 -425
- package/dist/tests/canonicalSnapshot.test.js.map +0 -1
- package/dist/tests/endpointDiffClassifier.test.d.ts +0 -2
- package/dist/tests/endpointDiffClassifier.test.d.ts.map +0 -1
- package/dist/tests/endpointDiffClassifier.test.js +0 -633
- package/dist/tests/endpointDiffClassifier.test.js.map +0 -1
- package/dist/tests/httpSnapshotTransport.test.d.ts +0 -2
- package/dist/tests/httpSnapshotTransport.test.d.ts.map +0 -1
- package/dist/tests/httpSnapshotTransport.test.js +0 -393
- package/dist/tests/httpSnapshotTransport.test.js.map +0 -1
- package/dist/tests/indexLifecycle.test.d.ts +0 -2
- package/dist/tests/indexLifecycle.test.d.ts.map +0 -1
- package/dist/tests/indexLifecycle.test.js +0 -44
- package/dist/tests/indexLifecycle.test.js.map +0 -1
- package/dist/tests/mcpSnapshotTools.test.d.ts +0 -2
- package/dist/tests/mcpSnapshotTools.test.d.ts.map +0 -1
- package/dist/tests/mcpSnapshotTools.test.js +0 -316
- package/dist/tests/mcpSnapshotTools.test.js.map +0 -1
- package/dist/tests/postgresMigrationSmoke.test.d.ts +0 -2
- package/dist/tests/postgresMigrationSmoke.test.d.ts.map +0 -1
- package/dist/tests/postgresMigrationSmoke.test.js +0 -187
- package/dist/tests/postgresMigrationSmoke.test.js.map +0 -1
- package/dist/tests/realPostgresTestSchema.d.ts +0 -10
- package/dist/tests/realPostgresTestSchema.d.ts.map +0 -1
- package/dist/tests/realPostgresTestSchema.js +0 -73
- package/dist/tests/realPostgresTestSchema.js.map +0 -1
- package/dist/tests/snapshotCapturePipeline.test.d.ts +0 -2
- package/dist/tests/snapshotCapturePipeline.test.d.ts.map +0 -1
- package/dist/tests/snapshotCapturePipeline.test.js +0 -475
- package/dist/tests/snapshotCapturePipeline.test.js.map +0 -1
- package/dist/tests/snapshotDiffContract.test.d.ts +0 -2
- package/dist/tests/snapshotDiffContract.test.d.ts.map +0 -1
- package/dist/tests/snapshotDiffContract.test.js +0 -156
- package/dist/tests/snapshotDiffContract.test.js.map +0 -1
- package/dist/tests/snapshotPersistence.real.test.d.ts +0 -2
- package/dist/tests/snapshotPersistence.real.test.d.ts.map +0 -1
- package/dist/tests/snapshotPersistence.real.test.js +0 -310
- package/dist/tests/snapshotPersistence.real.test.js.map +0 -1
- package/dist/tests/webServerRuntime.test.d.ts +0 -2
- package/dist/tests/webServerRuntime.test.d.ts.map +0 -1
- package/dist/tests/webServerRuntime.test.js +0 -123
- package/dist/tests/webServerRuntime.test.js.map +0 -1
- package/dist/transport/createSnapshotToolRuntime.d.ts +0 -7
- package/dist/transport/createSnapshotToolRuntime.d.ts.map +0 -1
- package/dist/transport/createSnapshotToolRuntime.js +0 -40
- package/dist/transport/createSnapshotToolRuntime.js.map +0 -1
- package/dist/transport/httpSnapshotServer.d.ts +0 -11
- package/dist/transport/httpSnapshotServer.d.ts.map +0 -1
- package/dist/transport/httpSnapshotServer.js +0 -234
- package/dist/transport/httpSnapshotServer.js.map +0 -1
- package/dist/transport/mcpToolRouter.d.ts +0 -81
- package/dist/transport/mcpToolRouter.d.ts.map +0 -1
- package/dist/transport/mcpToolRouter.js +0 -416
- package/dist/transport/mcpToolRouter.js.map +0 -1
- package/dist/transport/webServerRuntime.d.ts +0 -17
- package/dist/transport/webServerRuntime.d.ts.map +0 -1
- package/dist/transport/webServerRuntime.js +0 -73
- package/dist/transport/webServerRuntime.js.map +0 -1
- package/dist/web/dashboard.css +0 -411
- package/dist/web/dashboard.html +0 -141
- package/dist/web/dashboard.js +0 -540
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
import { newDb } from 'pg-mem';
|
|
2
|
-
import { SnapshotCaptureService } from '../application/snapshot/snapshotCaptureService.js';
|
|
3
|
-
import { PostgresSnapshotRepository } from '../infrastructure/postgres/postgresSnapshotRepository.js';
|
|
4
|
-
import { createSnapshotRuntime } from '../infrastructure/runtime/createSnapshotRuntime.js';
|
|
5
|
-
function createDocument(paths, version = '1.0.0') {
|
|
6
|
-
return {
|
|
7
|
-
openapi: '3.0.0',
|
|
8
|
-
info: {
|
|
9
|
-
title: 'Snapshot Pipeline API',
|
|
10
|
-
version,
|
|
11
|
-
},
|
|
12
|
-
paths,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
function createBaselineDocument() {
|
|
16
|
-
return createDocument({
|
|
17
|
-
'/pets': {
|
|
18
|
-
get: {
|
|
19
|
-
responses: {
|
|
20
|
-
'200': {
|
|
21
|
-
description: 'OK',
|
|
22
|
-
content: {
|
|
23
|
-
'application/json': {
|
|
24
|
-
schema: {
|
|
25
|
-
type: 'object',
|
|
26
|
-
required: ['id'],
|
|
27
|
-
properties: {
|
|
28
|
-
id: { type: 'string' },
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
function createChangedDocument() {
|
|
40
|
-
return createDocument({
|
|
41
|
-
'/pets': {
|
|
42
|
-
get: {
|
|
43
|
-
responses: {
|
|
44
|
-
'200': {
|
|
45
|
-
description: 'OK',
|
|
46
|
-
content: {
|
|
47
|
-
'application/json': {
|
|
48
|
-
schema: {
|
|
49
|
-
type: 'object',
|
|
50
|
-
required: ['id'],
|
|
51
|
-
properties: {
|
|
52
|
-
id: { type: 'string' },
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
post: {
|
|
61
|
-
requestBody: {
|
|
62
|
-
required: false,
|
|
63
|
-
content: {
|
|
64
|
-
'application/json': {
|
|
65
|
-
schema: {
|
|
66
|
-
type: 'object',
|
|
67
|
-
properties: {
|
|
68
|
-
name: { type: 'string' },
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
responses: {
|
|
75
|
-
'201': {
|
|
76
|
-
description: 'Created',
|
|
77
|
-
content: {
|
|
78
|
-
'application/json': {
|
|
79
|
-
schema: {
|
|
80
|
-
type: 'object',
|
|
81
|
-
required: ['id'],
|
|
82
|
-
properties: {
|
|
83
|
-
id: { type: 'string' },
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
function createEscalatedChangedDocument() {
|
|
95
|
-
return createDocument({
|
|
96
|
-
'/pets': {
|
|
97
|
-
get: {
|
|
98
|
-
responses: {
|
|
99
|
-
'200': {
|
|
100
|
-
description: 'OK',
|
|
101
|
-
content: {
|
|
102
|
-
'application/json': {
|
|
103
|
-
schema: {
|
|
104
|
-
type: 'object',
|
|
105
|
-
required: ['id'],
|
|
106
|
-
properties: {
|
|
107
|
-
id: { type: 'string' },
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
post: {
|
|
116
|
-
requestBody: {
|
|
117
|
-
required: true,
|
|
118
|
-
content: {
|
|
119
|
-
'application/json': {
|
|
120
|
-
schema: {
|
|
121
|
-
type: 'object',
|
|
122
|
-
properties: {
|
|
123
|
-
name: { type: 'string' },
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
responses: {
|
|
130
|
-
'201': {
|
|
131
|
-
description: 'Created',
|
|
132
|
-
content: {
|
|
133
|
-
'application/json': {
|
|
134
|
-
schema: {
|
|
135
|
-
type: 'object',
|
|
136
|
-
required: ['id'],
|
|
137
|
-
properties: {
|
|
138
|
-
id: { type: 'string' },
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
function createVersionOnlyDocument() {
|
|
150
|
-
return createDocument(createBaselineDocument().paths, '2.0.0');
|
|
151
|
-
}
|
|
152
|
-
function createMockLoader(sequence) {
|
|
153
|
-
let index = 0;
|
|
154
|
-
return {
|
|
155
|
-
loadFresh: jest.fn(() => {
|
|
156
|
-
const safeIndex = Math.min(index, sequence.length - 1);
|
|
157
|
-
const selected = sequence[safeIndex];
|
|
158
|
-
index += 1;
|
|
159
|
-
return Promise.resolve(selected);
|
|
160
|
-
}),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
async function createHarness(sequence) {
|
|
164
|
-
const db = newDb({
|
|
165
|
-
autoCreateForeignKeyIndices: true,
|
|
166
|
-
noAstCoverageCheck: true,
|
|
167
|
-
});
|
|
168
|
-
const pgAdapter = db.adapters.createPg();
|
|
169
|
-
const client = new pgAdapter.Client();
|
|
170
|
-
await client.connect();
|
|
171
|
-
const repository = new PostgresSnapshotRepository(client);
|
|
172
|
-
const loader = createMockLoader(sequence);
|
|
173
|
-
const service = new SnapshotCaptureService(repository, loader, () => new Date('2026-03-09T10:00:00.000Z'));
|
|
174
|
-
return {
|
|
175
|
-
client,
|
|
176
|
-
repository,
|
|
177
|
-
loader,
|
|
178
|
-
service,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
const baseEnv = {
|
|
182
|
-
DATABASE_URL: 'postgresql://localhost:5432/snapshot_diff',
|
|
183
|
-
PROJECT_KEY: 'frontend-diff',
|
|
184
|
-
SWAGGER_URL: 'https://api.example.com/v1/openapi.json',
|
|
185
|
-
};
|
|
186
|
-
const secondarySourceEnv = {
|
|
187
|
-
...baseEnv,
|
|
188
|
-
SWAGGER_URL: 'https://api.example.com/v2/openapi.json',
|
|
189
|
-
};
|
|
190
|
-
describe('Slice 4: snapshot capture pipeline + postgres repository', () => {
|
|
191
|
-
it('should support bootstrap -> capture -> compare -> replay flow with persisted snapshots', async () => {
|
|
192
|
-
const harness = await createHarness([createBaselineDocument(), createChangedDocument()]);
|
|
193
|
-
try {
|
|
194
|
-
const bootstrap = await harness.service.bootstrap(baseEnv);
|
|
195
|
-
expect(bootstrap.project.projectKey).toBe('frontend-diff');
|
|
196
|
-
expect(bootstrap.source.swaggerUrl).toBe('https://api.example.com/v1/openapi.json');
|
|
197
|
-
const firstCapture = await harness.service.capture(baseEnv);
|
|
198
|
-
expect(firstCapture.previousSnapshot).toBeNull();
|
|
199
|
-
expect(firstCapture.isNoopCapture).toBe(false);
|
|
200
|
-
expect(firstCapture.diffResult.summary.hasChanges).toBe(false);
|
|
201
|
-
expect(firstCapture.diffResult.counts.total).toBe(0);
|
|
202
|
-
const secondCapture = await harness.service.capture(baseEnv);
|
|
203
|
-
expect(secondCapture.previousSnapshot?.id).toBe(firstCapture.snapshot.id);
|
|
204
|
-
expect(secondCapture.snapshot.id).not.toBe(firstCapture.snapshot.id);
|
|
205
|
-
expect(secondCapture.isNoopCapture).toBe(false);
|
|
206
|
-
expect(secondCapture.diffResult.summary.baselineSnapshotId).toBe(firstCapture.snapshot.id);
|
|
207
|
-
expect(secondCapture.diffResult.summary.targetSnapshotId).toBe(secondCapture.snapshot.id);
|
|
208
|
-
expect(secondCapture.diffResult.summary.hasChanges).toBe(true);
|
|
209
|
-
expect(secondCapture.diffResult.counts.total).toBe(1);
|
|
210
|
-
expect(secondCapture.diffResult.changes[0].changeReason).toBe('method_added');
|
|
211
|
-
const history = await harness.service.listEndpointChangeHistory({
|
|
212
|
-
sourceId: secondCapture.source.id,
|
|
213
|
-
});
|
|
214
|
-
expect(history).toHaveLength(1);
|
|
215
|
-
expect(history[0]).toMatchObject({
|
|
216
|
-
baselineSnapshotId: firstCapture.snapshot.id,
|
|
217
|
-
targetSnapshotId: secondCapture.snapshot.id,
|
|
218
|
-
path: '/pets',
|
|
219
|
-
method: 'post',
|
|
220
|
-
classification: 'non-breaking',
|
|
221
|
-
changeReason: 'method_added',
|
|
222
|
-
});
|
|
223
|
-
const replayed = await harness.service.replayDiff({
|
|
224
|
-
baselineSnapshotId: firstCapture.snapshot.id,
|
|
225
|
-
targetSnapshotId: secondCapture.snapshot.id,
|
|
226
|
-
});
|
|
227
|
-
expect(replayed.counts).toEqual(secondCapture.diffResult.counts);
|
|
228
|
-
expect(replayed.changes).toEqual(secondCapture.diffResult.changes);
|
|
229
|
-
expect(replayed.summary.baselineSnapshotId).toBe(firstCapture.snapshot.id);
|
|
230
|
-
expect(replayed.summary.targetSnapshotId).toBe(secondCapture.snapshot.id);
|
|
231
|
-
const snapshots = await harness.repository.listSnapshotsBySource(secondCapture.source.id);
|
|
232
|
-
expect(snapshots).toHaveLength(2);
|
|
233
|
-
}
|
|
234
|
-
finally {
|
|
235
|
-
await harness.client.end();
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
it('should fail replay when baseline/target snapshots are missing or belong to different sources', async () => {
|
|
239
|
-
const harness = await createHarness([createBaselineDocument(), createChangedDocument(), createBaselineDocument()]);
|
|
240
|
-
try {
|
|
241
|
-
await harness.service.bootstrap(baseEnv);
|
|
242
|
-
await expect(harness.service.replayDiff({
|
|
243
|
-
baselineSnapshotId: '99999',
|
|
244
|
-
targetSnapshotId: '99998',
|
|
245
|
-
})).rejects.toThrow('Baseline snapshot not found');
|
|
246
|
-
const firstCapture = await harness.service.capture(baseEnv);
|
|
247
|
-
await expect(harness.service.replayDiff({
|
|
248
|
-
baselineSnapshotId: firstCapture.snapshot.id,
|
|
249
|
-
targetSnapshotId: '99997',
|
|
250
|
-
})).rejects.toThrow('Target snapshot not found');
|
|
251
|
-
const secondSourceCapture = await harness.service.capture(secondarySourceEnv);
|
|
252
|
-
await expect(harness.service.replayDiff({
|
|
253
|
-
baselineSnapshotId: firstCapture.snapshot.id,
|
|
254
|
-
targetSnapshotId: secondSourceCapture.snapshot.id,
|
|
255
|
-
})).rejects.toThrow('Cannot replay diff across different sources.');
|
|
256
|
-
}
|
|
257
|
-
finally {
|
|
258
|
-
await harness.client.end();
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
it('should return no-op on recapture when spec hash is unchanged and avoid creating new snapshots', async () => {
|
|
262
|
-
const harness = await createHarness([createBaselineDocument(), createBaselineDocument()]);
|
|
263
|
-
try {
|
|
264
|
-
const firstCapture = await harness.service.capture(baseEnv);
|
|
265
|
-
const secondCapture = await harness.service.capture(baseEnv);
|
|
266
|
-
expect(secondCapture.isNoopCapture).toBe(true);
|
|
267
|
-
expect(secondCapture.snapshot.id).toBe(firstCapture.snapshot.id);
|
|
268
|
-
expect(secondCapture.previousSnapshot?.id).toBe(firstCapture.snapshot.id);
|
|
269
|
-
expect(secondCapture.diffResult.summary.isNoopCapture).toBe(true);
|
|
270
|
-
expect(secondCapture.diffResult.summary.baselineSnapshotId).toBe(firstCapture.snapshot.id);
|
|
271
|
-
expect(secondCapture.diffResult.summary.targetSnapshotId).toBe(firstCapture.snapshot.id);
|
|
272
|
-
expect(secondCapture.diffResult.counts.total).toBe(0);
|
|
273
|
-
expect(secondCapture.diffResult.changes).toEqual([]);
|
|
274
|
-
const snapshots = await harness.repository.listSnapshotsBySource(firstCapture.source.id);
|
|
275
|
-
expect(snapshots).toHaveLength(1);
|
|
276
|
-
const history = await harness.service.listEndpointChangeHistory({
|
|
277
|
-
sourceId: firstCapture.source.id,
|
|
278
|
-
});
|
|
279
|
-
expect(history).toHaveLength(0);
|
|
280
|
-
expect(harness.loader.loadFresh).toHaveBeenCalledTimes(2);
|
|
281
|
-
}
|
|
282
|
-
finally {
|
|
283
|
-
await harness.client.end();
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
it('should persist version-only transitions without endpoint history rows', async () => {
|
|
287
|
-
const harness = await createHarness([createBaselineDocument(), createVersionOnlyDocument()]);
|
|
288
|
-
try {
|
|
289
|
-
const firstCapture = await harness.service.capture(baseEnv);
|
|
290
|
-
const secondCapture = await harness.service.capture(baseEnv);
|
|
291
|
-
expect(secondCapture.snapshot.id).not.toBe(firstCapture.snapshot.id);
|
|
292
|
-
expect(secondCapture.diffResult.counts.total).toBe(0);
|
|
293
|
-
expect(secondCapture.diffResult.warnings.length).toBeGreaterThan(0);
|
|
294
|
-
const history = await harness.service.listEndpointChangeHistory({
|
|
295
|
-
sourceId: secondCapture.source.id,
|
|
296
|
-
});
|
|
297
|
-
expect(history).toHaveLength(0);
|
|
298
|
-
}
|
|
299
|
-
finally {
|
|
300
|
-
await harness.client.end();
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
it('should return filtered endpoint timeline latest-first', async () => {
|
|
304
|
-
const harness = await createHarness([
|
|
305
|
-
createBaselineDocument(),
|
|
306
|
-
createChangedDocument(),
|
|
307
|
-
createEscalatedChangedDocument(),
|
|
308
|
-
]);
|
|
309
|
-
try {
|
|
310
|
-
await harness.service.capture(baseEnv);
|
|
311
|
-
const secondCapture = await harness.service.capture(baseEnv);
|
|
312
|
-
const thirdCapture = await harness.service.capture(baseEnv);
|
|
313
|
-
const timeline = await harness.service.listEndpointChangeHistory({
|
|
314
|
-
sourceId: thirdCapture.source.id,
|
|
315
|
-
path: '/pets',
|
|
316
|
-
method: 'post',
|
|
317
|
-
});
|
|
318
|
-
expect(timeline).toHaveLength(2);
|
|
319
|
-
expect(timeline.map(event => event.targetSnapshotId)).toEqual([
|
|
320
|
-
thirdCapture.snapshot.id,
|
|
321
|
-
secondCapture.snapshot.id,
|
|
322
|
-
]);
|
|
323
|
-
expect(timeline.map(event => event.changeReason)).toEqual(['request_optional_to_required', 'method_added']);
|
|
324
|
-
}
|
|
325
|
-
finally {
|
|
326
|
-
await harness.client.end();
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
it('should avoid duplicate snapshot rows under concurrent capture for the same source and spec hash', async () => {
|
|
330
|
-
const harness = await createHarness([createBaselineDocument(), createChangedDocument(), createChangedDocument()]);
|
|
331
|
-
try {
|
|
332
|
-
await harness.service.capture(baseEnv);
|
|
333
|
-
const [left, right] = await Promise.all([harness.service.capture(baseEnv), harness.service.capture(baseEnv)]);
|
|
334
|
-
const snapshots = await harness.repository.listSnapshotsBySource(left.source.id);
|
|
335
|
-
expect(snapshots).toHaveLength(2);
|
|
336
|
-
expect(left.snapshot.id).toBe(right.snapshot.id);
|
|
337
|
-
expect(snapshots.map(snapshot => snapshot.id)).toContain(left.snapshot.id);
|
|
338
|
-
expect(left.snapshot.specHash).toBe(right.snapshot.specHash);
|
|
339
|
-
const history = await harness.service.listEndpointChangeHistory({
|
|
340
|
-
sourceId: left.source.id,
|
|
341
|
-
});
|
|
342
|
-
expect(history).toHaveLength(1);
|
|
343
|
-
expect(history[0].targetSnapshotId).toBe(left.snapshot.id);
|
|
344
|
-
}
|
|
345
|
-
finally {
|
|
346
|
-
await harness.client.end();
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
it('should preserve replay semantics after service restart on the same database', async () => {
|
|
350
|
-
const db = newDb({
|
|
351
|
-
autoCreateForeignKeyIndices: true,
|
|
352
|
-
noAstCoverageCheck: true,
|
|
353
|
-
});
|
|
354
|
-
const pgAdapter = db.adapters.createPg();
|
|
355
|
-
const firstClient = new pgAdapter.Client();
|
|
356
|
-
await firstClient.connect();
|
|
357
|
-
let firstCaptureSnapshotId = '';
|
|
358
|
-
let secondCaptureSnapshotId = '';
|
|
359
|
-
let sourceId = '';
|
|
360
|
-
let expectedCounts = null;
|
|
361
|
-
try {
|
|
362
|
-
const firstRepository = new PostgresSnapshotRepository(firstClient);
|
|
363
|
-
const firstLoader = createMockLoader([createBaselineDocument(), createChangedDocument()]);
|
|
364
|
-
const firstService = new SnapshotCaptureService(firstRepository, firstLoader, () => new Date('2026-03-09T10:00:00.000Z'));
|
|
365
|
-
const firstCapture = await firstService.capture(baseEnv);
|
|
366
|
-
const secondCapture = await firstService.capture(baseEnv);
|
|
367
|
-
firstCaptureSnapshotId = firstCapture.snapshot.id;
|
|
368
|
-
secondCaptureSnapshotId = secondCapture.snapshot.id;
|
|
369
|
-
sourceId = secondCapture.source.id;
|
|
370
|
-
expectedCounts = secondCapture.diffResult.counts;
|
|
371
|
-
}
|
|
372
|
-
finally {
|
|
373
|
-
await firstClient.end();
|
|
374
|
-
}
|
|
375
|
-
const secondClient = new pgAdapter.Client();
|
|
376
|
-
await secondClient.connect();
|
|
377
|
-
try {
|
|
378
|
-
const restartedRepository = new PostgresSnapshotRepository(secondClient);
|
|
379
|
-
const restartedLoader = createMockLoader([createChangedDocument()]);
|
|
380
|
-
const restartedService = new SnapshotCaptureService(restartedRepository, restartedLoader, () => new Date('2026-03-09T10:00:00.000Z'));
|
|
381
|
-
const replayed = await restartedService.replayDiff({
|
|
382
|
-
baselineSnapshotId: firstCaptureSnapshotId,
|
|
383
|
-
targetSnapshotId: secondCaptureSnapshotId,
|
|
384
|
-
});
|
|
385
|
-
expect(replayed.counts).toEqual(expectedCounts);
|
|
386
|
-
expect(replayed.summary.baselineSnapshotId).toBe(firstCaptureSnapshotId);
|
|
387
|
-
expect(replayed.summary.targetSnapshotId).toBe(secondCaptureSnapshotId);
|
|
388
|
-
const snapshots = await restartedService.listSnapshotsBySource(sourceId);
|
|
389
|
-
expect(snapshots).toHaveLength(2);
|
|
390
|
-
expect(snapshots.map(snapshot => snapshot.id)).toEqual(expect.arrayContaining([firstCaptureSnapshotId, secondCaptureSnapshotId]));
|
|
391
|
-
}
|
|
392
|
-
finally {
|
|
393
|
-
await secondClient.end();
|
|
394
|
-
}
|
|
395
|
-
});
|
|
396
|
-
it('should preserve replay semantics after runtime restart via createSnapshotRuntime', async () => {
|
|
397
|
-
const db = newDb({
|
|
398
|
-
autoCreateForeignKeyIndices: true,
|
|
399
|
-
noAstCoverageCheck: true,
|
|
400
|
-
});
|
|
401
|
-
const pgAdapter = db.adapters.createPg();
|
|
402
|
-
const createDependencyFactory = (documents) => {
|
|
403
|
-
const loader = createMockLoader(documents);
|
|
404
|
-
return async () => {
|
|
405
|
-
const client = new pgAdapter.Client();
|
|
406
|
-
await client.connect();
|
|
407
|
-
return {
|
|
408
|
-
pool: {
|
|
409
|
-
end: async () => {
|
|
410
|
-
await client.end();
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
repository: new PostgresSnapshotRepository(client),
|
|
414
|
-
loader,
|
|
415
|
-
};
|
|
416
|
-
};
|
|
417
|
-
};
|
|
418
|
-
const runtimeA = await createSnapshotRuntime(baseEnv, createDependencyFactory([createBaselineDocument(), createChangedDocument()]));
|
|
419
|
-
let baselineSnapshotId = '';
|
|
420
|
-
let targetSnapshotId = '';
|
|
421
|
-
let sourceId = '';
|
|
422
|
-
let expectedCounts = null;
|
|
423
|
-
try {
|
|
424
|
-
const firstCapture = await runtimeA.service.capture(baseEnv);
|
|
425
|
-
const secondCapture = await runtimeA.service.capture(baseEnv);
|
|
426
|
-
baselineSnapshotId = firstCapture.snapshot.id;
|
|
427
|
-
targetSnapshotId = secondCapture.snapshot.id;
|
|
428
|
-
sourceId = secondCapture.source.id;
|
|
429
|
-
expectedCounts = secondCapture.diffResult.counts;
|
|
430
|
-
}
|
|
431
|
-
finally {
|
|
432
|
-
await runtimeA.dispose();
|
|
433
|
-
}
|
|
434
|
-
const runtimeB = await createSnapshotRuntime(baseEnv, createDependencyFactory([createChangedDocument()]));
|
|
435
|
-
try {
|
|
436
|
-
const replayed = await runtimeB.service.replayDiff({
|
|
437
|
-
baselineSnapshotId,
|
|
438
|
-
targetSnapshotId,
|
|
439
|
-
});
|
|
440
|
-
expect(replayed.counts).toEqual(expectedCounts);
|
|
441
|
-
expect(replayed.summary.baselineSnapshotId).toBe(baselineSnapshotId);
|
|
442
|
-
expect(replayed.summary.targetSnapshotId).toBe(targetSnapshotId);
|
|
443
|
-
const snapshots = await runtimeB.service.listSnapshotsBySource(sourceId);
|
|
444
|
-
expect(snapshots).toHaveLength(2);
|
|
445
|
-
}
|
|
446
|
-
finally {
|
|
447
|
-
await runtimeB.dispose();
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
it('should dispose pool when runtime bootstrap fails', async () => {
|
|
451
|
-
const disposeMock = jest.fn(() => Promise.resolve(undefined));
|
|
452
|
-
const runtimeFactory = () => ({
|
|
453
|
-
pool: {
|
|
454
|
-
end: disposeMock,
|
|
455
|
-
},
|
|
456
|
-
repository: {
|
|
457
|
-
initializeSchema: jest.fn(() => Promise.reject(new Error('initialize failed'))),
|
|
458
|
-
ensureProject: jest.fn(),
|
|
459
|
-
ensureSource: jest.fn(),
|
|
460
|
-
getSourceIdentityById: jest.fn(),
|
|
461
|
-
getLatestSnapshot: jest.fn(),
|
|
462
|
-
saveSnapshot: jest.fn(),
|
|
463
|
-
getSnapshotById: jest.fn(),
|
|
464
|
-
listSnapshotsBySource: jest.fn(),
|
|
465
|
-
listEndpointChangeHistory: jest.fn(),
|
|
466
|
-
},
|
|
467
|
-
loader: {
|
|
468
|
-
loadFresh: jest.fn(),
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
await expect(createSnapshotRuntime(baseEnv, runtimeFactory)).rejects.toThrow('initialize failed');
|
|
472
|
-
expect(disposeMock).toHaveBeenCalledTimes(1);
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
//# sourceMappingURL=snapshotCapturePipeline.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"snapshotCapturePipeline.test.js","sourceRoot":"","sources":["../../src/tests/snapshotCapturePipeline.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"snapshotDiffContract.test.d.ts","sourceRoot":"","sources":["../../src/tests/snapshotDiffContract.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { snapshotDiffResultSchema } from '../domain/contracts/snapshotDiffContract.js';
|
|
4
|
-
import { EnvironmentValidationError, validateEnvironment } from '../domain/contracts/runtimeEnvironmentContract.js';
|
|
5
|
-
const TEST_FILE_DIRECTORY = __dirname;
|
|
6
|
-
const FIXTURE_DIRECTORY = path.join(TEST_FILE_DIRECTORY, 'fixtures', 'snapshotDiff');
|
|
7
|
-
const FIXTURE_MATRIX_PATH = path.join(FIXTURE_DIRECTORY, 'fixture-matrix.json');
|
|
8
|
-
function readJsonFixture(fixturePath) {
|
|
9
|
-
return JSON.parse(fs.readFileSync(fixturePath, 'utf8'));
|
|
10
|
-
}
|
|
11
|
-
describe('Slice 1: Snapshot/Diff contract baseline', () => {
|
|
12
|
-
it('should provide a fixture matrix that covers acceptance 1 through 7', () => {
|
|
13
|
-
const matrix = readJsonFixture(FIXTURE_MATRIX_PATH);
|
|
14
|
-
const acceptanceIds = matrix.map(entry => entry.acceptanceId).sort((left, right) => left - right);
|
|
15
|
-
expect(matrix).toHaveLength(7);
|
|
16
|
-
expect(acceptanceIds).toEqual([1, 2, 3, 4, 5, 6, 7]);
|
|
17
|
-
matrix.forEach(entry => {
|
|
18
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, entry.fixtureFile);
|
|
19
|
-
expect(fs.existsSync(fixturePath)).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
it('should lock summary/counts/changes/warnings/changeReason contract for all fixtures', () => {
|
|
23
|
-
const matrix = readJsonFixture(FIXTURE_MATRIX_PATH);
|
|
24
|
-
matrix.forEach(entry => {
|
|
25
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, entry.fixtureFile);
|
|
26
|
-
const fixture = readJsonFixture(fixturePath);
|
|
27
|
-
const parsed = snapshotDiffResultSchema.parse(fixture);
|
|
28
|
-
expect(parsed.summary).toBeDefined();
|
|
29
|
-
expect(parsed.counts).toBeDefined();
|
|
30
|
-
expect(parsed.changes).toBeDefined();
|
|
31
|
-
expect(parsed.warnings).toBeDefined();
|
|
32
|
-
parsed.changes.forEach(change => {
|
|
33
|
-
expect(change.changeReason).toBeTruthy();
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
it('should keep version-only fixture warning without endpoint changes', () => {
|
|
38
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, 'acceptance-04-version-only-warning.json');
|
|
39
|
-
const fixture = readJsonFixture(fixturePath);
|
|
40
|
-
const parsed = snapshotDiffResultSchema.parse(fixture);
|
|
41
|
-
expect(parsed.counts.total).toBe(0);
|
|
42
|
-
expect(parsed.changes).toHaveLength(0);
|
|
43
|
-
expect(parsed.warnings.some(warning => warning.code === 'VERSION_ONLY_CHANGE')).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it('should keep no-op recapture fixture as no changes', () => {
|
|
46
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, 'acceptance-06-noop-recapture.json');
|
|
47
|
-
const fixture = readJsonFixture(fixturePath);
|
|
48
|
-
const parsed = snapshotDiffResultSchema.parse(fixture);
|
|
49
|
-
expect(parsed.summary.isNoopCapture).toBe(true);
|
|
50
|
-
expect(parsed.summary.hasChanges).toBe(false);
|
|
51
|
-
expect(parsed.counts.total).toBe(0);
|
|
52
|
-
});
|
|
53
|
-
it('should reject unexpected top-level fields to prevent contract drift', () => {
|
|
54
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, 'acceptance-07-contract-shape-stability.json');
|
|
55
|
-
const fixture = readJsonFixture(fixturePath);
|
|
56
|
-
const parsed = snapshotDiffResultSchema.safeParse({
|
|
57
|
-
...fixture,
|
|
58
|
-
unexpectedTopLevelField: 'drift',
|
|
59
|
-
});
|
|
60
|
-
expect(parsed.success).toBe(false);
|
|
61
|
-
if (!parsed.success) {
|
|
62
|
-
expect(parsed.error.issues.some(issue => issue.code === 'unrecognized_keys')).toBe(true);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
it('should reject unexpected nested fields in change rows to prevent contract drift', () => {
|
|
66
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, 'acceptance-07-contract-shape-stability.json');
|
|
67
|
-
const fixture = readJsonFixture(fixturePath);
|
|
68
|
-
const parsed = snapshotDiffResultSchema.safeParse({
|
|
69
|
-
...fixture,
|
|
70
|
-
changes: fixture.changes.map((change, index) => index === 0 ? { ...change, unexpectedChangeField: 'drift' } : change),
|
|
71
|
-
});
|
|
72
|
-
expect(parsed.success).toBe(false);
|
|
73
|
-
if (!parsed.success) {
|
|
74
|
-
expect(parsed.error.issues.some(issue => issue.code === 'unrecognized_keys')).toBe(true);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
it('should fail when counts do not match changes classification distribution', () => {
|
|
78
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, 'acceptance-07-contract-shape-stability.json');
|
|
79
|
-
const fixture = readJsonFixture(fixturePath);
|
|
80
|
-
const parsed = snapshotDiffResultSchema.safeParse({
|
|
81
|
-
...fixture,
|
|
82
|
-
counts: {
|
|
83
|
-
...fixture.counts,
|
|
84
|
-
breaking: fixture.counts.breaking - 1,
|
|
85
|
-
nonBreaking: fixture.counts.nonBreaking + 1,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
expect(parsed.success).toBe(false);
|
|
89
|
-
if (!parsed.success) {
|
|
90
|
-
expect(parsed.error.issues.some(issue => issue.message.includes('counts.breaking must equal number of changes classified as breaking'))).toBe(true);
|
|
91
|
-
expect(parsed.error.issues.some(issue => issue.message.includes('counts.nonBreaking must equal number of changes classified as non-breaking'))).toBe(true);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
it('should allow trace as a valid OpenAPI HTTP method', () => {
|
|
95
|
-
const fixturePath = path.join(FIXTURE_DIRECTORY, 'acceptance-07-contract-shape-stability.json');
|
|
96
|
-
const fixture = readJsonFixture(fixturePath);
|
|
97
|
-
const parsed = snapshotDiffResultSchema.safeParse({
|
|
98
|
-
...fixture,
|
|
99
|
-
changes: fixture.changes.map((change, index) => (index === 0 ? { ...change, method: 'trace' } : change)),
|
|
100
|
-
});
|
|
101
|
-
expect(parsed.success).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
describe('Slice 1: Environment startup contract', () => {
|
|
105
|
-
it('should accept required env values when all keys are configured', () => {
|
|
106
|
-
const validated = validateEnvironment({
|
|
107
|
-
DATABASE_URL: 'postgresql://localhost:5432/swagger_diff',
|
|
108
|
-
PROJECT_KEY: 'frontend-diff',
|
|
109
|
-
SWAGGER_URL: 'https://api.example.com/openapi.json',
|
|
110
|
-
});
|
|
111
|
-
expect(validated.DATABASE_URL).toContain('postgresql://');
|
|
112
|
-
expect(validated.PROJECT_KEY).toBe('frontend-diff');
|
|
113
|
-
expect(validated.SWAGGER_URL).toBe('https://api.example.com/openapi.json');
|
|
114
|
-
});
|
|
115
|
-
it('should fail startup when required env keys are missing', () => {
|
|
116
|
-
expect(() => {
|
|
117
|
-
validateEnvironment({
|
|
118
|
-
PROJECT_KEY: 'frontend-diff',
|
|
119
|
-
});
|
|
120
|
-
}).toThrow('Missing required environment variables: DATABASE_URL, SWAGGER_URL');
|
|
121
|
-
});
|
|
122
|
-
it('should fail startup when DATABASE_URL protocol is misconfigured', () => {
|
|
123
|
-
let caughtError;
|
|
124
|
-
try {
|
|
125
|
-
validateEnvironment({
|
|
126
|
-
DATABASE_URL: 'mysql://localhost:3306/swagger_diff',
|
|
127
|
-
PROJECT_KEY: 'frontend-diff',
|
|
128
|
-
SWAGGER_URL: 'https://api.example.com/openapi.json',
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
caughtError = error;
|
|
133
|
-
}
|
|
134
|
-
expect(caughtError).toBeInstanceOf(EnvironmentValidationError);
|
|
135
|
-
expect(caughtError.message).toContain('Invalid environment configuration: DATABASE_URL: DATABASE_URL must use postgres:// or postgresql://');
|
|
136
|
-
});
|
|
137
|
-
it('should fail startup when SWAGGER_URL protocol is misconfigured', () => {
|
|
138
|
-
expect(() => {
|
|
139
|
-
validateEnvironment({
|
|
140
|
-
DATABASE_URL: 'postgres://localhost:5432/swagger_diff',
|
|
141
|
-
PROJECT_KEY: 'frontend-diff',
|
|
142
|
-
SWAGGER_URL: 'ftp://api.example.com/openapi.json',
|
|
143
|
-
});
|
|
144
|
-
}).toThrow('SWAGGER_URL must use http:// or https://');
|
|
145
|
-
});
|
|
146
|
-
it('should fail startup when PROJECT_KEY format is invalid', () => {
|
|
147
|
-
expect(() => {
|
|
148
|
-
validateEnvironment({
|
|
149
|
-
DATABASE_URL: 'postgres://localhost:5432/swagger_diff',
|
|
150
|
-
PROJECT_KEY: 'invalid key with spaces',
|
|
151
|
-
SWAGGER_URL: 'https://api.example.com/openapi.json',
|
|
152
|
-
});
|
|
153
|
-
}).toThrow('PROJECT_KEY can only contain letters, numbers, dot, underscore, and hyphen');
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
//# sourceMappingURL=snapshotDiffContract.test.js.map
|