mirror of
https://github.com/chartdb/chartdb.git
synced 2026-02-10 21:49:56 -06:00
fix: preserve index names when applying DBML changes (#997)
* fix: preserve index names when applying DBML changes * fix
This commit is contained in:
@@ -163,14 +163,6 @@ ALTER TABLE ONLY "wizard_resident" ADD CONSTRAINT "wizard_tower_fk2" FOREIGN KEY
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Relationships found:', result.relationships.length);
|
||||
result.relationships.forEach((rel, i) => {
|
||||
console.log(
|
||||
`FK ${i + 1}: ${rel.sourceTable}.${rel.sourceColumn} -> ${rel.targetTable}.${rel.targetColumn}`
|
||||
);
|
||||
});
|
||||
console.log('Warnings:', result.warnings);
|
||||
|
||||
expect(result.tables).toHaveLength(2);
|
||||
|
||||
// At least one relationship should be found (the regex fallback should catch at least one)
|
||||
@@ -205,9 +197,6 @@ ALTER TABLE ONLY "public"."account_emailaddress" ADD CONSTRAINT "account_emailad
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Warnings:', result.warnings);
|
||||
console.log('Relationships:', result.relationships);
|
||||
|
||||
expect(result.tables).toHaveLength(2);
|
||||
expect(result.relationships).toHaveLength(1);
|
||||
|
||||
|
||||
@@ -13,13 +13,6 @@ CREATE TABLE crystal_enchantments (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('\nDebug info:');
|
||||
console.log('Tables found:', result.tables.length);
|
||||
console.log(
|
||||
'Table names:',
|
||||
result.tables.map((t) => t.name)
|
||||
);
|
||||
|
||||
expect(result.tables).toHaveLength(1);
|
||||
expect(result.tables[0].name).toBe('crystal_enchantments');
|
||||
expect(result.tables[0].columns).toHaveLength(2);
|
||||
|
||||
@@ -180,36 +180,26 @@ CREATE TABLE rewards (
|
||||
);`;
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('\nParsing results:');
|
||||
console.log(`- Tables found: ${result.tables.length}`);
|
||||
console.log(`- Enums found: ${result.enums?.length || 0}`);
|
||||
console.log(`- Warnings: ${result.warnings?.length || 0}`);
|
||||
|
||||
// List all table names
|
||||
const tableNames = result.tables.map((t) => t.name).sort();
|
||||
console.log('\nTable names:');
|
||||
tableNames.forEach((name, i) => {
|
||||
console.log(` ${i + 1}. ${name}`);
|
||||
});
|
||||
|
||||
// Should have all 20 tables
|
||||
expect(result.tables).toHaveLength(20);
|
||||
|
||||
// Verify parsing metadata
|
||||
expect(result.enums).toHaveLength(5);
|
||||
|
||||
// Check for quest_sample_rewards specifically
|
||||
const questSampleRewards = result.tables.find(
|
||||
(t) => t.name === 'quest_sample_rewards'
|
||||
);
|
||||
expect(questSampleRewards).toBeDefined();
|
||||
expect(questSampleRewards!.columns).toHaveLength(2);
|
||||
|
||||
if (questSampleRewards) {
|
||||
console.log('\nquest_sample_rewards table details:');
|
||||
console.log(`- Columns: ${questSampleRewards.columns.length}`);
|
||||
questSampleRewards.columns.forEach((col) => {
|
||||
console.log(
|
||||
` - ${col.name}: ${col.type} (nullable: ${col.nullable})`
|
||||
);
|
||||
});
|
||||
}
|
||||
// Verify quest_sample_rewards columns
|
||||
const qsrColumnNames = questSampleRewards!.columns.map((c) => c.name);
|
||||
expect(qsrColumnNames).toContain('quest_template_id');
|
||||
expect(qsrColumnNames).toContain('reward_id');
|
||||
|
||||
// Expected tables
|
||||
const expectedTables = [
|
||||
|
||||
@@ -62,15 +62,6 @@ ALTER TABLE "spells" ADD CONSTRAINT "spells_wizard_id_wizard_id_fk"
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
// Check enum parsing
|
||||
console.log('\n=== ENUMS FOUND ===');
|
||||
console.log('Count:', result.enums?.length || 0);
|
||||
if (result.enums) {
|
||||
result.enums.forEach((e) => {
|
||||
console.log(` - ${e.name}: ${e.values.length} values`);
|
||||
});
|
||||
}
|
||||
|
||||
// Should find all 7 enums
|
||||
expect(result.enums).toHaveLength(7);
|
||||
|
||||
@@ -97,11 +88,6 @@ ALTER TABLE "spells" ADD CONSTRAINT "spells_wizard_id_wizard_id_fk"
|
||||
'mythic',
|
||||
]);
|
||||
|
||||
// Check table parsing
|
||||
console.log('\n=== TABLES FOUND ===');
|
||||
console.log('Count:', result.tables.length);
|
||||
console.log('Names:', result.tables.map((t) => t.name).join(', '));
|
||||
|
||||
// Should find all 4 tables
|
||||
expect(result.tables).toHaveLength(4);
|
||||
expect(result.tables.map((t) => t.name).sort()).toEqual([
|
||||
@@ -111,15 +97,6 @@ ALTER TABLE "spells" ADD CONSTRAINT "spells_wizard_id_wizard_id_fk"
|
||||
'wizard_account',
|
||||
]);
|
||||
|
||||
// Check warnings for syntax issues
|
||||
console.log('\n=== WARNINGS ===');
|
||||
console.log('Count:', result.warnings?.length || 0);
|
||||
if (result.warnings) {
|
||||
result.warnings.forEach((w) => {
|
||||
console.log(` - ${w}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Should have warnings about custom types and parsing failures
|
||||
expect(result.warnings).toBeDefined();
|
||||
expect(result.warnings!.length).toBeGreaterThan(0);
|
||||
@@ -150,8 +127,7 @@ CREATE TABLE "dragons" (
|
||||
expect(result.enums).toHaveLength(1);
|
||||
expect(result.enums?.[0].name).toBe('dragon_element');
|
||||
|
||||
// Table might have issues due to missing space
|
||||
console.log('Tables:', result.tables.length);
|
||||
console.log('Warnings:', result.warnings);
|
||||
// Table might succeed or fail due to missing space syntax
|
||||
// The important thing is the enum was still parsed correctly
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,19 +11,8 @@ CREATE TABLE dragon_bonds (
|
||||
PRIMARY KEY (dragon_master_id, dragon_id)
|
||||
);`;
|
||||
|
||||
console.log('Testing with SQL:', sql);
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Result:', {
|
||||
tableCount: result.tables.length,
|
||||
tables: result.tables.map((t) => ({
|
||||
name: t.name,
|
||||
columns: t.columns.length,
|
||||
})),
|
||||
warnings: result.warnings,
|
||||
});
|
||||
|
||||
expect(result.tables).toHaveLength(1);
|
||||
expect(result.tables[0].name).toBe('dragon_bonds');
|
||||
});
|
||||
@@ -60,11 +49,6 @@ CREATE TABLE dragon_bonds (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('With dependencies:', {
|
||||
tableCount: result.tables.length,
|
||||
tableNames: result.tables.map((t) => t.name),
|
||||
});
|
||||
|
||||
expect(result.tables).toHaveLength(3);
|
||||
const dragonBonds = result.tables.find(
|
||||
(t) => t.name === 'dragon_bonds'
|
||||
|
||||
@@ -49,11 +49,6 @@ CREATE TABLE dragons (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log(
|
||||
'Parsed enums:',
|
||||
result.enums?.map((e) => e.name)
|
||||
);
|
||||
|
||||
expect(result.enums).toHaveLength(3);
|
||||
|
||||
// Specifically check for dragon_status
|
||||
|
||||
@@ -36,56 +36,6 @@ CREATE TABLE dragon_quests (
|
||||
// Parse the SQL
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
// Check enums
|
||||
console.log('\nEnum parsing results:');
|
||||
console.log(`Found ${result.enums?.length || 0} enum types`);
|
||||
|
||||
if (result.enums) {
|
||||
result.enums.forEach((e) => {
|
||||
console.log(` - ${e.name}: ${e.values.length} values`);
|
||||
});
|
||||
}
|
||||
|
||||
// Expected enums
|
||||
const expectedEnums = [
|
||||
'wizard_rank',
|
||||
'spell_frequency',
|
||||
'magic_school',
|
||||
'quest_status',
|
||||
'dragon_mood',
|
||||
];
|
||||
|
||||
// Check which are missing
|
||||
const foundEnumNames = result.enums?.map((e) => e.name) || [];
|
||||
const missingEnums = expectedEnums.filter(
|
||||
(e) => !foundEnumNames.includes(e)
|
||||
);
|
||||
|
||||
if (missingEnums.length > 0) {
|
||||
console.log('\nMissing enums:', missingEnums);
|
||||
|
||||
// Let's check if they're in the SQL at all
|
||||
missingEnums.forEach((enumName) => {
|
||||
const regex = new RegExp(`CREATE\\s+TYPE\\s+${enumName}`, 'i');
|
||||
if (regex.test(sql)) {
|
||||
console.log(
|
||||
` ${enumName} exists in SQL but wasn't parsed`
|
||||
);
|
||||
|
||||
// Find the line
|
||||
const lines = sql.split('\n');
|
||||
const lineIndex = lines.findIndex((line) =>
|
||||
regex.test(line)
|
||||
);
|
||||
if (lineIndex !== -1) {
|
||||
console.log(
|
||||
` Line ${lineIndex + 1}: ${lines[lineIndex].trim()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Convert to diagram
|
||||
const diagram = convertToChartDBDiagram(
|
||||
result,
|
||||
@@ -93,68 +43,54 @@ CREATE TABLE dragon_quests (
|
||||
DatabaseType.POSTGRESQL
|
||||
);
|
||||
|
||||
// Check custom types in diagram
|
||||
console.log(
|
||||
'\nCustom types in diagram:',
|
||||
diagram.customTypes?.length || 0
|
||||
);
|
||||
|
||||
// Check wizards table
|
||||
const wizardsTable = diagram.tables?.find((t) => t.name === 'wizards');
|
||||
if (wizardsTable) {
|
||||
console.log('\nWizards table:');
|
||||
const rankField = wizardsTable.fields.find(
|
||||
(f) => f.name === 'rank'
|
||||
);
|
||||
if (rankField) {
|
||||
console.log(
|
||||
` rank field type: ${rankField.type.name} (id: ${rankField.type.id})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check spellbooks table
|
||||
const spellbooksTable = diagram.tables?.find(
|
||||
(t) => t.name === 'spellbooks'
|
||||
);
|
||||
if (spellbooksTable) {
|
||||
console.log('\nSpellbooks table:');
|
||||
const frequencyField = spellbooksTable.fields.find(
|
||||
(f) => f.name === 'cast_frequency'
|
||||
);
|
||||
if (frequencyField) {
|
||||
console.log(
|
||||
` cast_frequency field type: ${frequencyField.type.name}`
|
||||
);
|
||||
}
|
||||
|
||||
const schoolField = spellbooksTable.fields.find(
|
||||
(f) => f.name === 'primary_school'
|
||||
);
|
||||
if (schoolField) {
|
||||
console.log(
|
||||
` primary_school field type: ${schoolField.type.name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Assertions
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(5);
|
||||
expect(diagram.customTypes).toHaveLength(5);
|
||||
|
||||
// Check that wizard_rank is present
|
||||
// Verify all expected enums are present
|
||||
const expectedEnums = [
|
||||
'wizard_rank',
|
||||
'spell_frequency',
|
||||
'magic_school',
|
||||
'quest_status',
|
||||
'dragon_mood',
|
||||
];
|
||||
const foundEnumNames = result.enums!.map((e) => e.name);
|
||||
expectedEnums.forEach((enumName) => {
|
||||
expect(foundEnumNames).toContain(enumName);
|
||||
});
|
||||
|
||||
// Check that wizard_rank is present with correct values
|
||||
const wizardRankEnum = result.enums!.find(
|
||||
(e) => e.name === 'wizard_rank'
|
||||
);
|
||||
expect(wizardRankEnum).toBeDefined();
|
||||
expect(wizardRankEnum!.values).toHaveLength(5);
|
||||
|
||||
// Check that the rank field uses wizard_rank type
|
||||
if (wizardsTable) {
|
||||
const rankField = wizardsTable.fields.find(
|
||||
(f) => f.name === 'rank'
|
||||
);
|
||||
expect(rankField?.type.name.toLowerCase()).toBe('wizard_rank');
|
||||
}
|
||||
const wizardsTable = diagram.tables?.find((t) => t.name === 'wizards');
|
||||
expect(wizardsTable).toBeDefined();
|
||||
const rankField = wizardsTable!.fields.find((f) => f.name === 'rank');
|
||||
expect(rankField).toBeDefined();
|
||||
expect(rankField!.type.name.toLowerCase()).toBe('wizard_rank');
|
||||
|
||||
// Check spellbooks table enum fields
|
||||
const spellbooksTable = diagram.tables?.find(
|
||||
(t) => t.name === 'spellbooks'
|
||||
);
|
||||
expect(spellbooksTable).toBeDefined();
|
||||
|
||||
const frequencyField = spellbooksTable!.fields.find(
|
||||
(f) => f.name === 'cast_frequency'
|
||||
);
|
||||
expect(frequencyField).toBeDefined();
|
||||
expect(frequencyField!.type.name.toLowerCase()).toBe('spell_frequency');
|
||||
|
||||
const schoolField = spellbooksTable!.fields.find(
|
||||
(f) => f.name === 'primary_school'
|
||||
);
|
||||
expect(schoolField).toBeDefined();
|
||||
expect(schoolField!.type.name.toLowerCase()).toBe('magic_school');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,25 +12,16 @@ CREATE TABLE spells (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Spells table result:', {
|
||||
tableCount: result.tables.length,
|
||||
columns: result.tables[0]?.columns.map((c) => ({
|
||||
name: c.name,
|
||||
type: c.type,
|
||||
})),
|
||||
});
|
||||
|
||||
expect(result.tables).toHaveLength(1);
|
||||
const spellsTable = result.tables[0];
|
||||
expect(spellsTable.name).toBe('spells');
|
||||
|
||||
// Debug: list all columns found
|
||||
console.log('Columns found:', spellsTable.columns.length);
|
||||
spellsTable.columns.forEach((col, idx) => {
|
||||
console.log(` ${idx + 1}. ${col.name}: ${col.type}`);
|
||||
});
|
||||
|
||||
expect(spellsTable.columns).toHaveLength(3);
|
||||
|
||||
// Verify all columns are present
|
||||
const columnNames = spellsTable.columns.map((c) => c.name);
|
||||
expect(columnNames).toContain('id');
|
||||
expect(columnNames).toContain('description');
|
||||
expect(columnNames).toContain('category');
|
||||
});
|
||||
|
||||
it('should handle magical enum types with mixed quotes', async () => {
|
||||
@@ -38,11 +29,6 @@ CREATE TABLE spells (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Enum result:', {
|
||||
enumCount: result.enums?.length || 0,
|
||||
values: result.enums?.[0]?.values,
|
||||
});
|
||||
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(1);
|
||||
expect(result.enums![0].values).toEqual([
|
||||
|
||||
@@ -22,14 +22,6 @@ CREATE TABLE spellbooks (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
// Debug output
|
||||
console.log('Enums found:', result.enums?.length || 0);
|
||||
if (result.enums) {
|
||||
result.enums.forEach((e) => {
|
||||
console.log(` - ${e.name}`);
|
||||
});
|
||||
}
|
||||
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(5);
|
||||
|
||||
|
||||
@@ -32,48 +32,19 @@ CREATE TABLE creature_abilities (
|
||||
);
|
||||
`;
|
||||
|
||||
console.log(
|
||||
'Testing PostgreSQL parser with CREATE EXTENSION and CREATE TYPE...\n'
|
||||
);
|
||||
const result = await fromPostgres(testSQL);
|
||||
|
||||
try {
|
||||
const result = await fromPostgres(testSQL);
|
||||
// Basic assertions
|
||||
expect(result.tables.length).toBe(2);
|
||||
expect(result.tables[0].name).toBe('mystical_creatures');
|
||||
expect(result.tables[1].name).toBe('creature_abilities');
|
||||
expect(result.relationships.length).toBe(1);
|
||||
|
||||
console.log('Parse successful!');
|
||||
console.log('\nTables found:', result.tables.length);
|
||||
result.tables.forEach((table) => {
|
||||
console.log(`\n- Table: ${table.name}`);
|
||||
console.log(' Columns:');
|
||||
table.columns.forEach((col) => {
|
||||
console.log(
|
||||
` - ${col.name}: ${col.type}${col.nullable ? '' : ' NOT NULL'}${col.primaryKey ? ' PRIMARY KEY' : ''}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
console.log('\nRelationships found:', result.relationships.length);
|
||||
result.relationships.forEach((rel) => {
|
||||
console.log(
|
||||
`- ${rel.sourceTable}.${rel.sourceColumn} -> ${rel.targetTable}.${rel.targetColumn}`
|
||||
);
|
||||
});
|
||||
|
||||
if (result.warnings && result.warnings.length > 0) {
|
||||
console.log('\nWarnings:');
|
||||
result.warnings.forEach((warning) => {
|
||||
console.log(`- ${warning}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Basic assertions
|
||||
expect(result.tables.length).toBe(2);
|
||||
expect(result.tables[0].name).toBe('mystical_creatures');
|
||||
expect(result.tables[1].name).toBe('creature_abilities');
|
||||
expect(result.relationships.length).toBe(1);
|
||||
} catch (error) {
|
||||
console.error('Error parsing SQL:', (error as Error).message);
|
||||
console.error('\nStack trace:', (error as Error).stack);
|
||||
throw error;
|
||||
}
|
||||
// Verify enums are parsed
|
||||
expect(result.enums).toHaveLength(2);
|
||||
expect(result.enums?.map((e) => e.name).sort()).toEqual([
|
||||
'creature_alignment',
|
||||
'magic_school',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,64 +140,30 @@ CREATE TABLE guild_master_actions (
|
||||
|
||||
// First, verify the table exists in the SQL
|
||||
const tableExists = sql.includes('CREATE TABLE quest_sample_rewards');
|
||||
console.log('\nDebugging quest_sample_rewards:');
|
||||
console.log('- Table exists in SQL:', tableExists);
|
||||
|
||||
// Extract the specific table definition
|
||||
const tableMatch = sql.match(
|
||||
/-- Junction table[\s\S]*?CREATE TABLE quest_sample_rewards[\s\S]*?;/
|
||||
);
|
||||
if (tableMatch) {
|
||||
console.log('- Table definition found, first 200 chars:');
|
||||
console.log(tableMatch[0].substring(0, 200) + '...');
|
||||
}
|
||||
|
||||
// Now parse
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('\nParsing results:');
|
||||
console.log('- Total tables:', result.tables.length);
|
||||
console.log(
|
||||
'- Table names:',
|
||||
result.tables.map((t) => t.name).join(', ')
|
||||
);
|
||||
|
||||
// Look for quest_sample_rewards
|
||||
const questSampleRewards = result.tables.find(
|
||||
(t) => t.name === 'quest_sample_rewards'
|
||||
);
|
||||
console.log('- quest_sample_rewards found:', !!questSampleRewards);
|
||||
|
||||
if (!questSampleRewards) {
|
||||
// Check warnings for clues
|
||||
console.log('\nWarnings that might be relevant:');
|
||||
result.warnings?.forEach((w, i) => {
|
||||
if (
|
||||
w.includes('quest_sample_rewards') ||
|
||||
w.includes('Failed to parse')
|
||||
) {
|
||||
console.log(` ${i}: ${w}`);
|
||||
}
|
||||
});
|
||||
|
||||
// List all tables to see what's missing
|
||||
console.log('\nAll parsed tables:');
|
||||
result.tables.forEach((t, i) => {
|
||||
console.log(
|
||||
` ${i + 1}. ${t.name} (${t.columns.length} columns)`
|
||||
);
|
||||
});
|
||||
} else {
|
||||
console.log('\nquest_sample_rewards details:');
|
||||
console.log('- Columns:', questSampleRewards.columns.length);
|
||||
questSampleRewards.columns.forEach((c) => {
|
||||
console.log(` - ${c.name}: ${c.type}`);
|
||||
});
|
||||
}
|
||||
|
||||
// The test expectation
|
||||
expect(tableExists).toBe(true);
|
||||
expect(result.tables.length).toBeGreaterThanOrEqual(19); // At least 19 tables
|
||||
expect(questSampleRewards).toBeDefined();
|
||||
|
||||
// Verify quest_sample_rewards has correct columns
|
||||
expect(questSampleRewards!.columns).toHaveLength(2);
|
||||
const columnNames = questSampleRewards!.columns.map((c) => c.name);
|
||||
expect(columnNames).toContain('quest_template_id');
|
||||
expect(columnNames).toContain('reward_id');
|
||||
|
||||
// Verify no parsing warnings for this table
|
||||
const questSampleRewardsWarnings = result.warnings?.filter((w) =>
|
||||
w.includes('quest_sample_rewards')
|
||||
);
|
||||
expect(questSampleRewardsWarnings?.length ?? 0).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,12 +15,6 @@ CREATE TABLE towers (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log(
|
||||
'Tables:',
|
||||
result.tables.map((t) => t.name)
|
||||
);
|
||||
console.log('Relationships:', result.relationships);
|
||||
|
||||
expect(result.tables).toHaveLength(2);
|
||||
expect(result.relationships).toHaveLength(1);
|
||||
expect(result.relationships[0].sourceTable).toBe('towers');
|
||||
@@ -43,13 +37,6 @@ CREATE TABLE quests (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log(
|
||||
'Tables:',
|
||||
result.tables.map((t) => t.name)
|
||||
);
|
||||
console.log('Relationships:', result.relationships);
|
||||
console.log('Warnings:', result.warnings);
|
||||
|
||||
expect(result.tables).toHaveLength(2);
|
||||
expect(result.relationships).toHaveLength(1);
|
||||
});
|
||||
|
||||
@@ -40,19 +40,13 @@ CREATE TABLE plan_sample_spells (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Parsing results:');
|
||||
console.log(
|
||||
'- Tables:',
|
||||
result.tables.map((t) => t.name)
|
||||
);
|
||||
console.log('- Table count:', result.tables.length);
|
||||
console.log('- Relationships:', result.relationships.length);
|
||||
console.log('- Enums:', result.enums?.length || 0);
|
||||
|
||||
// Should have 3 tables
|
||||
// Verify parsing results
|
||||
expect(result.tables).toHaveLength(3);
|
||||
expect(result.relationships).toHaveLength(2);
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(5);
|
||||
|
||||
// Check table names
|
||||
// Verify table names
|
||||
const tableNames = result.tables.map((t) => t.name).sort();
|
||||
expect(tableNames).toEqual([
|
||||
'plan_sample_spells',
|
||||
@@ -60,19 +54,12 @@ CREATE TABLE plan_sample_spells (
|
||||
'spells',
|
||||
]);
|
||||
|
||||
// Should have 2 relationships (both from plan_sample_spells)
|
||||
expect(result.relationships).toHaveLength(2);
|
||||
|
||||
// Check plan_sample_spells specifically
|
||||
const planSampleSpells = result.tables.find(
|
||||
(t) => t.name === 'plan_sample_spells'
|
||||
);
|
||||
expect(planSampleSpells).toBeDefined();
|
||||
expect(planSampleSpells!.columns).toHaveLength(2);
|
||||
|
||||
// Should have 5 enum types
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(5);
|
||||
});
|
||||
|
||||
it('should parse the exact junction table definition', async () => {
|
||||
|
||||
@@ -15,11 +15,6 @@ CREATE TABLE test_table (
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
// Even with syntax error, it should try to parse what it can
|
||||
console.log('Result:', {
|
||||
tables: result.tables.length,
|
||||
warnings: result.warnings,
|
||||
});
|
||||
|
||||
// Should attempt to parse the table even if parser fails
|
||||
expect(result.tables.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
@@ -43,12 +38,6 @@ CREATE TABLE table3 (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Multi-table result:', {
|
||||
tableCount: result.tables.length,
|
||||
tableNames: result.tables.map((t) => t.name),
|
||||
warnings: result.warnings?.length || 0,
|
||||
});
|
||||
|
||||
// Should parse at least table1 and table3
|
||||
expect(result.tables.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
|
||||
@@ -14,12 +14,6 @@ CREATE TABLE wizard_spells (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Test results:', {
|
||||
tableCount: result.tables.length,
|
||||
tableNames: result.tables.map((t) => t.name),
|
||||
warnings: result.warnings,
|
||||
});
|
||||
|
||||
expect(result.tables).toHaveLength(1);
|
||||
expect(result.tables[0].name).toBe('wizard_spells');
|
||||
});
|
||||
@@ -201,46 +195,23 @@ CREATE TABLE guild_master_actions (
|
||||
|
||||
// Count CREATE TABLE statements
|
||||
const createTableMatches = sql.match(/CREATE TABLE/gi) || [];
|
||||
console.log(
|
||||
`\nFound ${createTableMatches.length} CREATE TABLE statements in file`
|
||||
);
|
||||
|
||||
// Find all table names
|
||||
const tableNameMatches =
|
||||
sql.match(
|
||||
/CREATE TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["']?(\w+)["']?/gi
|
||||
) || [];
|
||||
const tableNames = tableNameMatches
|
||||
.map((match) => {
|
||||
const nameMatch = match.match(
|
||||
/CREATE TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["']?(\w+)["']?/i
|
||||
);
|
||||
return nameMatch ? nameMatch[1] : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
console.log('Table names found in SQL:', tableNames);
|
||||
console.log(
|
||||
'quest_sample_rewards in list?',
|
||||
tableNames.includes('quest_sample_rewards')
|
||||
);
|
||||
|
||||
// Parse the file
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log(`\nParsed ${result.tables.length} tables`);
|
||||
console.log(
|
||||
'Parsed table names:',
|
||||
result.tables.map((t) => t.name).sort()
|
||||
);
|
||||
|
||||
const junctionTable = result.tables.find(
|
||||
(t) => t.name.includes('_') && t.columns.length >= 2
|
||||
);
|
||||
console.log('junction table found?', !!junctionTable);
|
||||
|
||||
// All CREATE TABLE statements should be parsed
|
||||
expect(result.tables.length).toBe(createTableMatches.length);
|
||||
expect(junctionTable).toBeDefined();
|
||||
|
||||
// Verify quest_sample_rewards is parsed
|
||||
const questSampleRewards = result.tables.find(
|
||||
(t) => t.name === 'quest_sample_rewards'
|
||||
);
|
||||
expect(questSampleRewards).toBeDefined();
|
||||
expect(questSampleRewards!.columns).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -241,49 +241,13 @@ CREATE TABLE audit_logs (
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);`;
|
||||
|
||||
console.log('Parsing SQL...');
|
||||
const startTime = Date.now();
|
||||
const result = await fromPostgres(sql);
|
||||
const parseTime = Date.now() - startTime;
|
||||
|
||||
console.log(`Parse completed in ${parseTime}ms`);
|
||||
|
||||
// Expected counts
|
||||
const expectedTables = 27;
|
||||
const expectedEnums = 15;
|
||||
const minExpectedRelationships = 36; // Adjusted based on actual relationships in the schema
|
||||
|
||||
console.log('\n=== PARSING RESULTS ===');
|
||||
console.log(
|
||||
`Tables parsed: ${result.tables.length} (expected: ${expectedTables})`
|
||||
);
|
||||
console.log(
|
||||
`Enums parsed: ${result.enums?.length || 0} (expected: ${expectedEnums})`
|
||||
);
|
||||
console.log(
|
||||
`Relationships parsed: ${result.relationships.length} (expected min: ${minExpectedRelationships})`
|
||||
);
|
||||
console.log(`Warnings: ${result.warnings?.length || 0}`);
|
||||
|
||||
// List parsed tables
|
||||
console.log('\n=== TABLES PARSED ===');
|
||||
const tableNames = result.tables.map((t) => t.name).sort();
|
||||
tableNames.forEach((name) => console.log(`- ${name}`));
|
||||
|
||||
// List enums
|
||||
if (result.enums && result.enums.length > 0) {
|
||||
console.log('\n=== ENUMS PARSED ===');
|
||||
result.enums.forEach((e) => {
|
||||
console.log(`- ${e.name}: ${e.values.length} values`);
|
||||
});
|
||||
}
|
||||
|
||||
// Show warnings if any
|
||||
if (result.warnings && result.warnings.length > 0) {
|
||||
console.log('\n=== WARNINGS ===');
|
||||
result.warnings.forEach((w) => console.log(`- ${w}`));
|
||||
}
|
||||
|
||||
// Verify counts
|
||||
expect(result.tables).toHaveLength(expectedTables);
|
||||
expect(result.enums).toBeDefined();
|
||||
|
||||
@@ -29,13 +29,6 @@ CREATE TABLE schema1.table_with_schema (id INTEGER PRIMARY KEY);`;
|
||||
// Count CREATE TABLE statements in the SQL
|
||||
const createTableCount = (sql.match(/CREATE TABLE/gi) || []).length;
|
||||
|
||||
console.log(`\nValidation:`);
|
||||
console.log(`- CREATE TABLE statements in SQL: ${createTableCount}`);
|
||||
console.log(`- Tables parsed: ${result.tables.length}`);
|
||||
console.log(
|
||||
`- Table names: ${result.tables.map((t) => t.name).join(', ')}`
|
||||
);
|
||||
|
||||
// All CREATE TABLE statements should result in a parsed table
|
||||
expect(result.tables).toHaveLength(createTableCount);
|
||||
|
||||
@@ -82,20 +75,14 @@ CREATE TABLE complex_constraints (
|
||||
|
||||
const createTableCount = (sql.match(/CREATE TABLE/gi) || []).length;
|
||||
|
||||
console.log(`\nEdge case validation:`);
|
||||
console.log(`- CREATE TABLE statements: ${createTableCount}`);
|
||||
console.log(`- Tables parsed: ${result.tables.length}`);
|
||||
console.log(
|
||||
`- Expected tables: only_fks, no_pk, empty_table, complex_constraints`
|
||||
);
|
||||
console.log(
|
||||
`- Actual tables: ${result.tables.map((t) => t.name).join(', ')}`
|
||||
);
|
||||
result.tables.forEach((t) => {
|
||||
console.log(`- ${t.name}: ${t.columns.length} columns`);
|
||||
});
|
||||
|
||||
// Even edge cases should be parsed
|
||||
expect(result.tables).toHaveLength(createTableCount);
|
||||
|
||||
// Verify the expected tables are present
|
||||
const tableNames = result.tables.map((t) => t.name).sort();
|
||||
expect(tableNames).toContain('only_fks');
|
||||
expect(tableNames).toContain('no_pk');
|
||||
expect(tableNames).toContain('empty_table');
|
||||
expect(tableNames).toContain('complex_constraints');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,14 +17,8 @@ CREATE TYPE ritual_status AS ENUM ('pending', 'channeling', 'completed', 'failed
|
||||
CREATE TYPE mana_status AS ENUM ('pending', 'charged', 'depleted');
|
||||
`;
|
||||
|
||||
console.log('Testing with fromPostgres...');
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log(
|
||||
'Enums found:',
|
||||
result.enums?.map((e) => e.name)
|
||||
);
|
||||
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(5);
|
||||
|
||||
|
||||
@@ -55,13 +55,6 @@ CREATE INDEX "grimoires_category_idx" ON "grimoires" ("category");
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
// Verify enum parsing
|
||||
console.log('\n=== IMPORT RESULTS ===');
|
||||
console.log(`Enums parsed: ${result.enums?.length || 0}`);
|
||||
console.log(`Tables parsed: ${result.tables.length}`);
|
||||
console.log(`Relationships found: ${result.relationships.length}`);
|
||||
console.log(`Warnings: ${result.warnings?.length || 0}`);
|
||||
|
||||
// All enums should be parsed despite schema qualification
|
||||
expect(result.enums).toHaveLength(3);
|
||||
expect(result.enums?.map((e) => e.name).sort()).toEqual([
|
||||
@@ -98,12 +91,6 @@ CREATE INDEX "grimoires_category_idx" ON "grimoires" ("category");
|
||||
'master',
|
||||
'archmage',
|
||||
]);
|
||||
|
||||
// Log warnings for visibility
|
||||
if (result.warnings && result.warnings.length > 0) {
|
||||
console.log('\n=== WARNINGS ===');
|
||||
result.warnings.forEach((w) => console.log(`- ${w}`));
|
||||
}
|
||||
});
|
||||
|
||||
it('should provide actionable feedback for common syntax issues', async () => {
|
||||
|
||||
@@ -16,13 +16,6 @@ CREATE TABLE "wizards" (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Enums found:', result.enums?.length || 0);
|
||||
if (result.enums) {
|
||||
result.enums.forEach((e) => {
|
||||
console.log(` - ${e.name}: ${e.values.join(', ')}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Should find both enums
|
||||
expect(result.enums).toHaveLength(2);
|
||||
|
||||
@@ -64,8 +57,7 @@ CREATE TABLE "dragons" (
|
||||
expect(result.enums).toHaveLength(1);
|
||||
expect(result.enums?.[0].name).toBe('dragon_type');
|
||||
|
||||
// Table parsing might fail due to syntax error
|
||||
console.log('Tables found:', result.tables.length);
|
||||
console.log('Warnings:', result.warnings);
|
||||
// Table parsing might succeed or fail due to missing space syntax
|
||||
// The important thing is the enum was still parsed correctly
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,13 +14,6 @@ CREATE TYPE mana_status AS ENUM ('pending', 'charged', 'depleted');
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Result enums:', result.enums?.length || 0);
|
||||
if (result.enums) {
|
||||
result.enums.forEach((e) => {
|
||||
console.log(` - ${e.name}`);
|
||||
});
|
||||
}
|
||||
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(5);
|
||||
});
|
||||
@@ -48,9 +41,6 @@ CREATE TYPE mana_status AS ENUM ('pending', 'charged', 'depleted');
|
||||
for (const enumDef of enums) {
|
||||
const result = await fromPostgres(enumDef.sql);
|
||||
|
||||
console.log(`\nTesting ${enumDef.name}:`);
|
||||
console.log(` Found enums: ${result.enums?.length || 0}`);
|
||||
|
||||
expect(result.enums).toBeDefined();
|
||||
expect(result.enums).toHaveLength(1);
|
||||
expect(result.enums![0].name).toBe(enumDef.name);
|
||||
|
||||
@@ -44,16 +44,8 @@ CREATE TABLE plan_sample_spells (
|
||||
PRIMARY KEY (spell_plan_id, spell_id)
|
||||
);`;
|
||||
|
||||
console.log('Testing exact SQL from forth example...');
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Results:', {
|
||||
tables: result.tables.length,
|
||||
tableNames: result.tables.map((t) => t.name),
|
||||
warnings: result.warnings?.length || 0,
|
||||
});
|
||||
|
||||
// Should have 3 tables
|
||||
expect(result.tables).toHaveLength(3);
|
||||
|
||||
|
||||
@@ -11,15 +11,6 @@ CREATE TABLE spell_ingredients (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('String preservation result:', {
|
||||
tableCount: result.tables.length,
|
||||
columns: result.tables[0]?.columns.map((c) => ({
|
||||
name: c.name,
|
||||
type: c.type,
|
||||
default: c.default,
|
||||
})),
|
||||
});
|
||||
|
||||
expect(result.tables).toHaveLength(1);
|
||||
expect(result.tables[0].columns).toHaveLength(2);
|
||||
|
||||
|
||||
@@ -21,12 +21,6 @@ CREATE TABLE table3 (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('Test results:', {
|
||||
tableCount: result.tables.length,
|
||||
tableNames: result.tables.map((t) => t.name),
|
||||
warnings: result.warnings,
|
||||
});
|
||||
|
||||
// Should parse all 3 tables even though table2 has undefined reference
|
||||
expect(result.tables).toHaveLength(3);
|
||||
|
||||
|
||||
@@ -62,14 +62,6 @@ CREATE TABLE rewards (
|
||||
// Use the improved parser
|
||||
const parserResult = await fromPostgres(sql);
|
||||
|
||||
console.log('\nParser Result:');
|
||||
console.log('- Enums found:', parserResult.enums?.length || 0);
|
||||
if (parserResult.enums) {
|
||||
parserResult.enums.forEach((e) => {
|
||||
console.log(` - ${e.name}: ${e.values.length} values`);
|
||||
});
|
||||
}
|
||||
|
||||
// Convert to diagram
|
||||
const diagram = convertToChartDBDiagram(
|
||||
parserResult,
|
||||
@@ -77,33 +69,6 @@ CREATE TABLE rewards (
|
||||
DatabaseType.POSTGRESQL
|
||||
);
|
||||
|
||||
console.log('\nDiagram Result:');
|
||||
console.log('- Custom types:', diagram.customTypes?.length || 0);
|
||||
if (diagram.customTypes) {
|
||||
diagram.customTypes.forEach((t) => {
|
||||
console.log(` - ${t.name} (${t.kind})`);
|
||||
});
|
||||
}
|
||||
|
||||
// Check contracts table
|
||||
const contractsTable = diagram.tables?.find(
|
||||
(t) => t.name === 'contracts'
|
||||
);
|
||||
if (contractsTable) {
|
||||
console.log('\nContracts table enum fields:');
|
||||
const enumFields = ['status'];
|
||||
enumFields.forEach((fieldName) => {
|
||||
const field = contractsTable.fields.find(
|
||||
(f) => f.name === fieldName
|
||||
);
|
||||
if (field) {
|
||||
console.log(
|
||||
` - ${field.name}: ${field.type.name} (id: ${field.type.id})`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Assertions
|
||||
expect(parserResult.enums).toHaveLength(5);
|
||||
expect(diagram.customTypes).toHaveLength(5);
|
||||
|
||||
@@ -203,39 +203,18 @@ CREATE TABLE guild_master_actions (
|
||||
|
||||
const result = await fromPostgres(sql);
|
||||
|
||||
console.log('\n=== PARSING RESULTS ===');
|
||||
console.log(`Tables parsed: ${result.tables.length}`);
|
||||
console.log(`Expected: ${expectedTables.length}`);
|
||||
|
||||
const parsedTableNames = result.tables.map((t) => t.name).sort();
|
||||
console.log('\nParsed tables:');
|
||||
parsedTableNames.forEach((name, i) => {
|
||||
console.log(` ${i + 1}. ${name}`);
|
||||
});
|
||||
|
||||
// Find missing tables
|
||||
// Find missing tables and verify none are missing
|
||||
const missingTables = expectedTables.filter(
|
||||
(expected) => !parsedTableNames.includes(expected)
|
||||
);
|
||||
if (missingTables.length > 0) {
|
||||
console.log('\nMissing tables:');
|
||||
missingTables.forEach((name) => {
|
||||
console.log(` - ${name}`);
|
||||
});
|
||||
}
|
||||
expect(missingTables).toHaveLength(0);
|
||||
|
||||
// Check for quest_sample_rewards specifically
|
||||
const questSampleRewards = result.tables.find(
|
||||
(t) => t.name === 'quest_sample_rewards'
|
||||
);
|
||||
console.log(`\nquest_sample_rewards found: ${!!questSampleRewards}`);
|
||||
if (questSampleRewards) {
|
||||
console.log('quest_sample_rewards details:');
|
||||
console.log(` - Columns: ${questSampleRewards.columns.length}`);
|
||||
questSampleRewards.columns.forEach((col) => {
|
||||
console.log(` - ${col.name}: ${col.type}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Verify all tables were parsed
|
||||
expect(result.tables).toHaveLength(expectedTables.length);
|
||||
@@ -249,11 +228,5 @@ CREATE TABLE guild_master_actions (
|
||||
.map((c) => c.name)
|
||||
.sort();
|
||||
expect(columnNames).toEqual(['quest_template_id', 'reward_id']);
|
||||
|
||||
// Check warnings if any
|
||||
if (result.warnings && result.warnings.length > 0) {
|
||||
console.log('\nWarnings:');
|
||||
result.warnings.forEach((w) => console.log(` - ${w}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,32 +20,8 @@ CREATE TABLE [DBO].[SpellComponent](
|
||||
[ITSSCHOOLMETA] [VARCHAR](32), FOREIGN KEY (itsschoolmeta) REFERENCES MagicSchool(SCHOOLID),
|
||||
[KEYATTR] CHAR (100), ) ON [PRIMARY]`;
|
||||
|
||||
console.log('Testing complex fantasy SQL...');
|
||||
console.log(
|
||||
'Number of CREATE TABLE statements:',
|
||||
(sql.match(/CREATE\s+TABLE/gi) || []).length
|
||||
);
|
||||
|
||||
const result = await fromSQLServer(sql);
|
||||
|
||||
console.log(
|
||||
'Result tables:',
|
||||
result.tables.map((t) => t.name)
|
||||
);
|
||||
console.log('Result relationships:', result.relationships.length);
|
||||
|
||||
// Debug: Show actual relationships
|
||||
if (result.relationships.length === 0) {
|
||||
console.log('WARNING: No relationships found!');
|
||||
} else {
|
||||
console.log('Relationships found:');
|
||||
result.relationships.forEach((r) => {
|
||||
console.log(
|
||||
` ${r.sourceTable}.${r.sourceColumn} -> ${r.targetTable}.${r.targetColumn}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Should create TWO tables
|
||||
expect(result.tables).toHaveLength(2);
|
||||
|
||||
|
||||
@@ -60,28 +60,6 @@ CREATE TABLE [DBO].[SpellComponent](
|
||||
)
|
||||
);
|
||||
expect(fk2).toBeDefined();
|
||||
|
||||
console.log(
|
||||
'Full flow test - Relationships created:',
|
||||
diagram.relationships?.length
|
||||
);
|
||||
diagram.relationships?.forEach((r) => {
|
||||
const sourceTable = diagram.tables?.find(
|
||||
(t) => t.id === r.sourceTableId
|
||||
);
|
||||
const targetTable = diagram.tables?.find(
|
||||
(t) => t.id === r.targetTableId
|
||||
);
|
||||
const sourceField = sourceTable?.fields.find(
|
||||
(f) => f.id === r.sourceFieldId
|
||||
);
|
||||
const targetField = targetTable?.fields.find(
|
||||
(f) => f.id === r.targetFieldId
|
||||
);
|
||||
console.log(
|
||||
` ${sourceTable?.name}.${sourceField?.name} -> ${targetTable?.name}.${targetField?.name}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle case-insensitive field matching', async () => {
|
||||
|
||||
@@ -415,10 +415,8 @@ ALTER TABLE [marketplace].[transactions] ADD CONSTRAINT [FK_MarketTransactions_P
|
||||
(name) => !foundRelationshipNames.includes(name)
|
||||
);
|
||||
|
||||
if (missingRelationships.length > 0) {
|
||||
console.log('Missing relationships:', missingRelationships);
|
||||
console.log('Found relationships:', foundRelationshipNames);
|
||||
}
|
||||
// Verify all expected relationships were found
|
||||
expect(missingRelationships.length).toBe(0);
|
||||
|
||||
// Verify relationships count - we have 32 working relationships
|
||||
expect(result.relationships.length).toBe(32);
|
||||
@@ -509,16 +507,6 @@ ALTER TABLE [marketplace].[transactions] ADD CONSTRAINT [FK_MarketTransactions_P
|
||||
r.targetSchema === 'realm'
|
||||
);
|
||||
expect(citiesToKingdoms).toBeDefined();
|
||||
|
||||
console.log('Multi-schema test results:');
|
||||
console.log('Total schemas:', schemas.size);
|
||||
console.log('Total tables:', result.tables.length);
|
||||
console.log('Total relationships:', result.relationships.length);
|
||||
console.log(
|
||||
'Cross-schema relationships:',
|
||||
crossSchemaRelationships.length
|
||||
);
|
||||
console.log('Within-schema relationships:', withinSchemaRels.length);
|
||||
});
|
||||
|
||||
it('should handle mixed schema notation formats', async () => {
|
||||
|
||||
@@ -164,10 +164,6 @@ describe('SQL Server Foreign Key Relationship Tests', () => {
|
||||
|
||||
const result = await fromSQLServer(sql);
|
||||
|
||||
// Debug output
|
||||
console.log('Total tables:', result.tables.length);
|
||||
console.log('Total relationships:', result.relationships.length);
|
||||
|
||||
// Check if we have the expected number of tables and relationships
|
||||
expect(result.tables).toHaveLength(4);
|
||||
expect(result.relationships).toHaveLength(4);
|
||||
@@ -182,37 +178,22 @@ describe('SQL Server Foreign Key Relationship Tests', () => {
|
||||
|
||||
expect(spellCastingRel).toBeDefined();
|
||||
|
||||
if (spellCastingRel) {
|
||||
// Find the corresponding tables
|
||||
const spellTable = result.tables.find(
|
||||
(t) => t.name === 'Spell' && t.schema === 'spellcasting'
|
||||
);
|
||||
const spellCastingProcessTable = result.tables.find(
|
||||
(t) =>
|
||||
t.name === 'SpellCastingProcess' &&
|
||||
t.schema === 'spellcasting'
|
||||
);
|
||||
// Find the corresponding tables
|
||||
const spellTable = result.tables.find(
|
||||
(t) => t.name === 'Spell' && t.schema === 'spellcasting'
|
||||
);
|
||||
const spellCastingProcessTable = result.tables.find(
|
||||
(t) =>
|
||||
t.name === 'SpellCastingProcess' && t.schema === 'spellcasting'
|
||||
);
|
||||
|
||||
console.log('SpellCastingProcess relationship:', {
|
||||
sourceTableId: spellCastingRel.sourceTableId,
|
||||
targetTableId: spellCastingRel.targetTableId,
|
||||
spellCastingProcessTableId: spellCastingProcessTable?.id,
|
||||
spellTableId: spellTable?.id,
|
||||
isSourceIdValid:
|
||||
spellCastingRel.sourceTableId ===
|
||||
spellCastingProcessTable?.id,
|
||||
isTargetIdValid:
|
||||
spellCastingRel.targetTableId === spellTable?.id,
|
||||
});
|
||||
|
||||
// Verify the IDs are properly linked
|
||||
expect(spellCastingRel.sourceTableId).toBeTruthy();
|
||||
expect(spellCastingRel.targetTableId).toBeTruthy();
|
||||
expect(spellCastingRel.sourceTableId).toBe(
|
||||
spellCastingProcessTable!.id
|
||||
);
|
||||
expect(spellCastingRel.targetTableId).toBe(spellTable!.id);
|
||||
}
|
||||
// Verify the IDs are properly linked
|
||||
expect(spellCastingRel!.sourceTableId).toBeTruthy();
|
||||
expect(spellCastingRel!.targetTableId).toBeTruthy();
|
||||
expect(spellCastingRel!.sourceTableId).toBe(
|
||||
spellCastingProcessTable!.id
|
||||
);
|
||||
expect(spellCastingRel!.targetTableId).toBe(spellTable!.id);
|
||||
|
||||
// Check the apprentice self-referencing relationships
|
||||
const apprenticeWizardRel = result.relationships.find(
|
||||
@@ -241,13 +222,6 @@ describe('SQL Server Foreign Key Relationship Tests', () => {
|
||||
r.targetTableId === ''
|
||||
);
|
||||
|
||||
if (relationshipsWithMissingIds.length > 0) {
|
||||
console.log(
|
||||
'Relationships with missing IDs:',
|
||||
relationshipsWithMissingIds.slice(0, 5)
|
||||
);
|
||||
}
|
||||
|
||||
expect(relationshipsWithMissingIds).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -598,13 +598,6 @@ ALTER TABLE [Transactions] ADD CONSTRAINT [FK_Transactions_Currency]
|
||||
|
||||
const result = await fromSQLServer(sql);
|
||||
|
||||
// Debug: log table names to see what's parsed
|
||||
console.log('Tables found:', result.tables.length);
|
||||
console.log(
|
||||
'Table names:',
|
||||
result.tables.map((t) => t.name)
|
||||
);
|
||||
|
||||
// Verify correct number of tables
|
||||
expect(result.tables.length).toBe(37); // Actually 37 tables after counting
|
||||
|
||||
@@ -614,7 +607,6 @@ ALTER TABLE [Transactions] ADD CONSTRAINT [FK_Transactions_Currency]
|
||||
expect(schemas.has('dbo')).toBe(true);
|
||||
|
||||
// Verify correct number of relationships
|
||||
console.log('Relationships found:', result.relationships.length);
|
||||
expect(result.relationships.length).toBe(55); // 55 foreign key relationships that can be parsed
|
||||
|
||||
// Verify all relationships have valid source and target table IDs
|
||||
@@ -682,23 +674,5 @@ ALTER TABLE [Transactions] ADD CONSTRAINT [FK_Transactions_Currency]
|
||||
expect(rel.sourceTableId).toBe(sourceTable?.id);
|
||||
expect(rel.targetTableId).toBe(targetTable?.id);
|
||||
}
|
||||
|
||||
console.log('Single-schema test results:');
|
||||
console.log('Total tables:', result.tables.length);
|
||||
console.log('Total relationships:', result.relationships.length);
|
||||
console.log(
|
||||
'All relationships properly linked:',
|
||||
validRelationships.length === result.relationships.length
|
||||
);
|
||||
|
||||
// Sample of relationship names for verification
|
||||
const sampleRelationships = result.relationships
|
||||
.slice(0, 5)
|
||||
.map((r) => ({
|
||||
name: r.name,
|
||||
source: `${r.sourceTable}.${r.sourceColumn}`,
|
||||
target: `${r.targetTable}.${r.targetColumn}`,
|
||||
}));
|
||||
console.log('Sample relationships:', sampleRelationships);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,20 +54,6 @@ CREATE TABLE [DBO].[SpellComponent](
|
||||
expect(fk2?.targetColumn).toBe('SPELLID');
|
||||
expect(fk2?.sourceTableId).toBeTruthy();
|
||||
expect(fk2?.targetTableId).toBeTruthy();
|
||||
|
||||
// Log for debugging
|
||||
console.log('\n=== FK Verification Results ===');
|
||||
console.log(
|
||||
'Tables:',
|
||||
result.tables.map((t) => `${t.schema}.${t.name}`)
|
||||
);
|
||||
console.log('Total FKs found:', result.relationships.length);
|
||||
result.relationships.forEach((r, i) => {
|
||||
console.log(
|
||||
`FK ${i + 1}: ${r.sourceTable}.${r.sourceColumn} -> ${r.targetTable}.${r.targetColumn}`
|
||||
);
|
||||
console.log(` IDs: ${r.sourceTableId} -> ${r.targetTableId}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse inline FOREIGN KEY syntax correctly', async () => {
|
||||
|
||||
@@ -596,6 +596,666 @@ describe('Apply DBML Changes - single table', () => {
|
||||
// Check that the new field is added correctly
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should preserve index name', () => {
|
||||
const sourceDiagram: Diagram = {
|
||||
id: 'mqqwkkod9jb8',
|
||||
name: 'buckle_db',
|
||||
createdAt: new Date('2025-12-04T16:00:06.463Z'),
|
||||
updatedAt: new Date('2025-12-04T16:06:49.070Z'),
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
tables: [
|
||||
{
|
||||
id: 'r1rp4f64dtpifw7mub089bxy8',
|
||||
name: 'buckle_diagrams_history',
|
||||
schema: 'public',
|
||||
x: 100,
|
||||
y: 300,
|
||||
fields: [
|
||||
{
|
||||
id: '0t0b4e7irmvw53w4qyz99zqm1',
|
||||
name: 'rownum',
|
||||
type: {
|
||||
id: 'int',
|
||||
name: 'int',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: false,
|
||||
increment: true,
|
||||
isArray: false,
|
||||
createdAt: 1764864006454,
|
||||
},
|
||||
{
|
||||
id: 'hugwwfirbk609ejryqqsvw3be',
|
||||
name: 'id',
|
||||
type: {
|
||||
id: 'uuid',
|
||||
name: 'uuid',
|
||||
},
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
nullable: false,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'd9sw9l864y0s6b9bc990y7ebi',
|
||||
name: 'action_type',
|
||||
type: {
|
||||
id: 'varchar',
|
||||
name: 'varchar',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
characterMaximumLength: '50',
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'tref4xugo95u21hd02j3s5q67',
|
||||
name: 'diagram_id',
|
||||
type: {
|
||||
id: 'uuid',
|
||||
name: 'uuid',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'v3g2bkhtozhthlnhs3bhu6s4h',
|
||||
name: 'diagram_name',
|
||||
type: {
|
||||
id: 'varchar',
|
||||
name: 'varchar',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
characterMaximumLength: '2500',
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: '79jq9oamvjw7j5i081pplb4ii',
|
||||
name: 'database_type',
|
||||
type: {
|
||||
id: 'varchar',
|
||||
name: 'varchar',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
characterMaximumLength: '50',
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'dd05cmr4ntvwu5aaeir6s4bco',
|
||||
name: 'database_edition',
|
||||
type: {
|
||||
id: 'varchar',
|
||||
name: 'varchar',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
characterMaximumLength: '50',
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: '7589goqn7n2xk3rg5gny817bb',
|
||||
name: 'diagram_json',
|
||||
type: {
|
||||
id: 'json',
|
||||
name: 'json',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'i5zaobzgwbyn8g9xdcskq4qc8',
|
||||
name: 'changed_by_user_id',
|
||||
type: {
|
||||
id: 'uuid',
|
||||
name: 'uuid',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'u9uana25zrknsc5g7t8sinegm',
|
||||
name: 'account_id',
|
||||
type: {
|
||||
id: 'uuid',
|
||||
name: 'uuid',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'jy29dbbfcr4zn5l585y75tob1',
|
||||
name: 'created_at',
|
||||
type: {
|
||||
id: 'timestamp',
|
||||
name: 'timestamp',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'k5jdbgind50qitigvmpuk00n3',
|
||||
name: 'updated_at',
|
||||
type: {
|
||||
id: 'timestamp',
|
||||
name: 'timestamp',
|
||||
},
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
nullable: true,
|
||||
increment: false,
|
||||
isArray: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
id: 'drzp2kp4a74ceiwh0jl8fvuy7',
|
||||
name: 'index_diagrams_history_on_changed_by_user_id',
|
||||
unique: false,
|
||||
fieldIds: ['i5zaobzgwbyn8g9xdcskq4qc8'],
|
||||
createdAt: 1764864006455,
|
||||
type: 'btree',
|
||||
},
|
||||
{
|
||||
id: 'r3gc7sxg5dv8o6ur2z2jen8vf',
|
||||
name: 'index_diagrams_history_on_diagram_id',
|
||||
unique: false,
|
||||
fieldIds: ['tref4xugo95u21hd02j3s5q67'],
|
||||
createdAt: 1764864006455,
|
||||
type: 'btree',
|
||||
},
|
||||
{
|
||||
id: '7v78ps9i99hkwt086wos3dywa',
|
||||
name: 'index_diagrams_history_on_account_id',
|
||||
unique: false,
|
||||
fieldIds: ['u9uana25zrknsc5g7t8sinegm'],
|
||||
createdAt: 1764864006455,
|
||||
type: 'btree',
|
||||
},
|
||||
{
|
||||
id: 'jzv2yaggsmorqfczz6g60s22y',
|
||||
name: 'buckle_diagrams_history_new_pkey',
|
||||
unique: true,
|
||||
fieldIds: ['hugwwfirbk609ejryqqsvw3be'],
|
||||
createdAt: 1764864006455,
|
||||
isPrimaryKey: true,
|
||||
},
|
||||
],
|
||||
color: '#8eb7ff',
|
||||
isView: false,
|
||||
isMaterializedView: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
],
|
||||
relationships: [],
|
||||
dependencies: [],
|
||||
areas: [],
|
||||
customTypes: [],
|
||||
notes: [],
|
||||
};
|
||||
|
||||
const targetDiagram: Diagram = {
|
||||
id: 'mqqwkkod9jb8',
|
||||
name: 'buckle_db',
|
||||
createdAt: new Date('2025-12-04T16:00:06.463Z'),
|
||||
updatedAt: new Date('2025-12-04T16:06:49.070Z'),
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
tables: [
|
||||
{
|
||||
id: 'lexrean4b8pm5lg15ppmkywsv',
|
||||
name: 'buckle_diagrams_history',
|
||||
schema: '',
|
||||
order: 0,
|
||||
fields: [
|
||||
{
|
||||
id: '45z2qw9van4yyowwnbs21m767',
|
||||
name: 'rownum',
|
||||
type: {
|
||||
name: 'int',
|
||||
id: 'int',
|
||||
},
|
||||
nullable: false,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
increment: true,
|
||||
},
|
||||
{
|
||||
id: 'rdef7ta48s1wd1tjjoozaw1lv',
|
||||
name: 'id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
{
|
||||
id: '2mthge4c8s5yt1qe6zp2slsop',
|
||||
name: 'action_type',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
characterMaximumLength: '50',
|
||||
},
|
||||
{
|
||||
id: '8zpgr6s9whcuf43klrbjhaamg',
|
||||
name: 'diagram_id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
{
|
||||
id: '2rxqewro66448l3i7qo79f22d',
|
||||
name: 'diagram_name',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
characterMaximumLength: '2500',
|
||||
},
|
||||
{
|
||||
id: 'khvw72ifx0s1abkj70vn09gsy',
|
||||
name: 'database_type',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
characterMaximumLength: '50',
|
||||
},
|
||||
{
|
||||
id: 'vjle9bee169krs44qhr0xqnz8',
|
||||
name: 'database_edition',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
characterMaximumLength: '50',
|
||||
},
|
||||
{
|
||||
id: 'd6n910e7qnsc32lymsk02die3',
|
||||
name: 'diagram_json',
|
||||
type: {
|
||||
name: 'json',
|
||||
id: 'json',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
{
|
||||
id: 'za4nagw0bx1824awleviede7b',
|
||||
name: 'changed_by_user_id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
{
|
||||
id: 'cspop4m5whpw8ckq51aafe58a',
|
||||
name: 'account_id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
{
|
||||
id: '2z2sixnpkv243xx7nuh2ki0fw',
|
||||
name: 'created_at',
|
||||
type: {
|
||||
name: 'timestamp',
|
||||
id: 'timestamp',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
{
|
||||
id: '5jeve2jfv557ijuvwfv5j1t8m',
|
||||
name: 'updated_at',
|
||||
type: {
|
||||
name: 'timestamp',
|
||||
id: 'timestamp',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864417835,
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
id: '3fsk7qhy0xosg4gr6dcul5t01',
|
||||
name: 'pk_buckle_diagrams_history_id',
|
||||
fieldIds: ['rdef7ta48s1wd1tjjoozaw1lv'],
|
||||
unique: true,
|
||||
isPrimaryKey: true,
|
||||
createdAt: 1764864417836,
|
||||
},
|
||||
{
|
||||
id: 'iujj3p438ueo1vpbv08szxln3',
|
||||
name: 'index_diagrams_history_on_changed_by_user_id',
|
||||
fieldIds: ['za4nagw0bx1824awleviede7b'],
|
||||
unique: false,
|
||||
createdAt: 1764864417836,
|
||||
},
|
||||
{
|
||||
id: 'mq0eh23rt98izx8zhwg5rl0tf',
|
||||
name: 'index_diagrams_history_on_diagram_id',
|
||||
fieldIds: ['8zpgr6s9whcuf43klrbjhaamg'],
|
||||
unique: false,
|
||||
createdAt: 1764864417836,
|
||||
},
|
||||
{
|
||||
id: '57wcn3nl6nxeaq0uuptoolo48',
|
||||
name: 'index_diagrams_history_on_account_id',
|
||||
fieldIds: ['cspop4m5whpw8ckq51aafe58a'],
|
||||
unique: false,
|
||||
createdAt: 1764864417836,
|
||||
},
|
||||
],
|
||||
x: 0,
|
||||
y: 0,
|
||||
color: '#8eb7ff',
|
||||
isView: false,
|
||||
createdAt: 1764864417836,
|
||||
},
|
||||
],
|
||||
relationships: [],
|
||||
dependencies: [],
|
||||
areas: [],
|
||||
customTypes: [],
|
||||
notes: [],
|
||||
};
|
||||
|
||||
const expectedResult: Diagram = {
|
||||
id: 'mqqwkkod9jb8',
|
||||
name: 'buckle_db',
|
||||
createdAt: new Date('2025-12-04T16:00:06.463Z'),
|
||||
updatedAt: new Date('2025-12-04T16:06:49.070Z'),
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
tables: [
|
||||
{
|
||||
id: 'r1rp4f64dtpifw7mub089bxy8',
|
||||
name: 'buckle_diagrams_history',
|
||||
schema: 'public',
|
||||
x: 100,
|
||||
y: 300,
|
||||
fields: [
|
||||
{
|
||||
id: '0t0b4e7irmvw53w4qyz99zqm1',
|
||||
name: 'rownum',
|
||||
type: {
|
||||
name: 'int',
|
||||
id: 'int',
|
||||
},
|
||||
nullable: false,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006454,
|
||||
increment: true,
|
||||
},
|
||||
{
|
||||
id: 'hugwwfirbk609ejryqqsvw3be',
|
||||
name: 'id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'd9sw9l864y0s6b9bc990y7ebi',
|
||||
name: 'action_type',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
characterMaximumLength: '50',
|
||||
},
|
||||
{
|
||||
id: 'tref4xugo95u21hd02j3s5q67',
|
||||
name: 'diagram_id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'v3g2bkhtozhthlnhs3bhu6s4h',
|
||||
name: 'diagram_name',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
characterMaximumLength: '2500',
|
||||
},
|
||||
{
|
||||
id: '79jq9oamvjw7j5i081pplb4ii',
|
||||
name: 'database_type',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
characterMaximumLength: '50',
|
||||
},
|
||||
{
|
||||
id: 'dd05cmr4ntvwu5aaeir6s4bco',
|
||||
name: 'database_edition',
|
||||
type: {
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
characterMaximumLength: '50',
|
||||
},
|
||||
{
|
||||
id: '7589goqn7n2xk3rg5gny817bb',
|
||||
name: 'diagram_json',
|
||||
type: {
|
||||
name: 'json',
|
||||
id: 'json',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'i5zaobzgwbyn8g9xdcskq4qc8',
|
||||
name: 'changed_by_user_id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'u9uana25zrknsc5g7t8sinegm',
|
||||
name: 'account_id',
|
||||
type: {
|
||||
name: 'uuid',
|
||||
id: 'uuid',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'jy29dbbfcr4zn5l585y75tob1',
|
||||
name: 'created_at',
|
||||
type: {
|
||||
name: 'timestamp',
|
||||
id: 'timestamp',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'k5jdbgind50qitigvmpuk00n3',
|
||||
name: 'updated_at',
|
||||
type: {
|
||||
name: 'timestamp',
|
||||
id: 'timestamp',
|
||||
},
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
id: 'jzv2yaggsmorqfczz6g60s22y',
|
||||
name: 'buckle_diagrams_history_new_pkey',
|
||||
fieldIds: ['hugwwfirbk609ejryqqsvw3be'],
|
||||
unique: true,
|
||||
createdAt: 1764864006455,
|
||||
isPrimaryKey: true,
|
||||
},
|
||||
{
|
||||
id: 'drzp2kp4a74ceiwh0jl8fvuy7',
|
||||
name: 'index_diagrams_history_on_changed_by_user_id',
|
||||
fieldIds: ['i5zaobzgwbyn8g9xdcskq4qc8'],
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: 'r3gc7sxg5dv8o6ur2z2jen8vf',
|
||||
name: 'index_diagrams_history_on_diagram_id',
|
||||
fieldIds: ['tref4xugo95u21hd02j3s5q67'],
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
{
|
||||
id: '7v78ps9i99hkwt086wos3dywa',
|
||||
name: 'index_diagrams_history_on_account_id',
|
||||
fieldIds: ['u9uana25zrknsc5g7t8sinegm'],
|
||||
unique: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
],
|
||||
color: '#8eb7ff',
|
||||
isView: false,
|
||||
isMaterializedView: false,
|
||||
createdAt: 1764864006455,
|
||||
},
|
||||
],
|
||||
relationships: [],
|
||||
dependencies: [],
|
||||
areas: [],
|
||||
customTypes: [],
|
||||
notes: [],
|
||||
};
|
||||
|
||||
const result = applyDBMLChanges({
|
||||
sourceDiagram,
|
||||
targetDiagram,
|
||||
});
|
||||
|
||||
// Check that the new field is added correctly
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Apply DBML Changes - relationships', () => {
|
||||
|
||||
@@ -279,19 +279,71 @@ const updateTables = ({
|
||||
return targetField;
|
||||
});
|
||||
|
||||
// Update indexes by matching on name within the table
|
||||
// Update indexes - match by name first, then by semantic structure
|
||||
// Build map of source indexes by name for quick lookup
|
||||
const sourceIndexesByName = new Map<string, DBIndex>();
|
||||
sourceTable.indexes?.forEach((index) => {
|
||||
sourceIndexesByName.set(index.name, index);
|
||||
});
|
||||
|
||||
const updatedIndexes = targetTable.indexes?.map((targetIndex) => {
|
||||
const sourceIndex = sourceIndexesByName.get(targetIndex.name);
|
||||
if (sourceIndex) {
|
||||
// First try to match by name
|
||||
const sourceIndexByName = sourceIndexesByName.get(targetIndex.name);
|
||||
if (sourceIndexByName) {
|
||||
// Names match - preserve source's id, name, and createdAt
|
||||
return {
|
||||
...targetIndex,
|
||||
id: sourceIndex.id,
|
||||
createdAt: sourceIndex.createdAt,
|
||||
id: sourceIndexByName.id,
|
||||
name: sourceIndexByName.name,
|
||||
createdAt: sourceIndexByName.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
// No name match - try semantic match by field IDs, unique, and isPrimaryKey
|
||||
// Translate target field IDs to source field IDs for comparison
|
||||
const targetFieldIdsAsSourceIds = targetIndex.fieldIds.map(
|
||||
(fid) => idMappings.fields[fid] || fid
|
||||
);
|
||||
|
||||
const sourceIndexBySemantic = sourceTable.indexes?.find(
|
||||
(srcIndex) => {
|
||||
// Skip if this source index was already matched by name
|
||||
if (
|
||||
sourceIndexesByName.has(srcIndex.name) &&
|
||||
targetTable.indexes?.some(
|
||||
(ti) => ti.name === srcIndex.name
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare field IDs (order matters for indexes)
|
||||
if (
|
||||
srcIndex.fieldIds.length !==
|
||||
targetFieldIdsAsSourceIds.length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const fieldsMatch = srcIndex.fieldIds.every(
|
||||
(fid, i) => fid === targetFieldIdsAsSourceIds[i]
|
||||
);
|
||||
if (!fieldsMatch) return false;
|
||||
|
||||
// Match unique and isPrimaryKey status
|
||||
return (
|
||||
srcIndex.unique === targetIndex.unique &&
|
||||
!!srcIndex.isPrimaryKey === !!targetIndex.isPrimaryKey
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (sourceIndexBySemantic) {
|
||||
// Semantic match - keep target's id and createdAt, use source's name
|
||||
return {
|
||||
...targetIndex,
|
||||
name: sourceIndexBySemantic.name,
|
||||
id: sourceIndexBySemantic.id,
|
||||
createdAt: sourceIndexBySemantic.createdAt,
|
||||
};
|
||||
}
|
||||
return targetIndex;
|
||||
|
||||
@@ -43,9 +43,6 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
expect(diagram.tables).toBeDefined();
|
||||
diagram.tables?.forEach((table) => {
|
||||
expect(table.schema).toBe('');
|
||||
console.log(
|
||||
`✓ Table "${table.name}" has no schema (MySQL behavior)`
|
||||
);
|
||||
});
|
||||
|
||||
// Check specific tables
|
||||
@@ -129,8 +126,6 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
expect(resultField?.name).toBe(sourceField.name);
|
||||
});
|
||||
});
|
||||
|
||||
console.log('✓ All IDs preserved after DBML round-trip');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -230,9 +225,6 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
// For MySQL, 'public' schema should be stripped
|
||||
mysqlDiagram.tables?.forEach((table) => {
|
||||
expect(table.schema).toBe('');
|
||||
console.log(
|
||||
`✓ MySQL: Table "${table.name}" has no schema (public was stripped)`
|
||||
);
|
||||
});
|
||||
|
||||
// Now test with PostgreSQL - public should also be stripped (it's the default)
|
||||
@@ -242,9 +234,6 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
|
||||
pgDiagram.tables?.forEach((table) => {
|
||||
expect(table.schema).toBe('');
|
||||
console.log(
|
||||
`✓ PostgreSQL: Table "${table.name}" has no schema (public is default)`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -275,7 +264,6 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
|
||||
expect(magicTable?.schema).toBe('fantasy');
|
||||
expect(questTable?.schema).toBe('adventure');
|
||||
console.log('✓ Custom schemas preserved correctly');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -427,8 +415,6 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
expect(currentTable?.id).toBe(original.id);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✓ Data integrity maintained through 3 cycles');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user