apps/nestjs-backend/test/reference.e2e-spec.ts.bak
/* eslint-disable @typescript-eslint/naming-convention */
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import type { IRecord } from '@teable/core';
import {
CellValueType,
DbFieldType,
FieldType,
NumberFormattingType,
Relationship,
} from '@teable/core';
import { PrismaService } from '@teable/db-main-prisma';
import type { Knex } from 'knex';
import { CalculationModule } from '../src/features/calculation/calculation.module';
import type { ITopoItemWithRecords } from '../src/features/calculation/reference.service';
import { ReferenceService } from '../src/features/calculation/reference.service';
import type { IFieldInstance } from '../src/features/field/model/factory';
import { createFieldInstanceByVo } from '../src/features/field/model/factory';
import type { FormulaFieldDto } from '../src/features/field/model/field-dto/formula-field.dto';
import type { LinkFieldDto } from '../src/features/field/model/field-dto/link-field.dto';
import type { NumberFieldDto } from '../src/features/field/model/field-dto/number-field.dto';
import type { SingleLineTextFieldDto } from '../src/features/field/model/field-dto/single-line-text-field.dto';
import { GlobalModule } from '../src/global/global.module';
describe('Reference Service (e2e)', () => {
describe('ReferenceService data retrieval', () => {
let service: ReferenceService;
let prisma: PrismaService;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let initialReferences: {
fromFieldId: string;
toFieldId: string;
}[];
let db: Knex;
const s = JSON.stringify;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [GlobalModule, CalculationModule],
}).compile();
service = module.get<ReferenceService>(ReferenceService);
prisma = module.get<PrismaService>(PrismaService);
db = module.get('CUSTOM_KNEX');
});
afterAll(async () => {
await prisma.$disconnect();
});
async function executeKnex(builder: Knex.SchemaBuilder | Knex.QueryBuilder) {
const sql = builder.toSQL();
if (Array.isArray(sql)) {
for (const item of sql) {
await prisma.$executeRawUnsafe(item.sql, ...item.bindings);
}
} else {
const nativeSql = sql.toNative();
await prisma.$executeRawUnsafe(nativeSql.sql, ...nativeSql.bindings);
}
}
beforeEach(async () => {
// create tables
await executeKnex(
db.schema.createTable('A', (table) => {
table.string('__id').primary();
table.string('fieldA');
table.string('oneToManyB');
})
);
await executeKnex(
db.schema.createTable('B', (table) => {
table.string('__id').primary();
table.string('fieldB');
table.string('manyToOneA');
table.string('__fk_manyToOneA');
table.string('oneToManyC');
})
);
await executeKnex(
db.schema.createTable('C', (table) => {
table.string('__id').primary();
table.string('fieldC');
table.string('manyToOneB');
table.string('__fk_manyToOneB');
})
);
initialReferences = [
{ fromFieldId: 'f1', toFieldId: 'f2' },
{ fromFieldId: 'f2', toFieldId: 'f3' },
{ fromFieldId: 'f2', toFieldId: 'f4' },
{ fromFieldId: 'f3', toFieldId: 'f6' },
{ fromFieldId: 'f5', toFieldId: 'f4' },
{ fromFieldId: 'f7', toFieldId: 'f8' },
];
for (const data of initialReferences) {
await prisma.reference.create({
data,
});
}
});
afterEach(async () => {
// Delete test data
for (const data of initialReferences) {
await prisma.reference.deleteMany({
where: { fromFieldId: data.fromFieldId, AND: { toFieldId: data.toFieldId } },
});
}
// delete data
await executeKnex(db('A').truncate());
await executeKnex(db('B').truncate());
await executeKnex(db('C').truncate());
// delete table
await executeKnex(db.schema.dropTable('A'));
await executeKnex(db.schema.dropTable('B'));
await executeKnex(db.schema.dropTable('C'));
});
it('many to one link relationship order for getAffectedRecords', async () => {
// fill data
await executeKnex(
db('A').insert([
{ __id: 'idA1', fieldA: 'A1', oneToManyB: s(['B1', 'B2']) },
{ __id: 'idA2', fieldA: 'A2', oneToManyB: s(['B3']) },
])
);
await executeKnex(
db('B').insert([
/* eslint-disable prettier/prettier */
{
__id: 'idB1',
fieldB: 'A1',
manyToOneA: 'A1',
__fk_manyToOneA: 'idA1',
oneToManyC: s(['C1', 'C2']),
},
{
__id: 'idB2',
fieldB: 'A1',
manyToOneA: 'A1',
__fk_manyToOneA: 'idA1',
oneToManyC: s(['C3']),
},
{
__id: 'idB3',
fieldB: 'A2',
manyToOneA: 'A2',
__fk_manyToOneA: 'idA2',
oneToManyC: s(['C4']),
},
{ __id: 'idB4', fieldB: null, manyToOneA: null, __fk_manyToOneA: null, oneToManyC: null },
/* eslint-enable prettier/prettier */
])
);
await executeKnex(
db('C').insert([
{ __id: 'idC1', fieldC: 'C1', manyToOneB: 'A1', __fk_manyToOneB: 'idB1' },
{ __id: 'idC2', fieldC: 'C2', manyToOneB: 'A1', __fk_manyToOneB: 'idB1' },
{ __id: 'idC3', fieldC: 'C3', manyToOneB: 'A1', __fk_manyToOneB: 'idB2' },
{ __id: 'idC4', fieldC: 'C4', manyToOneB: 'A2', __fk_manyToOneB: 'idB3' },
])
);
const topoOrder = [
{
dbTableName: 'B',
fieldId: 'manyToOneA',
foreignKeyField: '__fk_manyToOneA',
relationship: Relationship.ManyOne,
linkedTable: 'A',
dependencies: ['fieldA'],
},
{
dbTableName: 'C',
fieldId: 'manyToOneB',
foreignKeyField: '__fk_manyToOneB',
relationship: Relationship.ManyOne,
linkedTable: 'B',
dependencies: ['fieldB'],
},
];
const records = await service['getAffectedRecordItems'](topoOrder, [
{ id: 'idA1', dbTableName: 'A' },
]);
expect(records).toEqual([
{ id: 'idA1', dbTableName: 'A' },
{ id: 'idB1', dbTableName: 'B', fieldId: 'manyToOneA', relationTo: 'idA1' },
{ id: 'idB2', dbTableName: 'B', fieldId: 'manyToOneA', relationTo: 'idA1' },
{ id: 'idC1', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB1' },
{ id: 'idC2', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB1' },
{ id: 'idC3', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB2' },
]);
const recordsWithMultiInput = await service['getAffectedRecordItems'](topoOrder, [
{ id: 'idA1', dbTableName: 'A' },
{ id: 'idA2', dbTableName: 'A' },
]);
expect(recordsWithMultiInput).toEqual([
{ id: 'idA1', dbTableName: 'A' },
{ id: 'idA2', dbTableName: 'A' },
{ id: 'idB1', dbTableName: 'B', relationTo: 'idA1', fieldId: 'manyToOneA' },
{ id: 'idB2', dbTableName: 'B', relationTo: 'idA1', fieldId: 'manyToOneA' },
{ id: 'idB3', dbTableName: 'B', relationTo: 'idA2', fieldId: 'manyToOneA' },
{ id: 'idC1', dbTableName: 'C', relationTo: 'idB1', fieldId: 'manyToOneB' },
{ id: 'idC2', dbTableName: 'C', relationTo: 'idB1', fieldId: 'manyToOneB' },
{ id: 'idC3', dbTableName: 'C', relationTo: 'idB2', fieldId: 'manyToOneB' },
{ id: 'idC4', dbTableName: 'C', relationTo: 'idB3', fieldId: 'manyToOneB' },
]);
});
it('one to many link relationship order for getAffectedRecords', async () => {
await executeKnex(
db('A').insert([{ __id: 'idA1', fieldA: 'A1', oneToManyB: s(['C1, C2', 'C3']) }])
);
await executeKnex(
db('B').insert([
/* eslint-disable prettier/prettier */
{
__id: 'idB1',
fieldB: 'C1, C2',
manyToOneA: 'A1',
__fk_manyToOneA: 'idA1',
oneToManyC: s(['C1', 'C2']),
},
{
__id: 'idB2',
fieldB: 'C3',
manyToOneA: 'A1',
__fk_manyToOneA: 'idA1',
oneToManyC: s(['C3']),
},
/* eslint-enable prettier/prettier */
])
);
await executeKnex(
db('C').insert([
{ __id: 'idC1', fieldC: 'C1', manyToOneB: 'C1, C2', __fk_manyToOneB: 'idB1' },
{ __id: 'idC2', fieldC: 'C2', manyToOneB: 'C1, C2', __fk_manyToOneB: 'idB1' },
{ __id: 'idC3', fieldC: 'C3', manyToOneB: 'C3', __fk_manyToOneB: 'idB2' },
])
);
// topoOrder Graph:
// C.fieldC -> B.oneToManyC -> B.fieldB -> A.oneToManyB
// -> C.manyToOneB
const topoOrder = [
{
dbTableName: 'B',
fieldId: 'oneToManyC',
foreignKeyField: '__fk_manyToOneB',
relationship: Relationship.OneMany,
linkedTable: 'C',
},
{
dbTableName: 'A',
fieldId: 'oneToManyB',
foreignKeyField: '__fk_manyToOneA',
relationship: Relationship.OneMany,
linkedTable: 'B',
},
{
dbTableName: 'C',
fieldId: 'manyToOneB',
foreignKeyField: '__fk_manyToOneB',
relationship: Relationship.ManyOne,
linkedTable: 'B',
},
];
const records = await service['getAffectedRecordItems'](topoOrder, [
{ id: 'idC1', dbTableName: 'C' },
]);
// manyToOneB: ['B1', 'B2']
expect(records).toEqual([
{ id: 'idC1', dbTableName: 'C' },
{ id: 'idB1', dbTableName: 'B', fieldId: 'oneToManyC', selectIn: 'C#__fk_manyToOneB' },
{ id: 'idA1', dbTableName: 'A', fieldId: 'oneToManyB', selectIn: 'B#__fk_manyToOneA' },
{ id: 'idC1', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB1' },
{ id: 'idC2', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB1' },
]);
const extraRecords = await service['getDependentRecordItems'](records);
expect(extraRecords).toEqual([
{ id: 'idB1', dbTableName: 'B', fieldId: 'oneToManyB', relationTo: 'idA1' },
{ id: 'idB2', dbTableName: 'B', fieldId: 'oneToManyB', relationTo: 'idA1' },
{ id: 'idC1', dbTableName: 'C', fieldId: 'oneToManyC', relationTo: 'idB1' },
{ id: 'idC2', dbTableName: 'C', fieldId: 'oneToManyC', relationTo: 'idB1' },
]);
});
it('getDependentNodesCTE should return all dependent nodes', async () => {
const result = await service['getDependentNodesCTE'](['f2']);
const resultData = [...initialReferences];
resultData.pop();
expect(result).toEqual(expect.arrayContaining(resultData));
});
it('should filter full graph by fieldIds', async () => {
/**
* f1 -> f3 -> f4
* f2 -> f3
*/
const graph = [
{
fromFieldId: 'f1',
toFieldId: 'f3',
},
{
fromFieldId: 'f2',
toFieldId: 'f3',
},
{
fromFieldId: 'f3',
toFieldId: 'f4',
},
];
expect(service['filterDirectedGraph'](graph, ['f1'])).toEqual(expect.arrayContaining(graph));
expect(service['filterDirectedGraph'](graph, ['f2'])).toEqual(expect.arrayContaining(graph));
expect(service['filterDirectedGraph'](graph, ['f3'])).toEqual(
expect.arrayContaining([
{
fromFieldId: 'f3',
toFieldId: 'f4',
},
])
);
});
});
describe('ReferenceService calculation', () => {
let service: ReferenceService;
let fieldMap: { [oneToMany: string]: IFieldInstance };
let fieldId2TableId: { [fieldId: string]: string };
let recordMap: { [recordId: string]: IRecord };
let ordersWithRecords: ITopoItemWithRecords[];
let tableId2DbTableName: { [tableId: string]: string };
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [GlobalModule, CalculationModule],
}).compile();
service = module.get<ReferenceService>(ReferenceService);
});
beforeEach(() => {
fieldMap = {
fieldA: createFieldInstanceByVo({
id: 'fieldA',
name: 'fieldA',
type: FieldType.Link,
options: {
relationship: Relationship.OneMany,
foreignTableId: 'foreignTable1',
lookupFieldId: 'lookupField1',
dbForeignKeyName: 'dbForeignKeyName1',
symmetricFieldId: 'symmetricField1',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Json,
isMultipleCellValue: true,
} as LinkFieldDto),
// {
// dbTableName: 'A',
// fieldId: 'oneToManyB',
// foreignKeyField: '__fk_manyToOneA',
// relationship: Relationship.OneMany,
// linkedTable: 'B',
// },
oneToManyB: createFieldInstanceByVo({
id: 'oneToManyB',
name: 'oneToManyB',
type: FieldType.Link,
options: {
relationship: Relationship.OneMany,
foreignTableId: 'B',
lookupFieldId: 'fieldB',
dbForeignKeyName: '__fk_manyToOneA',
symmetricFieldId: 'manyToOneA',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Json,
isMultipleCellValue: true,
} as LinkFieldDto),
// fieldB is a special field depend on oneToManyC, may be convert it to formula field
fieldB: createFieldInstanceByVo({
id: 'fieldB',
name: 'fieldB',
type: FieldType.Formula,
options: {
expression: '{oneToManyC}',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
isMultipleCellValue: true,
isComputed: true,
} as FormulaFieldDto),
manyToOneA: createFieldInstanceByVo({
id: 'manyToOneA',
name: 'manyToOneA',
type: FieldType.Link,
options: {
relationship: Relationship.ManyOne,
foreignTableId: 'A',
lookupFieldId: 'fieldA',
dbForeignKeyName: '__fk_manyToOneA',
symmetricFieldId: 'oneToManyB',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Json,
} as LinkFieldDto),
// {
// dbTableName: 'B',
// fieldId: 'oneToManyC',
// foreignKeyField: '__fk_manyToOneB',
// relationship: Relationship.OneMany,
// linkedTable: 'C',
// },
oneToManyC: createFieldInstanceByVo({
id: 'oneToManyC',
name: 'oneToManyC',
type: FieldType.Link,
options: {
relationship: Relationship.OneMany,
foreignTableId: 'C',
lookupFieldId: 'fieldC',
dbForeignKeyName: '__fk_manyToOneB',
symmetricFieldId: 'manyToOneB',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Json,
isMultipleCellValue: true,
} as LinkFieldDto),
fieldC: createFieldInstanceByVo({
id: 'fieldC',
name: 'fieldC',
type: FieldType.SingleLineText,
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
} as SingleLineTextFieldDto),
// {
// dbTableName: 'C',
// fieldId: 'manyToOneB',
// foreignKeyField: '__fk_manyToOneB',
// relationship: Relationship.ManyOne,
// linkedTable: 'B',
// },
manyToOneB: createFieldInstanceByVo({
id: 'manyToOneB',
name: 'manyToOneB',
type: FieldType.Link,
options: {
relationship: Relationship.ManyOne,
foreignTableId: 'B',
lookupFieldId: 'fieldB',
dbForeignKeyName: '__fk_manyToOneB',
symmetricFieldId: 'oneToManyC',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Json,
} as LinkFieldDto),
};
fieldId2TableId = {
fieldA: 'A',
oneToManyB: 'A',
fieldB: 'B',
manyToOneA: 'B',
oneToManyC: 'B',
fieldC: 'C',
manyToOneB: 'C',
};
tableId2DbTableName = {
A: 'A',
B: 'B',
C: 'C',
};
recordMap = {
// use new value fieldC: 'CX' here
idC1: {
id: 'idC1',
fields: { fieldC: 'CX', manyToOneB: { title: 'C1, C2', id: 'idB1' } },
},
idC2: {
id: 'idC2',
fields: { fieldC: 'C2', manyToOneB: { title: 'C1, C2', id: 'idB1' } },
},
idC3: {
id: 'idC3',
fields: { fieldC: 'C3', manyToOneB: { title: 'C3', id: 'idB2' } },
},
idB1: {
id: 'idB1',
fields: {
fieldB: ['C1', 'C2'],
manyToOneA: { title: 'A1', id: 'idA1' },
oneToManyC: [
{ title: 'C1', id: 'idC1' },
{ title: 'C2', id: 'idC2' },
],
},
},
idB2: {
id: 'idB2',
fields: {
fieldB: ['C3'],
manyToOneA: { title: 'A1', id: 'idA1' },
oneToManyC: [{ title: 'C3', id: 'idC3' }],
},
},
idA1: {
id: 'idA1',
fields: {
fieldA: 'A1',
oneToManyB: [
{ title: 'C1, C2', id: 'idB1' },
{ title: 'C3', id: 'idB2' },
],
},
},
};
// topoOrder Graph:
// C.fieldC -> B.oneToManyC -> B.fieldB -> A.oneToManyB
// -> C.manyToOneB
ordersWithRecords = [
{
id: 'oneToManyC',
dependencies: ['fieldC'],
recordItemMap: [
{
record: recordMap['idB1'],
dependencies: [recordMap['idC1'], recordMap['idC2']],
},
],
},
{
id: 'fieldB',
dependencies: ['oneToManyC'],
recordItemMap: [
{
record: recordMap['idB1'],
},
],
},
{
id: 'oneToManyB',
dependencies: ['fieldB'],
recordItemMap: [
{
record: recordMap['idA1'],
dependencies: [recordMap['idB1'], recordMap['idB2']],
},
],
},
{
id: 'manyToOneB',
dependencies: ['fieldB'],
recordItemMap: [
{
record: recordMap['idC1'],
dependencies: recordMap['idB1'],
},
{
record: recordMap['idC2'],
dependencies: recordMap['idB1'],
},
],
},
];
});
it('should correctly collect changes for Link and Computed fields', () => {
// 2. Act
const changes = service['collectChanges'](ordersWithRecords, fieldMap, fieldId2TableId);
// 3. Assert
// topoOrder Graph:
// C.fieldC -> B.oneToManyC -> B.fieldB -> A.oneToManyB
// -> C.manyToOneB
// change from: idC1.fieldC = 'C1' -> 'CX'
// change affected:
// idB1.oneToManyC = ['C1', 'C2'] -> ['CX', 'C2']
// idB1.fieldB = ['C1', 'C2'] -> ['CX', 'C2']
// idA1.oneToManyB = ['C1, C2', 'C3'] -> ['CX, C2', 'C3']
// idC1.manyToOneB = 'C1, C2' -> 'CX, C2'
// idC2.manyToOneB = 'C1, C2' -> 'CX, C2'
expect(changes).toEqual([
{
tableId: 'B',
recordId: 'idB1',
fieldId: 'oneToManyC',
oldValue: [
{ title: 'C1', id: 'idC1' },
{ title: 'C2', id: 'idC2' },
],
newValue: [
{ title: 'CX', id: 'idC1' },
{ title: 'C2', id: 'idC2' },
],
},
{
tableId: 'B',
recordId: 'idB1',
fieldId: 'fieldB',
oldValue: ['C1', 'C2'],
newValue: ['CX', 'C2'],
},
{
tableId: 'A',
recordId: 'idA1',
fieldId: 'oneToManyB',
oldValue: [
{ title: 'C1, C2', id: 'idB1' },
{ title: 'C3', id: 'idB2' },
],
newValue: [
{ title: 'CX, C2', id: 'idB1' },
{ title: 'C3', id: 'idB2' },
],
},
{
tableId: 'C',
recordId: 'idC1',
fieldId: 'manyToOneB',
oldValue: { title: 'C1, C2', id: 'idB1' },
newValue: { title: 'CX, C2', id: 'idB1' },
},
{
tableId: 'C',
recordId: 'idC2',
fieldId: 'manyToOneB',
oldValue: { title: 'C1, C2', id: 'idB1' },
newValue: { title: 'CX, C2', id: 'idB1' },
},
]);
});
it('should createTopoItemWithRecords from prepared context', () => {
const tableId2DbTableName = {
A: 'A',
B: 'B',
C: 'C',
};
const dbTableName2records = {
A: [recordMap['idA1']],
B: [recordMap['idB1'], recordMap['idB2']],
C: [recordMap['idC1'], recordMap['idC2'], recordMap['idC3']],
};
const affectedRecordItems = [
{ id: 'idB1', dbTableName: 'B', fieldId: 'oneToManyC', selectIn: 'C#__fk_manyToOneB' },
{ id: 'idA1', dbTableName: 'A', fieldId: 'oneToManyB', selectIn: 'B#__fk_manyToOneA' },
{ id: 'idC1', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB1' },
{ id: 'idC2', dbTableName: 'C', fieldId: 'manyToOneB', relationTo: 'idB1' },
];
const dependentRecordItems = [
{ id: 'idB1', dbTableName: 'B', fieldId: 'oneToManyB', relationTo: 'idA1' },
{ id: 'idB2', dbTableName: 'B', fieldId: 'oneToManyB', relationTo: 'idA1' },
{ id: 'idC1', dbTableName: 'C', fieldId: 'oneToManyC', relationTo: 'idB1' },
{ id: 'idC2', dbTableName: 'C', fieldId: 'oneToManyC', relationTo: 'idB1' },
];
// topoOrder Graph:
// C.fieldC -> B.oneToManyC -> B.fieldB -> A.oneToManyB
// -> C.manyToOneB
const topoOrders = [
{
id: 'oneToManyC',
dependencies: ['fieldC'],
},
{
id: 'fieldB',
dependencies: ['oneToManyC'],
},
{
id: 'oneToManyB',
dependencies: ['fieldB'],
},
{
id: 'manyToOneB',
dependencies: ['fieldB'],
},
];
const topoItems = service['createTopoItemWithRecords']({
tableId2DbTableName,
dbTableName2recordMap: dbTableName2records,
affectedRecordItems,
dependentRecordItems,
fieldMap,
fieldId2TableId,
topoOrders,
});
expect(topoItems).toEqual(ordersWithRecords);
});
});
describe('ReferenceService simple formula calculation', () => {
let service: ReferenceService;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [GlobalModule, CalculationModule],
}).compile();
service = module.get<ReferenceService>(ReferenceService);
});
it('should correctly collect changes for Computed fields', () => {
const fieldMap = {
fieldA: createFieldInstanceByVo({
id: 'fieldA',
name: 'fieldA',
type: FieldType.Number,
options: {
formatting: { type: NumberFormattingType.Decimal, precision: 1 },
},
cellValueType: CellValueType.Number,
dbFieldType: DbFieldType.Real,
} as NumberFieldDto),
fieldB: createFieldInstanceByVo({
id: 'fieldB',
name: 'fieldB',
type: FieldType.Formula,
options: {
expression: '{fieldA} & {fieldC}',
},
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
isComputed: true,
} as FormulaFieldDto),
fieldC: createFieldInstanceByVo({
id: 'fieldC',
name: 'fieldC',
type: FieldType.SingleLineText,
cellValueType: CellValueType.String,
dbFieldType: DbFieldType.Text,
} as SingleLineTextFieldDto),
};
const fieldId2TableId = {
fieldA: 'A',
fieldB: 'A',
fieldC: 'A',
};
const recordMap = {
// use new value fieldA: 1 here
idA1: { id: 'idA1', fields: { fieldA: 1, fieldB: null, fieldC: 'X' } },
};
// topoOrder Graph:
// A.fieldA -> A.fieldB
const ordersWithRecords = [
{
id: 'fieldB',
dependencies: ['fieldA', 'fieldC'],
recordItems: [
{
record: recordMap['idA1'],
},
],
},
];
const changes = service['collectChanges'](ordersWithRecords, fieldMap, fieldId2TableId);
expect(changes).toEqual([
{
tableId: 'A',
recordId: 'idA1',
fieldId: 'fieldB',
oldValue: null,
newValue: '1X',
},
]);
});
});
});