mirror of
https://github.com/Kaelio/ktx.git
synced 2026-07-01 08:59:39 +02:00
refactor(workspace): fold internal packages into cli
This commit is contained in:
parent
8c2333cc15
commit
ac3885b652
945 changed files with 517 additions and 2686 deletions
|
|
@ -0,0 +1,428 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { type LiveDatabaseSyncedSchema, planLiveDatabaseStructuralSync } from './structural-sync.js';
|
||||
|
||||
function idFactory(): () => string {
|
||||
let next = 1;
|
||||
return () => `id-${next++}`;
|
||||
}
|
||||
|
||||
describe('planLiveDatabaseStructuralSync', () => {
|
||||
it('plans table and column creates, updates, deletes, and metadata invalidation', () => {
|
||||
const current: LiveDatabaseSyncedSchema = {
|
||||
connectionId: 'conn-1',
|
||||
tables: [
|
||||
{
|
||||
id: 'tbl-orders',
|
||||
name: 'orders',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
enabled: true,
|
||||
descriptions: { ai: 'Old AI order text', db: 'Old DB order text' },
|
||||
columns: [
|
||||
{
|
||||
id: 'col-order-id',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
parentColumnId: null,
|
||||
descriptions: { db: 'Order id' },
|
||||
embedding: [1, 2, 3],
|
||||
sampleValues: null,
|
||||
cardinality: null,
|
||||
},
|
||||
{
|
||||
id: 'col-order-total',
|
||||
name: 'total',
|
||||
type: 'number',
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
parentColumnId: null,
|
||||
descriptions: { ai: 'Old AI total text', db: 'Old total text' },
|
||||
embedding: [4, 5, 6],
|
||||
sampleValues: ['10'],
|
||||
cardinality: 12,
|
||||
},
|
||||
{
|
||||
id: 'col-order-removed',
|
||||
name: 'removed',
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
primaryKey: false,
|
||||
parentColumnId: null,
|
||||
descriptions: {},
|
||||
embedding: null,
|
||||
sampleValues: null,
|
||||
cardinality: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'tbl-removed',
|
||||
name: 'removed_table',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
enabled: true,
|
||||
descriptions: {},
|
||||
columns: [
|
||||
{
|
||||
id: 'col-removed-id',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
parentColumnId: null,
|
||||
descriptions: {},
|
||||
embedding: null,
|
||||
sampleValues: null,
|
||||
cardinality: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
id: 'inferred-total-link',
|
||||
fromTableId: 'tbl-orders',
|
||||
fromColumnId: 'col-order-total',
|
||||
toTableId: 'tbl-orders',
|
||||
toColumnId: 'col-order-id',
|
||||
source: 'inferred',
|
||||
confidence: 0.7,
|
||||
relationshipType: 'MANY_TO_ONE',
|
||||
isPrimaryKeyReference: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const plan = planLiveDatabaseStructuralSync({
|
||||
connectionId: 'conn-1',
|
||||
current,
|
||||
extracted: {
|
||||
connectionId: 'conn-1',
|
||||
tables: [
|
||||
{
|
||||
name: 'orders',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: 'Fresh DB order text',
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
dbComment: 'Order id',
|
||||
},
|
||||
{
|
||||
name: 'total',
|
||||
type: 'string',
|
||||
nullable: false,
|
||||
primaryKey: false,
|
||||
dbComment: 'Fresh total text',
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
type: 'time',
|
||||
nullable: false,
|
||||
primaryKey: false,
|
||||
dbComment: 'Creation timestamp',
|
||||
},
|
||||
],
|
||||
foreignKeys: [],
|
||||
},
|
||||
{
|
||||
name: 'customers',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: 'Customer table',
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
dbComment: null,
|
||||
},
|
||||
],
|
||||
foreignKeys: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
idFactory: idFactory(),
|
||||
});
|
||||
|
||||
expect(plan.stats).toEqual({
|
||||
tablesCreated: 1,
|
||||
tablesDeleted: 1,
|
||||
columnsCreated: 2,
|
||||
columnsDeleted: 2,
|
||||
columnsModified: 1,
|
||||
formalLinksCreated: 0,
|
||||
formalLinksDeleted: 0,
|
||||
});
|
||||
expect(plan.operations.deleteTableIds).toEqual(['tbl-removed']);
|
||||
expect(plan.operations.deleteColumnIds).toEqual(['col-order-removed']);
|
||||
expect(plan.operations.insertTables).toEqual([
|
||||
{
|
||||
id: 'id-2',
|
||||
connectionId: 'conn-1',
|
||||
name: 'customers',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
enabled: true,
|
||||
},
|
||||
]);
|
||||
expect(plan.operations.insertColumns).toEqual([
|
||||
{
|
||||
id: 'id-1',
|
||||
tableId: 'tbl-orders',
|
||||
name: 'created_at',
|
||||
parentColumnId: null,
|
||||
},
|
||||
{
|
||||
id: 'id-3',
|
||||
tableId: 'id-2',
|
||||
name: 'id',
|
||||
parentColumnId: null,
|
||||
},
|
||||
]);
|
||||
expect(plan.operations.touchColumnIds).toEqual(['col-order-total']);
|
||||
expect(plan.operations.invalidateColumnEmbeddingIds).toEqual(['col-order-total']);
|
||||
expect(plan.inferredLinksToValidate).toEqual(['inferred-total-link']);
|
||||
expect(plan.changes).toEqual({
|
||||
newTableIds: ['id-2'],
|
||||
newColumnIds: ['id-1', 'id-3'],
|
||||
tablesWithStructuralChanges: ['tbl-orders', 'id-2'],
|
||||
columnsWithTypeChange: ['col-order-total'],
|
||||
columnsWithDescriptionChange: ['col-order-total'],
|
||||
tablesWithDescriptionChange: ['tbl-orders'],
|
||||
});
|
||||
|
||||
const orders = plan.schema.tables.find((table) => table.name === 'orders');
|
||||
expect(orders?.descriptions).toEqual({ db: 'Fresh DB order text' });
|
||||
expect(orders?.columns.map((column) => column.name)).toEqual(['id', 'total', 'created_at']);
|
||||
expect(orders?.columns.find((column) => column.name === 'total')).toMatchObject({
|
||||
id: 'col-order-total',
|
||||
type: 'string',
|
||||
nullable: false,
|
||||
primaryKey: false,
|
||||
descriptions: { db: 'Fresh total text' },
|
||||
embedding: null,
|
||||
sampleValues: ['10'],
|
||||
cardinality: 12,
|
||||
});
|
||||
});
|
||||
|
||||
it('builds formal links from extracted foreign keys and preserves valid inferred links', () => {
|
||||
const current: LiveDatabaseSyncedSchema = {
|
||||
connectionId: 'conn-1',
|
||||
tables: [
|
||||
{
|
||||
id: 'tbl-orders',
|
||||
name: 'orders',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
enabled: true,
|
||||
descriptions: {},
|
||||
columns: [
|
||||
{
|
||||
id: 'col-orders-id',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
parentColumnId: null,
|
||||
descriptions: {},
|
||||
embedding: null,
|
||||
sampleValues: null,
|
||||
cardinality: null,
|
||||
},
|
||||
{
|
||||
id: 'col-orders-customer',
|
||||
name: 'customer_id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: false,
|
||||
parentColumnId: null,
|
||||
descriptions: {},
|
||||
embedding: null,
|
||||
sampleValues: null,
|
||||
cardinality: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'tbl-customers',
|
||||
name: 'customers',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
enabled: true,
|
||||
descriptions: {},
|
||||
columns: [
|
||||
{
|
||||
id: 'col-customers-id',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
primaryKey: true,
|
||||
parentColumnId: null,
|
||||
descriptions: {},
|
||||
embedding: null,
|
||||
sampleValues: null,
|
||||
cardinality: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
id: 'formal-existing',
|
||||
fromTableId: 'tbl-orders',
|
||||
fromColumnId: 'col-orders-customer',
|
||||
toTableId: 'tbl-customers',
|
||||
toColumnId: 'col-customers-id',
|
||||
source: 'formal',
|
||||
confidence: 1,
|
||||
relationshipType: 'MANY_TO_ONE',
|
||||
isPrimaryKeyReference: true,
|
||||
},
|
||||
{
|
||||
id: 'inferred-existing',
|
||||
fromTableId: 'tbl-orders',
|
||||
fromColumnId: 'col-orders-id',
|
||||
toTableId: 'tbl-customers',
|
||||
toColumnId: 'col-customers-id',
|
||||
source: 'inferred',
|
||||
confidence: 0.6,
|
||||
relationshipType: 'MANY_TO_ONE',
|
||||
isPrimaryKeyReference: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const plan = planLiveDatabaseStructuralSync({
|
||||
connectionId: 'conn-1',
|
||||
current,
|
||||
extracted: {
|
||||
connectionId: 'conn-1',
|
||||
tables: [
|
||||
{
|
||||
name: 'orders',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: null,
|
||||
columns: [
|
||||
{ name: 'id', type: 'number', nullable: false, primaryKey: true, dbComment: null },
|
||||
{ name: 'customer_id', type: 'number', nullable: false, primaryKey: false, dbComment: null },
|
||||
],
|
||||
foreignKeys: [
|
||||
{
|
||||
fromTable: 'orders',
|
||||
fromColumn: 'customer_id',
|
||||
toTable: 'customers',
|
||||
toColumn: 'id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'customers',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: null,
|
||||
columns: [{ name: 'id', type: 'number', nullable: false, primaryKey: true, dbComment: null }],
|
||||
foreignKeys: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
idFactory: idFactory(),
|
||||
});
|
||||
|
||||
expect(plan.stats.formalLinksCreated).toBe(0);
|
||||
expect(plan.stats.formalLinksDeleted).toBe(0);
|
||||
expect(plan.schema.links.map((link) => link.id)).toEqual(['formal-existing', 'inferred-existing']);
|
||||
|
||||
const planAfterForeignKeyRemoval = planLiveDatabaseStructuralSync({
|
||||
connectionId: 'conn-1',
|
||||
current,
|
||||
extracted: {
|
||||
connectionId: 'conn-1',
|
||||
tables: [
|
||||
{
|
||||
name: 'orders',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: null,
|
||||
columns: [
|
||||
{ name: 'id', type: 'number', nullable: false, primaryKey: true, dbComment: null },
|
||||
{ name: 'customer_id', type: 'number', nullable: false, primaryKey: false, dbComment: null },
|
||||
],
|
||||
foreignKeys: [],
|
||||
},
|
||||
{
|
||||
name: 'customers',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: null,
|
||||
columns: [{ name: 'id', type: 'number', nullable: false, primaryKey: true, dbComment: null }],
|
||||
foreignKeys: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
idFactory: idFactory(),
|
||||
});
|
||||
|
||||
expect(planAfterForeignKeyRemoval.stats.formalLinksDeleted).toBe(1);
|
||||
expect(planAfterForeignKeyRemoval.schema.links.map((link) => link.id)).toEqual(['inferred-existing']);
|
||||
|
||||
const planAfterForeignKeyCreation = planLiveDatabaseStructuralSync({
|
||||
connectionId: 'conn-1',
|
||||
current: { ...current, links: [current.links[1]] },
|
||||
extracted: {
|
||||
connectionId: 'conn-1',
|
||||
tables: [
|
||||
{
|
||||
name: 'orders',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: null,
|
||||
columns: [
|
||||
{ name: 'id', type: 'number', nullable: false, primaryKey: true, dbComment: null },
|
||||
{ name: 'customer_id', type: 'number', nullable: false, primaryKey: false, dbComment: null },
|
||||
],
|
||||
foreignKeys: [
|
||||
{
|
||||
fromTable: 'orders',
|
||||
fromColumn: 'customer_id',
|
||||
toTable: 'customers',
|
||||
toColumn: 'id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'customers',
|
||||
catalog: null,
|
||||
db: 'public',
|
||||
dbComment: null,
|
||||
columns: [{ name: 'id', type: 'number', nullable: false, primaryKey: true, dbComment: null }],
|
||||
foreignKeys: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
idFactory: idFactory(),
|
||||
});
|
||||
|
||||
expect(planAfterForeignKeyCreation.stats.formalLinksCreated).toBe(1);
|
||||
expect(planAfterForeignKeyCreation.schema.links[0]).toMatchObject({
|
||||
id: 'id-1',
|
||||
fromTableId: 'tbl-orders',
|
||||
fromColumnId: 'col-orders-customer',
|
||||
toTableId: 'tbl-customers',
|
||||
toColumnId: 'col-customers-id',
|
||||
source: 'formal',
|
||||
confidence: 1,
|
||||
relationshipType: 'MANY_TO_ONE',
|
||||
isPrimaryKeyReference: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue