test('composite FK column arrays use constraint definition order rather than table column order', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(`
create table parent (
x int,
y int,
primary key (y, x)
);
create table child (
a int,
b int,
constraint child_parent_fk
foreign key (b, a) references parent (y, x)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const tableNames = result.tables
.map((t: { name: string }) => t.name)
.sort();
expect(tableNames).toHaveLength(2);
expect(tableNames).toEqual(['public.child', 'public.parent']);
const childTable = result.tables.find(
(t: { name: string }) => t.name === 'public.child'
);
const constraints = [...(childTable.foreign_key_constraints ?? [])].sort(
(a, b) => a.name.localeCompare(b.name)
);
expect(constraints).toHaveLength(1);
expect(constraints).toEqual([
{
name: 'child_parent_fk',
source_table: 'public.child',
source_columns: ['b', 'a'],
target_table: 'public.parent',
target_columns: ['y', 'x'],
},
]);
});test('independent FKs between the same tables remain separate constraint objects', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(`
create table users (
id int primary key
);
create table orders (
id int primary key,
buyer_id int,
seller_id int,
constraint orders_buyer_fk foreign key (buyer_id) references users (id),
constraint orders_seller_fk foreign key (seller_id) references users (id)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const tableNames = result.tables
.map((t: { name: string }) => t.name)
.sort();
expect(tableNames).toHaveLength(2);
expect(tableNames).toEqual(['public.orders', 'public.users']);
const ordersTable = result.tables.find(
(t: { name: string }) => t.name === 'public.orders'
);
const constraints = [...(ordersTable.foreign_key_constraints ?? [])].sort(
(a, b) => a.name.localeCompare(b.name)
);
expect(constraints).toHaveLength(2);
expect(constraints).toEqual([
{
name: 'orders_buyer_fk',
source_table: 'public.orders',
source_columns: ['buyer_id'],
target_table: 'public.users',
target_columns: ['id'],
},
{
name: 'orders_seller_fk',
source_table: 'public.orders',
source_columns: ['seller_id'],
target_table: 'public.users',
target_columns: ['id'],
},
]);
});test('target table FK relationship keeps the original source table', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(`
create table parent (
y int,
x int,
primary key (y, x)
);
create table child (
b int,
a int,
constraint child_parent_fk
foreign key (b, a) references parent (y, x)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const tableNames = result.tables
.map((t: { name: string }) => t.name)
.sort();
expect(tableNames).toHaveLength(2);
expect(tableNames).toEqual(['public.child', 'public.parent']);
const parentTable = result.tables.find(
(t: { name: string }) => t.name === 'public.parent'
);
const constraints = [...(parentTable.foreign_key_constraints ?? [])].sort(
(a, b) => a.name.localeCompare(b.name)
);
expect(constraints).toHaveLength(1);
expect(constraints).toEqual([
{
name: 'child_parent_fk',
source_table: 'public.child',
source_columns: ['b', 'a'],
target_table: 'public.parent',
target_columns: ['y', 'x'],
},
]);
});test('cross-schema composite FK reports schema-qualified source and target tables', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(`
create schema private;
create table private.parent (
y int,
x int,
primary key (y, x)
);
create table public.child (
b int,
a int,
constraint child_private_parent_fk
foreign key (b, a) references private.parent (y, x)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public', 'private'],
verbose: true,
},
});
const tableNames = result.tables
.map((t: { name: string }) => t.name)
.sort();
expect(tableNames).toHaveLength(2);
expect(tableNames).toEqual(['private.parent', 'public.child']);
const childTable = result.tables.find(
(t: { name: string }) => t.name === 'public.child'
);
const constraints = [...(childTable.foreign_key_constraints ?? [])].sort(
(a, b) => a.name.localeCompare(b.name)
);
expect(constraints).toHaveLength(1);
expect(constraints).toEqual([
{
name: 'child_private_parent_fk',
source_table: 'public.child',
source_columns: ['b', 'a'],
target_table: 'private.parent',
target_columns: ['y', 'x'],
},
]);
});test('verbose list_tables does not fabricate FK constraints for tables without FKs', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(`
create table solo (
id int primary key
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const tableNames = result.tables
.map((t: { name: string }) => t.name)
.sort();
expect(tableNames).toHaveLength(1);
expect(tableNames).toEqual(['public.solo']);
const soloTable = result.tables.find(
(t: { name: string }) => t.name === 'public.solo'
);
expect(soloTable.foreign_key_constraints).toBeUndefined();
});test('composite FK verbose output is stable across repeated list_tables calls', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(`
create table parent (
x int,
y int,
primary key (y, x)
);
create table child (
a int,
b int,
constraint child_parent_fk
foreign key (b, a) references parent (y, x)
);
`);
async function getChildConstraints() {
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const tableNames = result.tables
.map((t: { name: string }) => t.name)
.sort();
expect(tableNames).toHaveLength(2);
expect(tableNames).toEqual(['public.child', 'public.parent']);
const childTable = result.tables.find(
(t: { name: string }) => t.name === 'public.child'
);
return [...(childTable.foreign_key_constraints ?? [])].sort((a, b) =>
a.name.localeCompare(b.name)
);
}
const firstConstraints = await getChildConstraints();
const secondConstraints = await getChildConstraints();
expect(firstConstraints).toHaveLength(1);
expect(firstConstraints).toEqual([
{
name: 'child_parent_fk',
source_table: 'public.child',
source_columns: ['b', 'a'],
target_table: 'public.parent',
target_columns: ['y', 'x'],
},
]);
expect(secondConstraints).toHaveLength(1);
expect(secondConstraints).toEqual(firstConstraints);
});test('list tables reports a self-referential composite foreign key exactly once', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(stripIndent`
create table public.nodes (
tenant_id integer not null,
node_id integer not null,
parent_tenant_id integer,
parent_node_id integer,
primary key (tenant_id, node_id),
constraint nodes_parent_fk
foreign key (parent_tenant_id, parent_node_id)
references public.nodes (tenant_id, node_id)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const actualTables = result.tables
.map((table: any) => ({
name: table.name,
foreign_key_constraints: [...(table.foreign_key_constraints ?? [])].sort(
(a: any, b: any) =>
`${a.name}|${a.source_table}|${a.target_table}`.localeCompare(
`${b.name}|${b.source_table}|${b.target_table}`
)
),
}))
.sort((a: any, b: any) => a.name.localeCompare(b.name));
const expectedTables = [
{
name: 'public.nodes',
foreign_key_constraints: [
{
name: 'nodes_parent_fk',
source_table: 'public.nodes',
source_columns: ['parent_tenant_id', 'parent_node_id'],
target_table: 'public.nodes',
target_columns: ['tenant_id', 'node_id'],
},
],
},
];
expect(actualTables).toHaveLength(expectedTables.length);
actualTables.forEach((table: any, index: number) => {
expect(table.foreign_key_constraints).toHaveLength(
expectedTables[index].foreign_key_constraints.length
);
});
expect(actualTables).toEqual(expectedTables);
});test('list tables includes incoming cross-schema composite foreign keys from unlisted source schemas', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(stripIndent`
create schema private;
create table public.accounts (
tenant_id integer not null,
account_id integer not null,
primary key (tenant_id, account_id)
);
create table private.audit_events (
tenant_id integer not null,
account_id integer not null,
event_id integer not null,
primary key (tenant_id, event_id),
constraint audit_events_account_fk
foreign key (tenant_id, account_id)
references public.accounts (tenant_id, account_id)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const actualTables = result.tables
.map((table: any) => ({
name: table.name,
foreign_key_constraints: [...(table.foreign_key_constraints ?? [])].sort(
(a: any, b: any) =>
`${a.name}|${a.source_table}|${a.target_table}`.localeCompare(
`${b.name}|${b.source_table}|${b.target_table}`
)
),
}))
.sort((a: any, b: any) => a.name.localeCompare(b.name));
const expectedTables = [
{
name: 'public.accounts',
foreign_key_constraints: [
{
name: 'audit_events_account_fk',
source_table: 'private.audit_events',
source_columns: ['tenant_id', 'account_id'],
target_table: 'public.accounts',
target_columns: ['tenant_id', 'account_id'],
},
],
},
];
expect(actualTables).toHaveLength(expectedTables.length);
actualTables.forEach((table: any, index: number) => {
expect(table.foreign_key_constraints).toHaveLength(
expectedTables[index].foreign_key_constraints.length
);
});
expect(actualTables).toEqual(expectedTables);
});test('list tables does not merge incoming composite foreign keys that share a constraint name', async () => {
const { callTool } = await setup();
const org = await createOrganization({
name: 'My Org',
plan: 'free',
allowed_release_channels: ['ga'],
});
const project = await createProject({
name: 'Project 1',
region: 'us-east-1',
organization_id: org.id,
});
project.status = 'ACTIVE_HEALTHY';
await project.db.exec(stripIndent`
create table public.parent_accounts (
tenant_id integer not null,
account_id integer not null,
primary key (tenant_id, account_id)
);
create table public.documents (
document_id integer generated by default as identity primary key,
parent_tenant_id integer not null,
parent_account_id integer not null,
constraint fk_parent_account
foreign key (parent_tenant_id, parent_account_id)
references public.parent_accounts (tenant_id, account_id)
);
create table public.folders (
folder_id integer generated by default as identity primary key,
parent_tenant_id integer not null,
parent_account_id integer not null,
constraint fk_parent_account
foreign key (parent_tenant_id, parent_account_id)
references public.parent_accounts (tenant_id, account_id)
);
`);
const result = await callTool({
name: 'list_tables',
arguments: {
project_id: project.id,
schemas: ['public'],
verbose: true,
},
});
const actualTables = result.tables
.map((table: any) => ({
name: table.name,
foreign_key_constraints: [...(table.foreign_key_constraints ?? [])].sort(
(a: any, b: any) =>
`${a.name}|${a.source_table}|${a.target_table}`.localeCompare(
`${b.name}|${b.source_table}|${b.target_table}`
)
),
}))
.sort((a: any, b: any) => a.name.localeCompare(b.name));
const expectedTables = [
{
name: 'public.documents',
foreign_key_constraints: [
{
name: 'fk_parent_account',
source_table: 'public.documents',
source_columns: ['parent_tenant_id', 'parent_account_id'],
target_table: 'public.parent_accounts',
target_columns: ['tenant_id', 'account_id'],
},
],
},
{
name: 'public.folders',
foreign_key_constraints: [
{
name: 'fk_parent_account',
source_table: 'public.folders',
source_columns: ['parent_tenant_id', 'parent_account_id'],
target_table: 'public.parent_accounts',
target_columns: ['tenant_id', 'account_id'],
},
],
},
{
name: 'public.parent_accounts',
foreign_key_constraints: [
{
name: 'fk_parent_account',
source_table: 'public.documents',
source_columns: ['parent_tenant_id', 'parent_account_id'],
target_table: 'public.parent_accounts',
target_columns: ['tenant_id', 'account_id'],
},
{
name: 'fk_parent_account',
source_table: 'public.folders',
source_columns: ['parent_tenant_id', 'parent_account_id'],
target_table: 'public.parent_accounts',
target_columns: ['tenant_id', 'account_id'],
},
],
},
];
expect(actualTables).toHaveLength(expectedTables.length);
actualTables.forEach((table: any, index: number) => {
expect(table.foreign_key_constraints).toHaveLength(
expectedTables[index].foreign_key_constraints.length
);
});
expect(actualTables).toEqual(expectedTables);
});