fix: add tests for diff (#930)

* fix: add tests for diff

* fix
This commit is contained in:
Guy Ben-Aharon
2025-09-21 18:34:29 +03:00
committed by GitHub
parent d71b46e8b5
commit 47a7a73a13
2 changed files with 778 additions and 32 deletions

View File

@@ -0,0 +1,662 @@
import { describe, it, expect } from 'vitest';
import { generateDiff } from '../diff-check';
import type { Diagram } from '@/lib/domain/diagram';
import type { DBTable } from '@/lib/domain/db-table';
import type { DBField } from '@/lib/domain/db-field';
import type { DBIndex } from '@/lib/domain/db-index';
import type { DBRelationship } from '@/lib/domain/db-relationship';
import type { Area } from '@/lib/domain/area';
import { DatabaseType } from '@/lib/domain/database-type';
import type { TableDiffChanged } from '../../table-diff';
import type { FieldDiffChanged } from '../../field-diff';
// Helper function to create a mock diagram
function createMockDiagram(overrides?: Partial<Diagram>): Diagram {
return {
id: 'diagram-1',
name: 'Test Diagram',
databaseType: DatabaseType.POSTGRESQL,
tables: [],
relationships: [],
areas: [],
createdAt: new Date(),
updatedAt: new Date(),
...overrides,
};
}
// Helper function to create a mock table
function createMockTable(overrides?: Partial<DBTable>): DBTable {
return {
id: 'table-1',
name: 'users',
fields: [],
indexes: [],
x: 0,
y: 0,
...overrides,
} as DBTable;
}
// Helper function to create a mock field
function createMockField(overrides?: Partial<DBField>): DBField {
return {
id: 'field-1',
name: 'id',
type: { id: 'integer', name: 'integer' },
primaryKey: false,
nullable: true,
unique: false,
...overrides,
} as DBField;
}
// Helper function to create a mock relationship
function createMockRelationship(
overrides?: Partial<DBRelationship>
): DBRelationship {
return {
id: 'rel-1',
sourceTableId: 'table-1',
targetTableId: 'table-2',
sourceFieldId: 'field-1',
targetFieldId: 'field-2',
type: 'one-to-many',
...overrides,
} as DBRelationship;
}
// Helper function to create a mock area
function createMockArea(overrides?: Partial<Area>): Area {
return {
id: 'area-1',
name: 'Main Area',
x: 0,
y: 0,
width: 100,
height: 100,
color: 'blue',
...overrides,
} as Area;
}
describe('generateDiff', () => {
describe('Basic Table Diffing', () => {
it('should detect added tables', () => {
const oldDiagram = createMockDiagram({ tables: [] });
const newDiagram = createMockDiagram({
tables: [createMockTable()],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('table-table-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('added');
expect(result.changedTables.has('table-1')).toBe(true);
});
it('should detect removed tables', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable()],
});
const newDiagram = createMockDiagram({ tables: [] });
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('table-table-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('removed');
expect(result.changedTables.has('table-1')).toBe(true);
});
it('should detect table name changes', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ name: 'users' })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ name: 'customers' })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('table-name-table-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('changed');
expect((diff as TableDiffChanged)?.attribute).toBe('name');
});
it('should detect table position changes', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ x: 0, y: 0 })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ x: 100, y: 200 })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
attributes: {
tables: ['name', 'comments', 'color', 'x', 'y'],
},
},
});
expect(result.diffMap.size).toBe(2);
expect(result.diffMap.has('table-x-table-1')).toBe(true);
expect(result.diffMap.has('table-y-table-1')).toBe(true);
});
});
describe('Field Diffing', () => {
it('should detect added fields', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ fields: [] })],
});
const newDiagram = createMockDiagram({
tables: [
createMockTable({
fields: [createMockField()],
}),
],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('field-field-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('added');
expect(result.changedFields.has('field-1')).toBe(true);
});
it('should detect removed fields', () => {
const oldDiagram = createMockDiagram({
tables: [
createMockTable({
fields: [createMockField()],
}),
],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ fields: [] })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('field-field-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('removed');
});
it('should detect field type changes', () => {
const oldDiagram = createMockDiagram({
tables: [
createMockTable({
fields: [
createMockField({
type: { id: 'integer', name: 'integer' },
}),
],
}),
],
});
const newDiagram = createMockDiagram({
tables: [
createMockTable({
fields: [
createMockField({
type: { id: 'varchar', name: 'varchar' },
}),
],
}),
],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('field-type-field-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('changed');
expect((diff as FieldDiffChanged)?.attribute).toBe('type');
});
});
describe('Relationship Diffing', () => {
it('should detect added relationships', () => {
const oldDiagram = createMockDiagram({ relationships: [] });
const newDiagram = createMockDiagram({
relationships: [createMockRelationship()],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('relationship-rel-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('added');
});
it('should detect removed relationships', () => {
const oldDiagram = createMockDiagram({
relationships: [createMockRelationship()],
});
const newDiagram = createMockDiagram({ relationships: [] });
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('relationship-rel-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('removed');
});
});
describe('Area Diffing', () => {
it('should detect added areas when includeAreas is true', () => {
const oldDiagram = createMockDiagram({ areas: [] });
const newDiagram = createMockDiagram({
areas: [createMockArea()],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
includeAreas: true,
},
});
expect(result.diffMap.size).toBe(1);
const diff = result.diffMap.get('area-area-1');
expect(diff).toBeDefined();
expect(diff?.type).toBe('added');
expect(result.changedAreas.has('area-1')).toBe(true);
});
it('should not detect area changes when includeAreas is false', () => {
const oldDiagram = createMockDiagram({ areas: [] });
const newDiagram = createMockDiagram({
areas: [createMockArea()],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
includeAreas: false,
},
});
expect(result.diffMap.size).toBe(0);
});
});
describe('Custom Matchers', () => {
it('should use custom table matcher to match by name', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-1', name: 'users' })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-2', name: 'users' })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
matchers: {
table: (table, tables) =>
tables.find((t) => t.name === table.name),
},
},
});
// Should not detect any changes since tables match by name
expect(result.diffMap.size).toBe(0);
});
it('should detect changes when custom matcher finds no match', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-1', name: 'users' })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-2', name: 'customers' })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
matchers: {
table: (table, tables) =>
tables.find((t) => t.name === table.name),
},
},
});
// Should detect both added and removed since names don't match
expect(result.diffMap.size).toBe(2);
expect(result.diffMap.has('table-table-1')).toBe(true); // removed
expect(result.diffMap.has('table-table-2')).toBe(true); // added
});
it('should use custom field matcher to match by name', () => {
const field1 = createMockField({
id: 'field-1',
name: 'email',
nullable: true,
});
const field2 = createMockField({
id: 'field-2',
name: 'email',
nullable: false,
});
const oldDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-1', fields: [field1] })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-1', fields: [field2] })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
matchers: {
field: (field, fields) =>
fields.find((f) => f.name === field.name),
},
},
});
// With name-based matching, field-1 should match field-2 by name
// and detect the nullable change
const nullableChange = result.diffMap.get('field-nullable-field-1');
expect(nullableChange).toBeDefined();
expect(nullableChange?.type).toBe('changed');
expect((nullableChange as FieldDiffChanged)?.attribute).toBe(
'nullable'
);
});
it('should use case-insensitive custom matcher', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-1', name: 'Users' })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-2', name: 'users' })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
matchers: {
table: (table, tables) =>
tables.find(
(t) =>
t.name.toLowerCase() ===
table.name.toLowerCase()
),
},
},
});
// With case-insensitive name matching, the tables are matched
// but the name case difference is still detected as a change
expect(result.diffMap.size).toBe(1);
const nameChange = result.diffMap.get('table-name-table-1');
expect(nameChange).toBeDefined();
expect(nameChange?.type).toBe('changed');
expect((nameChange as TableDiffChanged)?.attribute).toBe('name');
expect((nameChange as TableDiffChanged)?.oldValue).toBe('Users');
expect((nameChange as TableDiffChanged)?.newValue).toBe('users');
});
});
describe('Filtering Options', () => {
it('should only check specified change types', () => {
const oldDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-1', name: 'users' })],
});
const newDiagram = createMockDiagram({
tables: [createMockTable({ id: 'table-2', name: 'products' })],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
changeTypes: {
tables: ['added'], // Only check for added tables
},
},
});
// Should only detect added table (table-2)
const addedTables = Array.from(result.diffMap.values()).filter(
(diff) => diff.type === 'added' && diff.object === 'table'
);
expect(addedTables.length).toBe(1);
// Should not detect removed table (table-1)
const removedTables = Array.from(result.diffMap.values()).filter(
(diff) => diff.type === 'removed' && diff.object === 'table'
);
expect(removedTables.length).toBe(0);
});
it('should only check specified attributes', () => {
const oldDiagram = createMockDiagram({
tables: [
createMockTable({
id: 'table-1',
name: 'users',
color: 'blue',
comments: 'old comment',
}),
],
});
const newDiagram = createMockDiagram({
tables: [
createMockTable({
id: 'table-1',
name: 'customers',
color: 'red',
comments: 'new comment',
}),
],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
attributes: {
tables: ['name'], // Only check name changes
},
},
});
// Should only detect name change
const nameChanges = Array.from(result.diffMap.values()).filter(
(diff) =>
diff.type === 'changed' &&
diff.attribute === 'name' &&
diff.object === 'table'
);
expect(nameChanges.length).toBe(1);
// Should not detect color or comments changes
const otherChanges = Array.from(result.diffMap.values()).filter(
(diff) =>
diff.type === 'changed' &&
(diff.attribute === 'color' ||
diff.attribute === 'comments') &&
diff.object === 'table'
);
expect(otherChanges.length).toBe(0);
});
it('should respect include flags', () => {
const oldDiagram = createMockDiagram({
tables: [
createMockTable({
fields: [createMockField()],
indexes: [{ id: 'idx-1', name: 'idx' } as DBIndex],
}),
],
});
const newDiagram = createMockDiagram({
tables: [
createMockTable({
fields: [],
indexes: [],
}),
],
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
options: {
includeFields: false,
includeIndexes: true,
},
});
// Should only detect index removal, not field removal
expect(result.diffMap.has('index-idx-1')).toBe(true);
expect(result.diffMap.has('field-field-1')).toBe(false);
});
});
describe('Complex Scenarios', () => {
it('should handle multiple simultaneous changes', () => {
const oldDiagram = createMockDiagram({
tables: [
createMockTable({
id: 'table-1',
name: 'users',
fields: [
createMockField({ id: 'field-1', name: 'id' }),
createMockField({ id: 'field-2', name: 'email' }),
],
}),
createMockTable({
id: 'table-2',
name: 'products',
}),
],
relationships: [createMockRelationship()],
});
const newDiagram = createMockDiagram({
tables: [
createMockTable({
id: 'table-1',
name: 'customers', // Changed name
fields: [
createMockField({ id: 'field-1', name: 'id' }),
// Removed field-2
createMockField({ id: 'field-3', name: 'name' }), // Added field
],
}),
// Removed table-2
createMockTable({
id: 'table-3',
name: 'orders', // Added table
}),
],
relationships: [], // Removed relationship
});
const result = generateDiff({
diagram: oldDiagram,
newDiagram,
});
// Verify all changes are detected
expect(result.diffMap.has('table-name-table-1')).toBe(true); // Table name change
expect(result.diffMap.has('field-field-2')).toBe(true); // Removed field
expect(result.diffMap.has('field-field-3')).toBe(true); // Added field
expect(result.diffMap.has('table-table-2')).toBe(true); // Removed table
expect(result.diffMap.has('table-table-3')).toBe(true); // Added table
expect(result.diffMap.has('relationship-rel-1')).toBe(true); // Removed relationship
});
it('should handle empty diagrams', () => {
const emptyDiagram1 = createMockDiagram();
const emptyDiagram2 = createMockDiagram();
const result = generateDiff({
diagram: emptyDiagram1,
newDiagram: emptyDiagram2,
});
expect(result.diffMap.size).toBe(0);
expect(result.changedTables.size).toBe(0);
expect(result.changedFields.size).toBe(0);
expect(result.changedAreas.size).toBe(0);
});
it('should handle diagrams with undefined collections', () => {
const diagram1 = createMockDiagram({
tables: undefined,
relationships: undefined,
areas: undefined,
});
const diagram2 = createMockDiagram({
tables: [createMockTable({ id: 'table-1' })],
relationships: [createMockRelationship({ id: 'rel-1' })],
areas: [createMockArea({ id: 'area-1' })],
});
const result = generateDiff({
diagram: diagram1,
newDiagram: diagram2,
options: {
includeAreas: true,
},
});
// Should detect all as added
expect(result.diffMap.has('table-table-1')).toBe(true);
expect(result.diffMap.has('relationship-rel-1')).toBe(true);
expect(result.diffMap.has('area-area-1')).toBe(true);
});
});
});

View File

@@ -1,6 +1,9 @@
import type { Diagram } from '@/lib/domain/diagram';
import type { DBField } from '@/lib/domain/db-field';
import type { DBIndex } from '@/lib/domain/db-index';
import type { DBTable } from '@/lib/domain/db-table';
import type { DBRelationship } from '@/lib/domain/db-relationship';
import type { Area } from '@/lib/domain/area';
import type { ChartDBDiff, DiffMap, DiffObject } from '@/lib/domain/diff/diff';
import type {
FieldDiff,
@@ -43,20 +46,22 @@ export interface GenerateDiffOptions {
relationships?: RelationshipDiff['type'][];
areas?: AreaDiff['type'][];
};
matchers?: {
table?: (table: DBTable, tables: DBTable[]) => DBTable | undefined;
field?: (field: DBField, fields: DBField[]) => DBField | undefined;
index?: (index: DBIndex, indexes: DBIndex[]) => DBIndex | undefined;
relationship?: (
relationship: DBRelationship,
relationships: DBRelationship[]
) => DBRelationship | undefined;
area?: (area: Area, areas: Area[]) => Area | undefined;
};
}
export function generateDiff({
diagram,
newDiagram,
options = {
includeTables: true,
includeFields: true,
includeIndexes: true,
includeRelationships: true,
includeAreas: false,
attributes: {},
changeTypes: {},
},
options = {},
}: {
diagram: Diagram;
newDiagram: Diagram;
@@ -67,20 +72,41 @@ export function generateDiff({
changedFields: Map<string, boolean>;
changedAreas: Map<string, boolean>;
} {
// Merge with default options
const mergedOptions: GenerateDiffOptions = {
includeTables: options.includeTables ?? true,
includeFields: options.includeFields ?? true,
includeIndexes: options.includeIndexes ?? true,
includeRelationships: options.includeRelationships ?? true,
includeAreas: options.includeAreas ?? false,
attributes: options.attributes ?? {},
changeTypes: options.changeTypes ?? {},
matchers: options.matchers ?? {},
};
const newDiffs = new Map<string, ChartDBDiff>();
const changedTables = new Map<string, boolean>();
const changedFields = new Map<string, boolean>();
const changedAreas = new Map<string, boolean>();
// Use provided matchers or default ones
const tableMatcher = mergedOptions.matchers?.table ?? defaultTableMatcher;
const fieldMatcher = mergedOptions.matchers?.field ?? defaultFieldMatcher;
const indexMatcher = mergedOptions.matchers?.index ?? defaultIndexMatcher;
const relationshipMatcher =
mergedOptions.matchers?.relationship ?? defaultRelationshipMatcher;
const areaMatcher = mergedOptions.matchers?.area ?? defaultAreaMatcher;
// Compare tables
if (options.includeTables) {
if (mergedOptions.includeTables) {
compareTables({
diagram,
newDiagram,
diffMap: newDiffs,
changedTables,
attributes: options.attributes?.tables,
changeTypes: options.changeTypes?.tables,
attributes: mergedOptions.attributes?.tables,
changeTypes: mergedOptions.changeTypes?.tables,
tableMatcher,
});
}
@@ -91,28 +117,33 @@ export function generateDiff({
diffMap: newDiffs,
changedTables,
changedFields,
options,
options: mergedOptions,
tableMatcher,
fieldMatcher,
indexMatcher,
});
// Compare relationships
if (options.includeRelationships) {
if (mergedOptions.includeRelationships) {
compareRelationships({
diagram,
newDiagram,
diffMap: newDiffs,
changeTypes: options.changeTypes?.relationships,
changeTypes: mergedOptions.changeTypes?.relationships,
relationshipMatcher,
});
}
// Compare areas if enabled
if (options.includeAreas) {
if (mergedOptions.includeAreas) {
compareAreas({
diagram,
newDiagram,
diffMap: newDiffs,
changedAreas,
attributes: options.attributes?.areas,
changeTypes: options.changeTypes?.areas,
attributes: mergedOptions.attributes?.areas,
changeTypes: mergedOptions.changeTypes?.areas,
areaMatcher,
});
}
@@ -127,6 +158,7 @@ function compareTables({
changedTables,
attributes,
changeTypes,
tableMatcher,
}: {
diagram: Diagram;
newDiagram: Diagram;
@@ -134,6 +166,7 @@ function compareTables({
changedTables: Map<string, boolean>;
attributes?: TableDiffAttribute[];
changeTypes?: TableDiff['type'][];
tableMatcher: (table: DBTable, tables: DBTable[]) => DBTable | undefined;
}) {
const oldTables = diagram.tables || [];
const newTables = newDiagram.tables || [];
@@ -149,7 +182,7 @@ function compareTables({
// Check for added tables
if (typesToCheck.includes('added')) {
for (const newTable of newTables) {
if (!oldTables.find((t) => t.id === newTable.id)) {
if (!tableMatcher(newTable, oldTables)) {
diffMap.set(
getDiffMapKey({
diffObject: 'table',
@@ -169,7 +202,7 @@ function compareTables({
// Check for removed tables
if (typesToCheck.includes('removed')) {
for (const oldTable of oldTables) {
if (!newTables.find((t) => t.id === oldTable.id)) {
if (!tableMatcher(oldTable, newTables)) {
diffMap.set(
getDiffMapKey({
diffObject: 'table',
@@ -189,7 +222,7 @@ function compareTables({
// Check for table name, comments and color changes
if (typesToCheck.includes('changed')) {
for (const oldTable of oldTables) {
const newTable = newTables.find((t) => t.id === oldTable.id);
const newTable = tableMatcher(oldTable, newTables);
if (!newTable) continue;
@@ -321,6 +354,9 @@ function compareTableContents({
changedTables,
changedFields,
options,
tableMatcher,
fieldMatcher,
indexMatcher,
}: {
diagram: Diagram;
newDiagram: Diagram;
@@ -328,13 +364,16 @@ function compareTableContents({
changedTables: Map<string, boolean>;
changedFields: Map<string, boolean>;
options?: GenerateDiffOptions;
tableMatcher: (table: DBTable, tables: DBTable[]) => DBTable | undefined;
fieldMatcher: (field: DBField, fields: DBField[]) => DBField | undefined;
indexMatcher: (index: DBIndex, indexes: DBIndex[]) => DBIndex | undefined;
}) {
const oldTables = diagram.tables || [];
const newTables = newDiagram.tables || [];
// For each table that exists in both diagrams
for (const oldTable of oldTables) {
const newTable = newTables.find((t) => t.id === oldTable.id);
const newTable = tableMatcher(oldTable, newTables);
if (!newTable) continue;
// Compare fields
@@ -348,6 +387,7 @@ function compareTableContents({
changedFields,
attributes: options?.attributes?.fields,
changeTypes: options?.changeTypes?.fields,
fieldMatcher,
});
}
@@ -360,6 +400,7 @@ function compareTableContents({
diffMap,
changedTables,
changeTypes: options?.changeTypes?.indexes,
indexMatcher,
});
}
}
@@ -375,6 +416,7 @@ function compareFields({
changedFields,
attributes,
changeTypes,
fieldMatcher,
}: {
tableId: string;
oldFields: DBField[];
@@ -384,6 +426,7 @@ function compareFields({
changedFields: Map<string, boolean>;
attributes?: FieldDiffAttribute[];
changeTypes?: FieldDiff['type'][];
fieldMatcher: (field: DBField, fields: DBField[]) => DBField | undefined;
}) {
// If changeTypes is empty array, don't check any changes
if (changeTypes && changeTypes.length === 0) {
@@ -395,7 +438,7 @@ function compareFields({
// Check for added fields
if (typesToCheck.includes('added')) {
for (const newField of newFields) {
if (!oldFields.find((f) => f.id === newField.id)) {
if (!fieldMatcher(newField, oldFields)) {
diffMap.set(
getDiffMapKey({
diffObject: 'field',
@@ -417,7 +460,7 @@ function compareFields({
// Check for removed fields
if (typesToCheck.includes('removed')) {
for (const oldField of oldFields) {
if (!newFields.find((f) => f.id === oldField.id)) {
if (!fieldMatcher(oldField, newFields)) {
diffMap.set(
getDiffMapKey({
diffObject: 'field',
@@ -440,7 +483,7 @@ function compareFields({
// Check for field changes
if (typesToCheck.includes('changed')) {
for (const oldField of oldFields) {
const newField = newFields.find((f) => f.id === oldField.id);
const newField = fieldMatcher(oldField, newFields);
if (!newField) continue;
// Compare basic field properties
@@ -586,6 +629,7 @@ function compareIndexes({
diffMap,
changedTables,
changeTypes,
indexMatcher,
}: {
tableId: string;
oldIndexes: DBIndex[];
@@ -593,6 +637,7 @@ function compareIndexes({
diffMap: DiffMap;
changedTables: Map<string, boolean>;
changeTypes?: IndexDiff['type'][];
indexMatcher: (index: DBIndex, indexes: DBIndex[]) => DBIndex | undefined;
}) {
// If changeTypes is empty array, don't check any changes
if (changeTypes && changeTypes.length === 0) {
@@ -604,7 +649,7 @@ function compareIndexes({
// Check for added indexes
if (typesToCheck.includes('added')) {
for (const newIndex of newIndexes) {
if (!oldIndexes.find((i) => i.id === newIndex.id)) {
if (!indexMatcher(newIndex, oldIndexes)) {
diffMap.set(
getDiffMapKey({
diffObject: 'index',
@@ -625,7 +670,7 @@ function compareIndexes({
// Check for removed indexes
if (typesToCheck.includes('removed')) {
for (const oldIndex of oldIndexes) {
if (!newIndexes.find((i) => i.id === oldIndex.id)) {
if (!indexMatcher(oldIndex, newIndexes)) {
diffMap.set(
getDiffMapKey({
diffObject: 'index',
@@ -650,11 +695,16 @@ function compareRelationships({
newDiagram,
diffMap,
changeTypes,
relationshipMatcher,
}: {
diagram: Diagram;
newDiagram: Diagram;
diffMap: DiffMap;
changeTypes?: RelationshipDiff['type'][];
relationshipMatcher: (
relationship: DBRelationship,
relationships: DBRelationship[]
) => DBRelationship | undefined;
}) {
// If changeTypes is empty array, don't check any changes
if (changeTypes && changeTypes.length === 0) {
@@ -669,7 +719,7 @@ function compareRelationships({
// Check for added relationships
if (typesToCheck.includes('added')) {
for (const newRelationship of newRelationships) {
if (!oldRelationships.find((r) => r.id === newRelationship.id)) {
if (!relationshipMatcher(newRelationship, oldRelationships)) {
diffMap.set(
getDiffMapKey({
diffObject: 'relationship',
@@ -688,7 +738,7 @@ function compareRelationships({
// Check for removed relationships
if (typesToCheck.includes('removed')) {
for (const oldRelationship of oldRelationships) {
if (!newRelationships.find((r) => r.id === oldRelationship.id)) {
if (!relationshipMatcher(oldRelationship, newRelationships)) {
diffMap.set(
getDiffMapKey({
diffObject: 'relationship',
@@ -713,6 +763,7 @@ function compareAreas({
changedAreas,
attributes,
changeTypes,
areaMatcher,
}: {
diagram: Diagram;
newDiagram: Diagram;
@@ -720,6 +771,7 @@ function compareAreas({
changedAreas: Map<string, boolean>;
attributes?: AreaDiffAttribute[];
changeTypes?: AreaDiff['type'][];
areaMatcher: (area: Area, areas: Area[]) => Area | undefined;
}) {
const oldAreas = diagram.areas || [];
const newAreas = newDiagram.areas || [];
@@ -735,7 +787,7 @@ function compareAreas({
// Check for added areas
if (typesToCheck.includes('added')) {
for (const newArea of newAreas) {
if (!oldAreas.find((a) => a.id === newArea.id)) {
if (!areaMatcher(newArea, oldAreas)) {
diffMap.set(
getDiffMapKey({
diffObject: 'area',
@@ -755,7 +807,7 @@ function compareAreas({
// Check for removed areas
if (typesToCheck.includes('removed')) {
for (const oldArea of oldAreas) {
if (!newAreas.find((a) => a.id === oldArea.id)) {
if (!areaMatcher(oldArea, newAreas)) {
diffMap.set(
getDiffMapKey({
diffObject: 'area',
@@ -775,7 +827,7 @@ function compareAreas({
// Check for area name and color changes
if (typesToCheck.includes('changed')) {
for (const oldArea of oldAreas) {
const newArea = newAreas.find((a) => a.id === oldArea.id);
const newArea = areaMatcher(oldArea, newAreas);
if (!newArea) continue;
@@ -869,3 +921,35 @@ function compareAreas({
}
}
}
const defaultTableMatcher = (
table: DBTable,
tables: DBTable[]
): DBTable | undefined => {
return tables.find((t) => t.id === table.id);
};
const defaultFieldMatcher = (
field: DBField,
fields: DBField[]
): DBField | undefined => {
return fields.find((f) => f.id === field.id);
};
const defaultIndexMatcher = (
index: DBIndex,
indexes: DBIndex[]
): DBIndex | undefined => {
return indexes.find((i) => i.id === index.id);
};
const defaultRelationshipMatcher = (
relationship: DBRelationship,
relationships: DBRelationship[]
): DBRelationship | undefined => {
return relationships.find((r) => r.id === relationship.id);
};
const defaultAreaMatcher = (area: Area, areas: Area[]): Area | undefined => {
return areas.find((a) => a.id === area.id);
};