Skip to content

Commit ef9983c

Browse files
committed
updating comparison output. Normalizing rg name always.
1 parent 763f568 commit ef9983c

5 files changed

Lines changed: 268 additions & 40 deletions

File tree

src/cli/compare-command.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,9 @@ function outputTable(result: CompareResult): void {
232232

233233
if (result.totalDifferences === 0) {
234234
console.log('✅ PASS — Instances are identical');
235-
console.log(
236-
` ${result.totalTypes} resource types compared, ${result.totalResources} resources matched`,
237-
);
238235
} else {
239236
console.log('❌ FAIL — Differences found');
240-
console.log(
241-
` ${result.totalDifferences} difference(s) across ${result.totalTypes} resource types`,
242-
);
237+
console.log(` ${result.totalDifferences} difference(s) found`);
243238
console.log('');
244239

245240
// Group by resource type
@@ -336,13 +331,9 @@ async function runLocalCompare(
336331

337332
function outputText(result: CompareResult): void {
338333
if (result.totalDifferences === 0) {
339-
logger.info(
340-
`✅ PASS — ${result.totalTypes} resource types compared, ${result.totalResources} resources matched`,
341-
);
334+
logger.info('✅ PASS — Instances are identical');
342335
} else {
343-
logger.error(
344-
`❌ FAIL — ${result.totalDifferences} difference(s) found across ${result.totalTypes} resource types`,
345-
);
336+
logger.error(`❌ FAIL — ${result.totalDifferences} difference(s) found`);
346337
for (const diff of result.differences) {
347338
if (diff.diffType === 'missing') {
348339
logger.error(

src/lib/compare-normalizer.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ function normalizeString(value: string, context: NormalizeContext): string {
155155
s = s.replace(new RegExp(escapeRegex(sourceSub), 'g'), placeholderSub);
156156
s = s.replace(new RegExp(escapeRegex(targetSub), 'g'), placeholderSub);
157157

158+
// Normalize resource group segment for any ARM resource ID under normalized subscriptions.
159+
// This avoids false diffs when equivalent dependent resources live in different RGs.
160+
s = s.replace(
161+
/\/subscriptions\/\{\{sub\}\}\/resourceGroups\/[^/]+(?=\/providers\/)/g,
162+
'/subscriptions/{{sub}}/resourceGroups/{{rg}}',
163+
);
164+
158165
// Neutralize service name in any remaining positions
159166
s = s.replace(
160167
new RegExp(escapeRegex(context.sourceServiceName), 'g'),

src/services/compare-service.ts

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ import { LogLevel } from '../lib/logger.js';
2727
import { loadLocalArtifacts } from './local-artifact-loader.js';
2828

2929
export interface CompareResult {
30-
totalTypes: number;
31-
totalResources: number;
3230
totalDifferences: number;
3331
differences: ComparisonDifference[];
3432
}
3533

3634
export interface ComparisonDifference {
35+
instance?: 'source' | 'target';
3736
resourceType: string;
3837
resourceName: string;
38+
displayName?: string;
3939
diffType: 'missing' | 'extra' | 'property-diff';
4040
diffs?: ResourceDiff[];
4141
}
@@ -66,8 +66,6 @@ export async function compareApimInstances(
6666
};
6767

6868
const differences: ComparisonDifference[] = [];
69-
let totalTypes = 0;
70-
let totalResources = 0;
7169

7270
// Get clients from config
7371
const sourceClient: IApimClient = config.sourceClient;
@@ -113,8 +111,6 @@ export async function compareApimInstances(
113111
typeConfig.skipLoggerCreds ?? false,
114112
);
115113
differences.push(...typeDiffs);
116-
totalTypes++;
117-
totalResources += typeDiffs.length;
118114
}
119115

120116
// Compare APIs
@@ -128,8 +124,6 @@ export async function compareApimInstances(
128124
EXCLUDE_APIS,
129125
);
130126
differences.push(...apiDiffs);
131-
totalTypes++;
132-
totalResources += apiDiffs.length;
133127

134128
// Enumerate APIs for child resource comparison
135129
const sourceApis = await safeListResources(
@@ -170,8 +164,6 @@ export async function compareApimInstances(
170164
config.target,
171165
);
172166
differences.push(...childDiffs);
173-
totalTypes++;
174-
totalResources += childDiffs.length;
175167
}
176168

177169
// API Operation Policies
@@ -195,8 +187,6 @@ export async function compareApimInstances(
195187
config.target,
196188
);
197189
differences.push(...opPolicyDiffs);
198-
totalTypes++;
199-
totalResources += opPolicyDiffs.length;
200190
}
201191

202192
// API Resolver Policies
@@ -220,8 +210,6 @@ export async function compareApimInstances(
220210
config.target,
221211
);
222212
differences.push(...resolverPolicyDiffs);
223-
totalTypes++;
224-
totalResources += resolverPolicyDiffs.length;
225213
}
226214
}
227215

@@ -259,8 +247,6 @@ export async function compareApimInstances(
259247
config.target,
260248
);
261249
differences.push(...childDiffs);
262-
totalTypes++;
263-
totalResources += childDiffs.length;
264250
}
265251
}
266252

@@ -283,8 +269,6 @@ export async function compareApimInstances(
283269
config.target,
284270
);
285271
differences.push(...gatewayApiDiffs);
286-
totalTypes++;
287-
totalResources += gatewayApiDiffs.length;
288272
}
289273

290274
// Note: Workspace comparison disabled - Workspace types not yet defined in ResourceType enum
@@ -326,8 +310,6 @@ export async function compareApimInstances(
326310
config.target,
327311
);
328312
differences.push(...childDiffs);
329-
totalTypes++;
330-
totalResources += childDiffs.length;
331313
}
332314
}
333315
*/
@@ -337,8 +319,6 @@ export async function compareApimInstances(
337319
).length;
338320

339321
return {
340-
totalTypes,
341-
totalResources,
342322
totalDifferences,
343323
differences,
344324
};
@@ -657,21 +637,31 @@ function compareResourceLists(
657637
// Find missing resources (in source but not in target)
658638
for (const name of sourceMap.keys()) {
659639
if (!targetMap.has(name)) {
640+
const sourceResource = sourceMap.get(name)!;
641+
const displayName = getComparisonDisplayName(sourceResource);
642+
660643
differences.push({
661644
resourceType: typeLabel,
662645
resourceName: name,
663646
diffType: 'missing',
647+
...(displayName === undefined ? {} : { displayName }),
648+
instance: 'source',
664649
});
665650
}
666651
}
667652

668653
// Find extra resources (in target but not in source)
669654
for (const name of targetMap.keys()) {
670655
if (!sourceMap.has(name)) {
656+
const targetResource = targetMap.get(name)!;
657+
const displayName = getComparisonDisplayName(targetResource);
658+
671659
differences.push({
672660
resourceType: typeLabel,
673661
resourceName: name,
674662
diffType: 'extra',
663+
...(displayName === undefined ? {} : { displayName }),
664+
instance: 'target',
675665
});
676666
}
677667
}
@@ -682,6 +672,9 @@ function compareResourceLists(
682672

683673
const sourceResource = sourceMap.get(name)!;
684674
const targetResource = targetMap.get(name)!;
675+
const displayName =
676+
getComparisonDisplayName(sourceResource) ??
677+
getComparisonDisplayName(targetResource);
685678

686679
const sourceNorm = normalizeResource(sourceResource, normalizeContext);
687680
const targetNorm = normalizeResource(targetResource, normalizeContext);
@@ -728,6 +721,7 @@ function compareResourceLists(
728721
resourceType: typeLabel,
729722
resourceName: name,
730723
diffType: 'property-diff',
724+
...(displayName === undefined ? {} : { displayName }),
731725
diffs,
732726
});
733727
}
@@ -736,6 +730,25 @@ function compareResourceLists(
736730
return differences;
737731
}
738732

733+
function getComparisonDisplayName(
734+
resource: Record<string, unknown>,
735+
): string | undefined {
736+
const properties = resource.properties;
737+
if (typeof properties === 'object' && properties !== null) {
738+
const displayName = (properties as Record<string, unknown>).displayName;
739+
if (typeof displayName === 'string' && displayName.length > 0) {
740+
return displayName;
741+
}
742+
}
743+
744+
const displayName = resource.displayName;
745+
if (typeof displayName === 'string' && displayName.length > 0) {
746+
return displayName;
747+
}
748+
749+
return undefined;
750+
}
751+
739752
/**
740753
* Builds a resource map keyed by name, handling auto-generated IDs
741754
*/
@@ -844,8 +857,6 @@ export async function compareLocalArtifacts(
844857
};
845858

846859
const differences: ComparisonDifference[] = [];
847-
let totalTypes = 0;
848-
let totalResources = 0;
849860

850861
// Get all unique resource types from both sources
851862
const allTypes = new Set<string>();
@@ -872,17 +883,13 @@ export async function compareLocalArtifacts(
872883
);
873884

874885
differences.push(...typeDiffs);
875-
totalTypes++;
876-
totalResources += Math.max(sourceResources.length, targetResources.length);
877886
}
878887

879888
const totalDifferences = differences.filter(
880889
(d) => d.diffType !== 'property-diff' || (d.diffs && d.diffs.length > 0),
881890
).length;
882891

883892
return {
884-
totalTypes,
885-
totalResources,
886893
totalDifferences,
887894
differences,
888895
};
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import { describe, it, expect } from 'vitest';
5+
import { normalizeResource, type NormalizeContext } from '../../../src/lib/compare-normalizer.js';
6+
7+
const context: NormalizeContext = {
8+
sourceServiceName: 'src-apim',
9+
targetServiceName: 'tgt-apim',
10+
sourceSubscriptionId: '00000000-0000-0000-0000-000000000001',
11+
targetSubscriptionId: '00000000-0000-0000-0000-000000000001',
12+
sourceResourceGroup: 'src-apim-rg',
13+
targetResourceGroup: 'tgt-apim-rg',
14+
};
15+
16+
describe('compare-normalizer', () => {
17+
it('normalizes backend function resourceId when only resource group differs', () => {
18+
const source = {
19+
properties: {
20+
resourceId:
21+
'/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/src-function-rg/providers/Microsoft.Web/sites/src-backend-function',
22+
},
23+
};
24+
const target = {
25+
properties: {
26+
resourceId:
27+
'/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/tgt-function-rg/providers/Microsoft.Web/sites/src-backend-function',
28+
},
29+
};
30+
31+
const sourceNorm = normalizeResource(source, context);
32+
const targetNorm = normalizeResource(target, context);
33+
34+
expect(sourceNorm).toEqual(targetNorm);
35+
expect((sourceNorm.properties as Record<string, unknown>).resourceId).toBe(
36+
'/subscriptions/{{sub}}/resourceGroups/{{rg}}/providers/Microsoft.Web/sites/src-backend-function',
37+
);
38+
});
39+
40+
it('normalizes backend logic app resourceId when only resource group differs', () => {
41+
const source = {
42+
properties: {
43+
resourceId:
44+
'/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/src-logicapp-rg/providers/Microsoft.Logic/workflows/src-backend-logicapp',
45+
},
46+
};
47+
const target = {
48+
properties: {
49+
resourceId:
50+
'/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/tgt-logicapp-rg/providers/Microsoft.Logic/workflows/src-backend-logicapp',
51+
},
52+
};
53+
54+
const sourceNorm = normalizeResource(source, context);
55+
const targetNorm = normalizeResource(target, context);
56+
57+
expect(sourceNorm).toEqual(targetNorm);
58+
expect((sourceNorm.properties as Record<string, unknown>).resourceId).toBe(
59+
'/subscriptions/{{sub}}/resourceGroups/{{rg}}/providers/Microsoft.Logic/workflows/src-backend-logicapp',
60+
);
61+
});
62+
63+
it('keeps meaningful provider or resource-name differences', () => {
64+
const source = {
65+
properties: {
66+
resourceId:
67+
'/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/src-function-rg/providers/Microsoft.Web/sites/src-backend-function',
68+
},
69+
};
70+
const target = {
71+
properties: {
72+
resourceId:
73+
'/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/tgt-function-rg/providers/Microsoft.Logic/workflows/src-backend-function',
74+
},
75+
};
76+
77+
const sourceNorm = normalizeResource(source, context);
78+
const targetNorm = normalizeResource(target, context);
79+
80+
expect(sourceNorm).not.toEqual(targetNorm);
81+
});
82+
});

0 commit comments

Comments
 (0)