Skip to content

Commit 28d7ccf

Browse files
committed
run diff based on yaml files
1 parent 3e36efb commit 28d7ccf

File tree

2 files changed

+155
-57
lines changed

2 files changed

+155
-57
lines changed

src/diff/oasDiff.test.ts

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,26 @@ function setupDirectoryStructure(
7575
},
7676
structure: Array<{ path: string; contents: string[] }>
7777
): void {
78-
let callIndex = 0;
79-
8078
for (const dir of structure) {
81-
fsStub.readdir.onCall(callIndex++).returns(dir.contents);
79+
fsStub.readdir.withArgs(dir.path).resolves(dir.contents);
8280

8381
for (const item of dir.contents) {
8482
const itemPath = `${dir.path}/${item}`;
85-
const isFile = item.endsWith(".json") || item.endsWith(".yaml");
86-
fsStub.stat.withArgs(itemPath).returns({ isDirectory: () => !isFile });
83+
const isFile =
84+
item.endsWith(".json") ||
85+
item.endsWith(".yaml") ||
86+
item.endsWith(".yml");
87+
fsStub.stat.withArgs(itemPath).resolves({
88+
isDirectory: () => !isFile,
89+
isFile: () => isFile,
90+
});
8791
}
8892
}
8993

90-
fsStub.stat.returns({ isDirectory: () => true });
94+
fsStub.stat.resolves({
95+
isDirectory: () => true,
96+
isFile: () => false,
97+
});
9198
}
9299

93100
function createOasDiffProxy(
@@ -264,7 +271,9 @@ describe("oasDiffChangelog", () => {
264271
const result = await oasDiff.oasDiffChangelog(baseApi, newApi, flags);
265272

266273
expect(execStub.called).to.be.true;
267-
expect(execStub.args[1][0]).to.include("base/api-v1/**/*.yaml");
274+
expect(execStub.args[1][0]).to.equal(
275+
'oasdiff changelog "base/api-v1/spec.yaml" "new/api-v1/spec.yaml"'
276+
);
268277
expect(result).to.equal(0);
269278
});
270279

@@ -307,7 +316,7 @@ describe("oasDiffChangelog", () => {
307316

308317
expect(execStub.called).to.be.true;
309318
expect(execStub.args[1][0]).to.equal(
310-
'oasdiff changelog --composed "base/api-v1/**/*.yaml" "new/api-v1/**/*.yaml"'
319+
'oasdiff changelog "base/api-v1/spec.yaml" "new/api-v1/spec.yaml"'
311320
);
312321
});
313322

@@ -319,11 +328,11 @@ describe("oasDiffChangelog", () => {
319328
const fsStub = createMockFs();
320329
setupDirectoryStructure(fsStub, [
321330
{ path: "base", contents: ["api-v1", "api-v2"] },
322-
{ path: "base/api-v1", contents: ["exchange.json"] },
323-
{ path: "base/api-v2", contents: ["exchange.json"] },
331+
{ path: "base/api-v1", contents: ["exchange.json", "spec.yaml"] },
332+
{ path: "base/api-v2", contents: ["exchange.json", "spec.yaml"] },
324333
{ path: "new", contents: ["api-v1", "api-v2"] },
325-
{ path: "new/api-v1", contents: ["exchange.json"] },
326-
{ path: "new/api-v2", contents: ["exchange.json"] },
334+
{ path: "new/api-v1", contents: ["exchange.json", "spec.yaml"] },
335+
{ path: "new/api-v2", contents: ["exchange.json", "spec.yaml"] },
327336
]);
328337

329338
const oasDiff = createOasDiffProxy(execStub, fsStub);
@@ -357,11 +366,11 @@ describe("oasDiffChangelog", () => {
357366
const fsStub = createMockFs();
358367
setupDirectoryStructure(fsStub, [
359368
{ path: "base", contents: ["api-v1", "api-v2"] },
360-
{ path: "base/api-v1", contents: ["exchange.json"] },
361-
{ path: "base/api-v2", contents: ["exchange.json"] },
369+
{ path: "base/api-v1", contents: ["exchange.json", "spec.yaml"] },
370+
{ path: "base/api-v2", contents: ["exchange.json", "spec.yaml"] },
362371
{ path: "new", contents: ["api-v1", "api-v2"] },
363-
{ path: "new/api-v1", contents: ["exchange.json"] },
364-
{ path: "new/api-v2", contents: ["exchange.json"] },
372+
{ path: "new/api-v1", contents: ["exchange.json", "spec.yaml"] },
373+
{ path: "new/api-v2", contents: ["exchange.json", "spec.yaml"] },
365374
]);
366375

367376
const oasDiff = createOasDiffProxy(execStub, fsStub);
@@ -452,11 +461,11 @@ describe("oasDiffChangelog", () => {
452461
const fsStub = createMockFs();
453462
setupDirectoryStructure(fsStub, [
454463
{ path: "base", contents: ["api-v1", "api-v2"] },
455-
{ path: "base/api-v1", contents: ["exchange.json"] },
456-
{ path: "base/api-v2", contents: ["exchange.json"] },
464+
{ path: "base/api-v1", contents: ["exchange.json", "spec.yaml"] },
465+
{ path: "base/api-v2", contents: ["exchange.json", "spec.yaml"] },
457466
{ path: "new", contents: ["api-v2", "api-v3"] },
458-
{ path: "new/api-v2", contents: ["exchange.json"] },
459-
{ path: "new/api-v3", contents: ["exchange.json"] },
467+
{ path: "new/api-v2", contents: ["exchange.json", "spec.yaml"] },
468+
{ path: "new/api-v3", contents: ["exchange.json", "spec.yaml"] },
460469
]);
461470

462471
const oasDiff = createOasDiffProxy(execStub, fsStub);
@@ -485,13 +494,13 @@ describe("oasDiffChangelog", () => {
485494
const fsStub = createMockFs();
486495
setupDirectoryStructure(fsStub, [
487496
{ path: "base", contents: ["common-api", "stable-api", "old-api"] },
488-
{ path: "base/common-api", contents: ["exchange.json"] },
489-
{ path: "base/stable-api", contents: ["exchange.json"] },
490-
{ path: "base/old-api", contents: ["exchange.json"] },
497+
{ path: "base/common-api", contents: ["exchange.json", "spec.yaml"] },
498+
{ path: "base/stable-api", contents: ["exchange.json", "spec.yaml"] },
499+
{ path: "base/old-api", contents: ["exchange.json", "spec.yaml"] },
491500
{ path: "new", contents: ["common-api", "stable-api", "new-api"] },
492-
{ path: "new/common-api", contents: ["exchange.json"] },
493-
{ path: "new/stable-api", contents: ["exchange.json"] },
494-
{ path: "new/new-api", contents: ["exchange.json"] },
501+
{ path: "new/common-api", contents: ["exchange.json", "spec.yaml"] },
502+
{ path: "new/stable-api", contents: ["exchange.json", "spec.yaml"] },
503+
{ path: "new/new-api", contents: ["exchange.json", "spec.yaml"] },
495504
]);
496505

497506
const oasDiff = createOasDiffProxy(execStub, fsStub);
@@ -599,16 +608,16 @@ describe("oasDiffChangelog", () => {
599608
execStub
600609
.onSecondCall()
601610
.callsArgWith(1, null, '[{"changes": "in api-v1"}]', "");
602-
execStub.onThirdCall().callsArgWith(1, null, "[]", ""); // empty array
611+
execStub.onThirdCall().callsArgWith(1, null, "[]", "");
603612

604613
const fsStub = createMockFs();
605614
setupDirectoryStructure(fsStub, [
606615
{ path: "base", contents: ["api-v1", "api-v2"] },
607-
{ path: "base/api-v1", contents: ["exchange.json"] },
608-
{ path: "base/api-v2", contents: ["exchange.json"] },
616+
{ path: "base/api-v1", contents: ["exchange.json", "spec.yaml"] },
617+
{ path: "base/api-v2", contents: ["exchange.json", "spec.yaml"] },
609618
{ path: "new", contents: ["api-v1", "api-v2"] },
610-
{ path: "new/api-v1", contents: ["exchange.json"] },
611-
{ path: "new/api-v2", contents: ["exchange.json"] },
619+
{ path: "new/api-v1", contents: ["exchange.json", "spec.yaml"] },
620+
{ path: "new/api-v2", contents: ["exchange.json", "spec.yaml"] },
612621
]);
613622

614623
const oasDiff = createOasDiffProxy(execStub, fsStub);
@@ -634,19 +643,18 @@ describe("oasDiffChangelog", () => {
634643

635644
it("should not include empty results in single file JSON mode", async () => {
636645
const execStub = createMockExec();
637-
execStub.onSecondCall().callsArgWith(1, null, "[]", ""); // empty array result
646+
execStub.onSecondCall().callsArgWith(1, null, "[]", "");
638647

639648
const fsStub = createMockFs();
640649
const oasDiff = createOasDiffProxy(execStub, fsStub);
641650

642-
// Arrange
643651
const baseApi = "base.yaml";
644652
const newApi = "new.yaml";
645653
const flags = { format: "json" };
646654
const result = await oasDiff.oasDiffChangelog(baseApi, newApi, flags);
647655

648656
expect(execStub.called).to.be.true;
649-
expect(result).to.equal(0); // No changes should be reported
657+
expect(result).to.equal(0);
650658
});
651659

652660
it("should include non-empty results in single file JSON mode", async () => {

src/diff/oasDiff.ts

Lines changed: 113 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ async function findExchangeDirectories(
5454
subdirectories.push(entryPath);
5555
}
5656
}
57-
console.log("subdirectories", subdirectories);
58-
console.log("hasExchangeJson", hasExchangeJson);
5957
// If this is a leaf directory and we haven't found exchange.json, that's an error
6058
if (subdirectories.length === 0 && !hasExchangeJson) {
6159
throw new Error(
@@ -95,6 +93,36 @@ async function findExchangeDirectories(
9593
return result;
9694
}
9795

96+
/**
97+
* Find YAML files in a directory
98+
*
99+
* @param dirPath - The directory path to search
100+
* @returns Array of YAML file paths
101+
*/
102+
async function findYamlFiles(dirPath: string): Promise<string[]> {
103+
try {
104+
const entries = await fs.readdir(dirPath);
105+
const yamlFiles = [];
106+
107+
for (const entry of entries) {
108+
const entryPath = path.join(dirPath, entry);
109+
const stat = await fs.stat(entryPath);
110+
111+
if (
112+
stat.isFile() &&
113+
(entry.endsWith(".yaml") || entry.endsWith(".yml"))
114+
) {
115+
yamlFiles.push(entryPath);
116+
}
117+
}
118+
119+
return yamlFiles;
120+
} catch (err) {
121+
console.warn(`Warning: Could not read directory ${dirPath}:`, err.message);
122+
return [];
123+
}
124+
}
125+
98126
/**
99127
* If a file is given, saves the changes to the file, as JSON by default.
100128
* Otherwise, logs the changes to console, as text by default.
@@ -166,7 +194,6 @@ export async function oasDiffChangelog(baseApi: string, newApi: string, flags) {
166194
console.log("......Starting oasdiff......");
167195

168196
const jsonMode = flags.format === "json" ? "-f json" : "";
169-
const directoryMode = flags.dir ? "--composed" : "";
170197

171198
let hasChanges = false;
172199
let hasErrors = false;
@@ -221,36 +248,99 @@ export async function oasDiffChangelog(baseApi: string, newApi: string, flags) {
221248

222249
// Both directories exist, compare them
223250
if (baseDir && newDir) {
251+
console.log("===================================");
224252
console.log(`Processing directory pair: ${dirName}`);
225253

226254
try {
227-
const baseYamlPath = `${baseDir.path}/**/*.yaml`;
228-
const newYamlPath = `${newDir.path}/**/*.yaml`;
255+
const baseYamlFiles = await findYamlFiles(baseDir.path);
256+
const newYamlFiles = await findYamlFiles(newDir.path);
229257

230-
const oasdiffOutput = await executeOasDiff(
231-
baseYamlPath,
232-
newYamlPath,
233-
jsonMode,
234-
directoryMode
235-
);
258+
const directoryChanges = [];
259+
const directoryChangesText = [];
236260

237-
if (oasdiffOutput.trim().length > 0) {
238-
console.log(`Changes found in ${dirName}`);
239-
if (flags.format === "json") {
240-
const outputJson = JSON.parse(oasdiffOutput);
241-
if (outputJson?.length > 0) {
242-
allResults.push({
243-
directory: dirName,
244-
changes: outputJson,
261+
// Process each YAML file pair
262+
for (const baseYamlFile of baseYamlFiles) {
263+
const baseFileName = path.basename(baseYamlFile);
264+
const newYamlFile = newYamlFiles.find(
265+
(f) => path.basename(f) === baseFileName
266+
);
267+
268+
if (newYamlFile) {
269+
console.log(`Comparing ${baseFileName} in ${dirName}`);
270+
const oasdiffOutput = await executeOasDiff(
271+
baseYamlFile,
272+
newYamlFile,
273+
jsonMode
274+
);
275+
276+
if (oasdiffOutput.trim().length > 0) {
277+
if (flags.format === "json") {
278+
const outputJson = JSON.parse(oasdiffOutput);
279+
if (outputJson?.length > 0) {
280+
directoryChanges.push(...outputJson);
281+
}
282+
} else {
283+
directoryChangesText.push(
284+
`--- Changes in ${baseFileName} ---\n${oasdiffOutput}`
285+
);
286+
}
287+
}
288+
} else {
289+
console.log(`File ${baseFileName} was deleted in ${dirName}`);
290+
if (flags.format === "json") {
291+
directoryChanges.push({
292+
file: baseFileName,
293+
status: "deleted",
294+
message: `File ${baseFileName} was deleted`,
295+
});
296+
} else {
297+
directoryChangesText.push(
298+
`--- File ${baseFileName} was deleted ---`
299+
);
300+
}
301+
}
302+
}
303+
304+
// Check for added files
305+
for (const newYamlFile of newYamlFiles) {
306+
const newFileName = path.basename(newYamlFile);
307+
const baseYamlFile = baseYamlFiles.find(
308+
(f) => path.basename(f) === newFileName
309+
);
310+
311+
if (!baseYamlFile) {
312+
console.log(`File ${newFileName} was added in ${dirName}`);
313+
if (flags.format === "json") {
314+
directoryChanges.push({
315+
file: newFileName,
316+
status: "added",
317+
message: `File ${newFileName} was added`,
245318
});
246-
hasChanges = true;
319+
} else {
320+
directoryChangesText.push(
321+
`--- File ${newFileName} was added ---`
322+
);
247323
}
324+
}
325+
}
326+
327+
if (
328+
directoryChanges.length > 0 ||
329+
directoryChangesText.length > 0
330+
) {
331+
console.log(`Changes found in ${dirName}`);
332+
if (flags.format === "json") {
333+
allResults.push({
334+
directory: dirName,
335+
changes: directoryChanges,
336+
});
248337
} else {
249-
// For text format, add section headers
250-
const formattedOutput = `=== Changes in ${dirName} ===\n${oasdiffOutput}`;
338+
const formattedOutput = `=== Changes in ${dirName} ===\n${directoryChangesText.join(
339+
"\n"
340+
)}`;
251341
allResults.push(formattedOutput);
252-
hasChanges = true;
253342
}
343+
hasChanges = true;
254344
} else {
255345
console.log(`No changes found in ${dirName}`);
256346
}

0 commit comments

Comments
 (0)