Workspace migration v2 testing (#13136)
# Introduction Introduced `EachTesting` pattern for the builder unit tests. As always any suggestions are more than welcomed ! Still need to: - [x] implem basic tests for field - [x] create `get-flat-index-field-metadata.mock.ts` - [x] Implement basic tests for index and index-fields - [ ] Implem standard edges cases tests TDD style ## Misc - was https://github.com/twentyhq/twenty/pull/13132 closed due to mess to rebase on main
This commit is contained in:
@ -0,0 +1,54 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
|
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||||
|
|
||||||
|
type FlatFieldMetadataOverrides<
|
||||||
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
|
> = Required<
|
||||||
|
Pick<FlatFieldMetadata<T>, 'uniqueIdentifier' | 'objectMetadataId'>
|
||||||
|
> &
|
||||||
|
Partial<FlatFieldMetadata<T>>;
|
||||||
|
|
||||||
|
export const getFlatFieldMetadataMock = <
|
||||||
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
|
>(
|
||||||
|
overrides: FlatFieldMetadataOverrides<T>,
|
||||||
|
): FlatFieldMetadata<T> => {
|
||||||
|
const createdAt = faker.date.anytime();
|
||||||
|
|
||||||
|
return {
|
||||||
|
createdAt,
|
||||||
|
description: 'default flat field metadata description',
|
||||||
|
icon: 'icon',
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
isActive: true,
|
||||||
|
isCustom: true,
|
||||||
|
name: 'flatFieldMetadataName',
|
||||||
|
label: 'flat field metadata label',
|
||||||
|
isNullable: true,
|
||||||
|
isUnique: false,
|
||||||
|
relationTargetFieldMetadataId: undefined,
|
||||||
|
relationTargetObjectMetadataId: undefined,
|
||||||
|
type: FieldMetadataType.TEXT as T,
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
isSystem: false,
|
||||||
|
standardId: undefined,
|
||||||
|
standardOverrides: undefined,
|
||||||
|
updatedAt: createdAt,
|
||||||
|
workspaceId: faker.string.uuid(),
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStandardFlatFieldMetadataMock = (
|
||||||
|
overrides: Omit<FlatFieldMetadataOverrides, 'isCustom' | 'isSystem'>,
|
||||||
|
) => {
|
||||||
|
return getFlatFieldMetadataMock({
|
||||||
|
standardId: faker.string.uuid(),
|
||||||
|
standardOverrides: {},
|
||||||
|
isCustom: false,
|
||||||
|
isSystem: true,
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
import { FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
||||||
|
|
||||||
|
type FlatIndexFieldMetadataOverrides = Required<
|
||||||
|
Pick<
|
||||||
|
FlatIndexFieldMetadata,
|
||||||
|
'fieldMetadataId' | 'indexMetadataId' | 'uniqueIdentifier'
|
||||||
|
>
|
||||||
|
> &
|
||||||
|
Partial<FlatIndexFieldMetadata>;
|
||||||
|
export const getFlatIndexFieldMetadataMock = (
|
||||||
|
overrides: FlatIndexFieldMetadataOverrides,
|
||||||
|
): FlatIndexFieldMetadata => {
|
||||||
|
const createdAt = faker.date.anytime();
|
||||||
|
|
||||||
|
return {
|
||||||
|
createdAt,
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
order: faker.number.int(),
|
||||||
|
updatedAt: createdAt,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||||
|
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
||||||
|
|
||||||
|
type FlatIndexMetadataOverrides = Required<
|
||||||
|
Pick<FlatIndexMetadata, 'uniqueIdentifier' | 'objectMetadataId'>
|
||||||
|
> &
|
||||||
|
Partial<FlatIndexMetadata>;
|
||||||
|
export const getFlatIndexMetadataMock = (
|
||||||
|
overrides: FlatIndexMetadataOverrides,
|
||||||
|
): FlatIndexMetadata => {
|
||||||
|
const createdAt = faker.date.anytime();
|
||||||
|
|
||||||
|
return {
|
||||||
|
flatIndexFieldMetadatas: [],
|
||||||
|
createdAt,
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
indexType: IndexType.BTREE,
|
||||||
|
indexWhereClause: undefined,
|
||||||
|
isCustom: false,
|
||||||
|
isUnique: false,
|
||||||
|
name: 'defaultFlatIndexMetadataName',
|
||||||
|
updatedAt: createdAt,
|
||||||
|
workspaceId: faker.string.uuid(),
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStandardFlatIndexMetadataMock = (
|
||||||
|
overrides: Omit<FlatIndexMetadataOverrides, 'isCustom'>,
|
||||||
|
) => {
|
||||||
|
return getFlatIndexMetadataMock({
|
||||||
|
isCustom: false,
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
||||||
|
|
||||||
|
type FlatObjectMetadataOverrides = Required<
|
||||||
|
Pick<FlatObjectMetadata, 'uniqueIdentifier'>
|
||||||
|
> &
|
||||||
|
Partial<FlatObjectMetadata>;
|
||||||
|
export const getFlatObjectMetadataMock = (
|
||||||
|
overrides: FlatObjectMetadataOverrides,
|
||||||
|
): FlatObjectMetadata => {
|
||||||
|
const createdAt = faker.date.anytime();
|
||||||
|
|
||||||
|
return {
|
||||||
|
flatFieldMetadatas: [],
|
||||||
|
flatIndexMetadatas: [],
|
||||||
|
createdAt,
|
||||||
|
dataSourceId: faker.string.uuid(),
|
||||||
|
description: 'default flat object metadata description',
|
||||||
|
duplicateCriteria: [],
|
||||||
|
icon: 'icon',
|
||||||
|
id: faker.string.uuid(),
|
||||||
|
imageIdentifierFieldMetadataId: faker.string.uuid(),
|
||||||
|
isActive: true,
|
||||||
|
isAuditLogged: true,
|
||||||
|
isCustom: true,
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
isRemote: false,
|
||||||
|
isSearchable: true,
|
||||||
|
isSystem: false,
|
||||||
|
labelIdentifierFieldMetadataId: faker.string.uuid(),
|
||||||
|
labelPlural: 'default flat object metadata label plural',
|
||||||
|
labelSingular: 'default flat object metadata label singular',
|
||||||
|
namePlural: 'defaultflatObjectMetadataNamePlural',
|
||||||
|
nameSingular: 'defaultflatObjectMetadataNameSingular',
|
||||||
|
shortcut: 'shortcut',
|
||||||
|
standardId: undefined,
|
||||||
|
standardOverrides: undefined,
|
||||||
|
targetTableName: '',
|
||||||
|
updatedAt: createdAt,
|
||||||
|
workspaceId: faker.string.uuid(),
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStandardFlatObjectMetadataMock = (
|
||||||
|
overrides: Omit<FlatObjectMetadataOverrides, 'isCustom' | 'isSystem'>,
|
||||||
|
) => {
|
||||||
|
return getFlatObjectMetadataMock({
|
||||||
|
standardId: faker.string.uuid(),
|
||||||
|
standardOverrides: {},
|
||||||
|
isCustom: false,
|
||||||
|
isSystem: true,
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { Relation } from 'typeorm';
|
||||||
|
|
||||||
|
export type ExtractRecordTypeOrmRelationProperties<T, TRelationTargets> =
|
||||||
|
NonNullable<
|
||||||
|
{
|
||||||
|
[P in keyof T]: T[P] extends Relation<
|
||||||
|
TRelationTargets | TRelationTargets[]
|
||||||
|
>
|
||||||
|
? P
|
||||||
|
: never;
|
||||||
|
}[keyof T]
|
||||||
|
>;
|
||||||
@ -1,7 +1,18 @@
|
|||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
export type FlatFieldMetadata = Partial<
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
Omit<FieldMetadataEntity, 'object' | 'indexFieldMetadatas'>
|
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||||
> & {
|
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||||
uniqueIdentifier: string;
|
|
||||||
};
|
type FieldMetadataEntityRelationProperties =
|
||||||
|
ExtractRecordTypeOrmRelationProperties<
|
||||||
|
FieldMetadataEntity,
|
||||||
|
MetadataEntitiesRelationTarget
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type FlatFieldMetadata<T extends FieldMetadataType = FieldMetadataType> =
|
||||||
|
Partial<
|
||||||
|
Omit<FieldMetadataEntity<T>, FieldMetadataEntityRelationProperties>
|
||||||
|
> & {
|
||||||
|
uniqueIdentifier: string;
|
||||||
|
};
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||||
|
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||||
|
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||||
|
|
||||||
const indexFieldMetadataEntityRelationProperties = [
|
type IndexFieldMetadataEntityRelationProperties =
|
||||||
'indexMetadata',
|
ExtractRecordTypeOrmRelationProperties<
|
||||||
'fieldMetadata',
|
IndexFieldMetadataEntity,
|
||||||
] as const satisfies (keyof IndexFieldMetadataEntity)[];
|
MetadataEntitiesRelationTarget
|
||||||
|
>;
|
||||||
type IndexFieldMetadataRelationProperties =
|
|
||||||
(typeof indexFieldMetadataEntityRelationProperties)[number];
|
|
||||||
|
|
||||||
export type FlatIndexFieldMetadata = Partial<
|
export type FlatIndexFieldMetadata = Partial<
|
||||||
Omit<IndexFieldMetadataEntity, IndexFieldMetadataRelationProperties>
|
Omit<IndexFieldMetadataEntity, IndexFieldMetadataEntityRelationProperties>
|
||||||
> & {
|
> & {
|
||||||
uniqueIdentifier: string;
|
uniqueIdentifier: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||||
|
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||||
import { FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
import { FlatIndexFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-field-metadata';
|
||||||
|
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||||
|
|
||||||
|
type IndexMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
|
||||||
|
IndexMetadataEntity,
|
||||||
|
MetadataEntitiesRelationTarget
|
||||||
|
>;
|
||||||
|
|
||||||
export type FlatIndexMetadata = Partial<
|
export type FlatIndexMetadata = Partial<
|
||||||
Omit<IndexMetadataEntity, 'indexFieldMetadatas' | 'objectMetadata'> // Might have an issue as ObjectMetadataId != uniqueIdentifier
|
Omit<IndexMetadataEntity, IndexMetadataRelationProperties>
|
||||||
> & {
|
> & {
|
||||||
flatIndexFieldMetadatas: FlatIndexFieldMetadata[];
|
flatIndexFieldMetadatas: FlatIndexFieldMetadata[];
|
||||||
uniqueIdentifier: string;
|
uniqueIdentifier: string;
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
|
import { ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||||
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
||||||
|
import { MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||||
|
|
||||||
|
type ObjectMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
|
||||||
|
ObjectMetadataEntity,
|
||||||
|
MetadataEntitiesRelationTarget
|
||||||
|
>;
|
||||||
|
|
||||||
export type FlatObjectMetadata = Partial<
|
export type FlatObjectMetadata = Partial<
|
||||||
Omit<ObjectMetadataEntity, 'fields' | 'indexMetadatas'>
|
Omit<ObjectMetadataEntity, ObjectMetadataRelationProperties>
|
||||||
> & {
|
> & {
|
||||||
uniqueIdentifier: string;
|
uniqueIdentifier: string;
|
||||||
flatIndexMetadatas: FlatIndexMetadata[];
|
flatIndexMetadatas: FlatIndexMetadata[];
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||||
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
|
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||||
|
|
||||||
|
export type MetadataEntitiesRelationTarget =
|
||||||
|
| ObjectMetadataEntity
|
||||||
|
| FieldMetadataEntity
|
||||||
|
| IndexFieldMetadataEntity
|
||||||
|
| FieldPermissionEntity;
|
||||||
@ -2,6 +2,7 @@ import diff from 'microdiff';
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
||||||
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
import { FromTo } from 'src/engine/workspace-manager/workspace-migration-v2/types/from-to.type';
|
||||||
import { UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
import { UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||||
@ -18,6 +19,11 @@ const flatFieldMetadataPropertiesToCompare = [
|
|||||||
'name',
|
'name',
|
||||||
'options',
|
'options',
|
||||||
'standardOverrides',
|
'standardOverrides',
|
||||||
|
'settings',
|
||||||
|
// To reactivate once we authorize relation edition, see https://github.com/twentyhq/twenty/commit/39f6f3c4bb101272a9014e142a842d0801a3c33b
|
||||||
|
// 'relationTargetFieldMetadataId',
|
||||||
|
// 'relationTargetObjectMetadataId',
|
||||||
|
///
|
||||||
] as const satisfies (keyof FlatFieldMetadata)[];
|
] as const satisfies (keyof FlatFieldMetadata)[];
|
||||||
|
|
||||||
export type FlatFieldMetadataPropertiesToCompare =
|
export type FlatFieldMetadataPropertiesToCompare =
|
||||||
@ -26,6 +32,7 @@ export type FlatFieldMetadataPropertiesToCompare =
|
|||||||
const fieldMetadataPropertiesToStringify = [
|
const fieldMetadataPropertiesToStringify = [
|
||||||
'defaultValue',
|
'defaultValue',
|
||||||
'standardOverrides',
|
'standardOverrides',
|
||||||
|
'settings',
|
||||||
] as const satisfies FlatFieldMetadataPropertiesToCompare[];
|
] as const satisfies FlatFieldMetadataPropertiesToCompare[];
|
||||||
|
|
||||||
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
|
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
|
||||||
@ -65,6 +72,15 @@ export const compareTwoFlatFieldMetadata = ({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove below assertion when we authorize relation edition, see https://github.com/twentyhq/twenty/commit/39f6f3c4bb101272a9014e142a842d0801a3c33b
|
||||||
|
if (
|
||||||
|
isDefined(fieldMetadata.type) &&
|
||||||
|
isRelationFieldMetadataType(fieldMetadata.type) &&
|
||||||
|
!['label', 'description', 'isActive'].includes(property)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
propertiesToStringify: fieldMetadataPropertiesToStringify,
|
propertiesToStringify: fieldMetadataPropertiesToStringify,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,284 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
|
|
||||||
|
import { getFlatFieldMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-field-metadata.mock';
|
||||||
|
import { getFlatObjectMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-object-metadata.mock';
|
||||||
|
import { WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||||
|
|
||||||
|
const basicObjectMetadataId = faker.string.uuid();
|
||||||
|
const basicFlatFieldMetadatas = Array.from({ length: 5 }, (_value, index) =>
|
||||||
|
getFlatFieldMetadataMock({
|
||||||
|
objectMetadataId: basicObjectMetadataId,
|
||||||
|
uniqueIdentifier: `field_${index}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO prastoin test defaultValue and settings updates
|
||||||
|
// TODO prastoin test standard abstraction in TDD style
|
||||||
|
|
||||||
|
const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||||
|
{
|
||||||
|
title: 'It should build an create_field action for a RELATION field',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
|
||||||
|
const createdFlatRelationFieldMetadata = getFlatFieldMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||||
|
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatFieldMetadatas: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatFieldMetadatas: [createdFlatRelationFieldMetadata],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
createField: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should build an update_field action for a RELATION field',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
|
||||||
|
const updatedFieldMetadata = getFlatFieldMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||||
|
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatFieldMetadatas: [
|
||||||
|
...basicFlatFieldMetadatas,
|
||||||
|
updatedFieldMetadata,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatFieldMetadatas: [
|
||||||
|
...basicFlatFieldMetadatas,
|
||||||
|
{
|
||||||
|
...updatedFieldMetadata,
|
||||||
|
isActive: false,
|
||||||
|
description: 'new description',
|
||||||
|
label: 'new label',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
updateField: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
'It should NOT build an update_field action for a field RELATION uncovered fields mutation',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
const updatedFieldMetadata =
|
||||||
|
getFlatFieldMetadataMock<FieldMetadataType.RELATION>({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
settings: {
|
||||||
|
relationType: RelationType.MANY_TO_ONE,
|
||||||
|
isForeignKey: true,
|
||||||
|
joinColumnName: 'column-name',
|
||||||
|
onDelete: undefined,
|
||||||
|
},
|
||||||
|
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||||
|
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatFieldMetadatas: [updatedFieldMetadata],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatFieldMetadatas: [
|
||||||
|
{
|
||||||
|
...updatedFieldMetadata,
|
||||||
|
settings: {
|
||||||
|
relationType: RelationType.ONE_TO_MANY,
|
||||||
|
isForeignKey: false,
|
||||||
|
joinColumnName: 'new-column-name',
|
||||||
|
onDelete: undefined,
|
||||||
|
},
|
||||||
|
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||||
|
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||||
|
{
|
||||||
|
title: 'It should build an create_field action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
|
||||||
|
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatFieldMetadatas: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatFieldMetadatas: [flatFieldMetadata],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
createField: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should build an update_field action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
|
||||||
|
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatFieldMetadatas: [
|
||||||
|
...basicFlatFieldMetadatas,
|
||||||
|
{
|
||||||
|
...flatFieldMetadata,
|
||||||
|
description: 'new description',
|
||||||
|
name: 'new name',
|
||||||
|
isActive: false,
|
||||||
|
icon: 'new icon',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
updateField: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should build a delete_field action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
|
||||||
|
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatFieldMetadatas: basicFlatFieldMetadatas,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
deleteField: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||||
|
[
|
||||||
|
...relationTestCases,
|
||||||
|
...basicCrudTestCases,
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
'It should not infer any actions as from and to fields are identical',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const from = [
|
||||||
|
getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
flatFieldMetadatas: [flatFieldMetadata],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
from,
|
||||||
|
to: from,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
import { getFlatIndexMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-index-metadata.mock';
|
||||||
|
import { getFlatObjectMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-object-metadata.mock';
|
||||||
|
import { WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||||
|
|
||||||
|
// Should test more things such as flatFieldIndex diffing
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
|
||||||
|
export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: 'It should build an create_index action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatIndexMetadatas: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatIndexMetadatas: [flatIndexMetadata],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
createIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
'It should build an delete_index and a create_index action ( the way we handle update )',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatIndexMetadatas: [flatIndexMetadata],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatIndexMetadatas: [
|
||||||
|
{
|
||||||
|
...flatIndexMetadata,
|
||||||
|
name: 'new index name',
|
||||||
|
isUnique: false,
|
||||||
|
indexWhereClause: 'new index where clause',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
createIndex: 1,
|
||||||
|
deleteIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should build a delete_index action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatIndexMetadatas: [flatIndexMetadata],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
flatIndexMetadatas: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
deleteIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
'It should not infer any actions as from and to indexes are identical',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||||
|
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||||
|
objectMetadataId,
|
||||||
|
});
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
flatIndexMetadatas: [flatIndexMetadata],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [flatObjectMetadata],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
import { getFlatFieldMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-field-metadata.mock';
|
||||||
|
import { getFlatObjectMetadataMock } from 'src/engine/workspace-manager/workspace-migration-v2/__tests__/get-flat-object-metadata.mock';
|
||||||
|
import { WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||||
|
|
||||||
|
export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
'It should build an update_object action with all object updated fields',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'pomme',
|
||||||
|
nameSingular: 'toto',
|
||||||
|
namePlural: 'totos',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
...flatObjectMetadata,
|
||||||
|
nameSingular: 'prastouin',
|
||||||
|
namePlural: 'prastoins',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
updateObject: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should build a create_object action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'pomme',
|
||||||
|
nameSingular: 'toto',
|
||||||
|
namePlural: 'totos',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [],
|
||||||
|
to: [flatObjectMetadata],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
createObject: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
'It should build a create_object and create_field actions for each of this fieldMetadata',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const objectMetadataId = faker.string.uuid();
|
||||||
|
const flatFieldMetadatas = Array.from(
|
||||||
|
{ length: 5 },
|
||||||
|
(_value, index) =>
|
||||||
|
getFlatFieldMetadataMock({
|
||||||
|
objectMetadataId,
|
||||||
|
uniqueIdentifier: `field_${index}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'pomme',
|
||||||
|
nameSingular: 'toto',
|
||||||
|
namePlural: 'totos',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
id: objectMetadataId,
|
||||||
|
flatFieldMetadatas,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [],
|
||||||
|
to: [flatObjectMetadata],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
createObject: 1,
|
||||||
|
createField: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should build a delete_object action',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||||
|
uniqueIdentifier: 'pomme',
|
||||||
|
nameSingular: 'toto',
|
||||||
|
namePlural: 'totos',
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: [flatObjectMetadata],
|
||||||
|
to: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
expectedActionsTypeCounter: {
|
||||||
|
deleteObject: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'It should not infer any actions as from and to are identical',
|
||||||
|
context: {
|
||||||
|
input: () => {
|
||||||
|
const from = [
|
||||||
|
getFlatObjectMetadataMock({ uniqueIdentifier: 'pomme' }),
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
from,
|
||||||
|
to: from,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { EachTestingContext } from 'twenty-shared/testing';
|
||||||
|
|
||||||
|
import { WorkspaceMigrationActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||||
|
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||||
|
|
||||||
|
type WorkspaceBuilderArgs = Parameters<
|
||||||
|
typeof WorkspaceMigrationBuilderV2Service.prototype.build
|
||||||
|
>[0];
|
||||||
|
|
||||||
|
type ConvertActionTypeToCamelCase<T extends string> =
|
||||||
|
T extends `${infer Before}_${infer After}`
|
||||||
|
? `${Before}${Capitalize<After>}`
|
||||||
|
: T;
|
||||||
|
|
||||||
|
export type CamelCasedWorkspaceMigrationActionsType =
|
||||||
|
ConvertActionTypeToCamelCase<WorkspaceMigrationActionTypeV2>;
|
||||||
|
|
||||||
|
export type ExpectedActionCounters = Partial<
|
||||||
|
Record<CamelCasedWorkspaceMigrationActionsType, number>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type WorkspaceMigrationBuilderTestCase = EachTestingContext<{
|
||||||
|
input: WorkspaceBuilderArgs | (() => WorkspaceBuilderArgs);
|
||||||
|
expectedActionsTypeCounter?: ExpectedActionCounters;
|
||||||
|
}>;
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||||
|
import { eachTestingContextFilter } from 'twenty-shared/testing';
|
||||||
|
import { capitalize } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import { WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case';
|
||||||
|
import { WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case';
|
||||||
|
import { WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case';
|
||||||
|
import {
|
||||||
|
CamelCasedWorkspaceMigrationActionsType,
|
||||||
|
ExpectedActionCounters,
|
||||||
|
WorkspaceMigrationBuilderTestCase,
|
||||||
|
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||||
|
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||||
|
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||||
|
|
||||||
|
const allWorkspaceBuilderTestCases: {
|
||||||
|
label: string;
|
||||||
|
testCases: WorkspaceMigrationBuilderTestCase[];
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
label: 'object',
|
||||||
|
testCases: WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'field',
|
||||||
|
testCases: WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'index',
|
||||||
|
testCases: WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const expectedActionsTypeCounterChecker = ({
|
||||||
|
expectedActionsTypeCounter,
|
||||||
|
workspaceMigration,
|
||||||
|
}: {
|
||||||
|
workspaceMigration: WorkspaceMigrationV2;
|
||||||
|
expectedActionsTypeCounter?: ExpectedActionCounters;
|
||||||
|
}) => {
|
||||||
|
const initialAcc: ExpectedActionCounters = {
|
||||||
|
createField: 0,
|
||||||
|
createIndex: 0,
|
||||||
|
createObject: 0,
|
||||||
|
deleteField: 0,
|
||||||
|
deleteIndex: 0,
|
||||||
|
deleteObject: 0,
|
||||||
|
updateField: 0,
|
||||||
|
updateObject: 0,
|
||||||
|
};
|
||||||
|
const actualActionsTypeCounter = workspaceMigration.actions.reduce(
|
||||||
|
(acc, action) => {
|
||||||
|
const { type } = action;
|
||||||
|
const [operation, target] = type.split('_');
|
||||||
|
const formattedActionKey =
|
||||||
|
`${operation}${capitalize(target)}` as CamelCasedWorkspaceMigrationActionsType;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[formattedActionKey]: (acc[formattedActionKey] ?? 0) + 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
initialAcc,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(actualActionsTypeCounter).toEqual({
|
||||||
|
...initialAcc,
|
||||||
|
...expectedActionsTypeCounter,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe.each(allWorkspaceBuilderTestCases)(
|
||||||
|
'Workspace migration builder $label actions test suite',
|
||||||
|
({ testCases }) => {
|
||||||
|
let service: WorkspaceMigrationBuilderV2Service;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new WorkspaceMigrationBuilderV2Service();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(eachTestingContextFilter(testCases))(
|
||||||
|
'$title',
|
||||||
|
({ context: { input, expectedActionsTypeCounter } }) => {
|
||||||
|
const { from, to } = typeof input === 'function' ? input() : input;
|
||||||
|
const workspaceMigration = service.build({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
});
|
||||||
|
|
||||||
|
expectedActionsTypeCounterChecker({
|
||||||
|
expectedActionsTypeCounter,
|
||||||
|
workspaceMigration,
|
||||||
|
});
|
||||||
|
const { actions } = workspaceMigration;
|
||||||
|
|
||||||
|
expect(actions).toMatchSnapshot(
|
||||||
|
actions.map(extractRecordIdsAndDatesAsExpectAny),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -1,605 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
|
|
||||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
|
||||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
|
||||||
|
|
||||||
describe('WorkspaceMigrationBuilderV2Service', () => {
|
|
||||||
let service: WorkspaceMigrationBuilderV2Service;
|
|
||||||
|
|
||||||
const baseObject: FlatObjectMetadata = {
|
|
||||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614175000',
|
|
||||||
nameSingular: 'Contact',
|
|
||||||
namePlural: 'Contacts',
|
|
||||||
labelSingular: 'Contact',
|
|
||||||
flatIndexMetadatas: [],
|
|
||||||
labelPlural: 'Contacts',
|
|
||||||
description: 'A contact',
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
{
|
|
||||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174000',
|
|
||||||
name: 'firstName',
|
|
||||||
label: 'First Name',
|
|
||||||
type: FieldMetadataType.FULL_NAME,
|
|
||||||
defaultValue: '',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new WorkspaceMigrationBuilderV2Service();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return a migration when nameSingular changes', () => {
|
|
||||||
const from: FlatObjectMetadata = baseObject;
|
|
||||||
const to: FlatObjectMetadata = {
|
|
||||||
...from,
|
|
||||||
nameSingular: 'Person',
|
|
||||||
};
|
|
||||||
const result = service.build({ from: [from], to: [to] });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A contact",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "First Name",
|
|
||||||
"name": "firstName",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Contacts",
|
|
||||||
"labelSingular": "Contact",
|
|
||||||
"namePlural": "Contacts",
|
|
||||||
"nameSingular": "Person",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
|
||||||
},
|
|
||||||
"type": "update_object",
|
|
||||||
"updates": [
|
|
||||||
{
|
|
||||||
"from": "Contact",
|
|
||||||
"property": "nameSingular",
|
|
||||||
"to": "Person",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return a migration when creating a new object', () => {
|
|
||||||
const newObject: FlatObjectMetadata = {
|
|
||||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614175001',
|
|
||||||
nameSingular: 'Company',
|
|
||||||
namePlural: 'Companies',
|
|
||||||
flatIndexMetadatas: [],
|
|
||||||
labelSingular: 'Company',
|
|
||||||
labelPlural: 'Companies',
|
|
||||||
description: 'A company',
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
{
|
|
||||||
uniqueIdentifier: '20202020-e89b-12d3-a456-426614174001',
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
type: FieldMetadataType.ADDRESS,
|
|
||||||
defaultValue: '',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = service.build({ from: [], to: [newObject] });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A company",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Companies",
|
|
||||||
"labelSingular": "Company",
|
|
||||||
"namePlural": "Companies",
|
|
||||||
"nameSingular": "Company",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A company",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174001",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Companies",
|
|
||||||
"labelSingular": "Company",
|
|
||||||
"namePlural": "Companies",
|
|
||||||
"nameSingular": "Company",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175001",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return a migration when deleting an object', () => {
|
|
||||||
const result = service.build({ from: [baseObject], to: [] });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A contact",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "First Name",
|
|
||||||
"name": "firstName",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Contacts",
|
|
||||||
"labelSingular": "Contact",
|
|
||||||
"namePlural": "Contacts",
|
|
||||||
"nameSingular": "Contact",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
|
||||||
},
|
|
||||||
"type": "delete_object",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple operations in a single migration', () => {
|
|
||||||
const objectToUpdate: FlatObjectMetadata = {
|
|
||||||
...baseObject,
|
|
||||||
nameSingular: 'Person',
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
...baseObject.flatFieldMetadatas,
|
|
||||||
{
|
|
||||||
defaultValue: '',
|
|
||||||
label: 'New field',
|
|
||||||
type: FieldMetadataType.NUMBER,
|
|
||||||
name: 'newField',
|
|
||||||
uniqueIdentifier: '20202020-3ad3-4fec-9c46-8dc9158980e3',
|
|
||||||
description: 'new field description',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const objectToDelete = {
|
|
||||||
...baseObject,
|
|
||||||
uniqueIdentifier: '20202020-59ef-4a14-a509-0a02acb248d5',
|
|
||||||
};
|
|
||||||
const objectToCreate: FlatObjectMetadata = {
|
|
||||||
uniqueIdentifier: '20202020-1218-4fc0-b32d-fc4f005c4bab',
|
|
||||||
nameSingular: 'Company',
|
|
||||||
namePlural: 'Companies',
|
|
||||||
flatIndexMetadatas: [],
|
|
||||||
labelSingular: 'Company',
|
|
||||||
labelPlural: 'Companies',
|
|
||||||
description: 'A company',
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
{
|
|
||||||
uniqueIdentifier: '20202020-1016-4f09-bad6-e75681f385f4',
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
type: FieldMetadataType.ADDRESS,
|
|
||||||
defaultValue: '',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = service.build({
|
|
||||||
from: [baseObject, objectToDelete],
|
|
||||||
to: [objectToUpdate, objectToCreate],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A company",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Companies",
|
|
||||||
"labelSingular": "Company",
|
|
||||||
"namePlural": "Companies",
|
|
||||||
"nameSingular": "Company",
|
|
||||||
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A contact",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "First Name",
|
|
||||||
"name": "firstName",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Contacts",
|
|
||||||
"labelSingular": "Contact",
|
|
||||||
"namePlural": "Contacts",
|
|
||||||
"nameSingular": "Contact",
|
|
||||||
"uniqueIdentifier": "20202020-59ef-4a14-a509-0a02acb248d5",
|
|
||||||
},
|
|
||||||
"type": "delete_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A contact",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "First Name",
|
|
||||||
"name": "firstName",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "new field description",
|
|
||||||
"label": "New field",
|
|
||||||
"name": "newField",
|
|
||||||
"type": "NUMBER",
|
|
||||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Contacts",
|
|
||||||
"labelSingular": "Contact",
|
|
||||||
"namePlural": "Contacts",
|
|
||||||
"nameSingular": "Person",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
|
||||||
},
|
|
||||||
"type": "update_object",
|
|
||||||
"updates": [
|
|
||||||
{
|
|
||||||
"from": "Contact",
|
|
||||||
"property": "nameSingular",
|
|
||||||
"to": "Person",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A company",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "20202020-1016-4f09-bad6-e75681f385f4",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Companies",
|
|
||||||
"labelSingular": "Company",
|
|
||||||
"namePlural": "Companies",
|
|
||||||
"nameSingular": "Company",
|
|
||||||
"uniqueIdentifier": "20202020-1218-4fc0-b32d-fc4f005c4bab",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "new field description",
|
|
||||||
"label": "New field",
|
|
||||||
"name": "newField",
|
|
||||||
"type": "NUMBER",
|
|
||||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "A contact",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "First Name",
|
|
||||||
"name": "firstName",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614174000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "new field description",
|
|
||||||
"label": "New field",
|
|
||||||
"name": "newField",
|
|
||||||
"type": "NUMBER",
|
|
||||||
"uniqueIdentifier": "20202020-3ad3-4fec-9c46-8dc9158980e3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Contacts",
|
|
||||||
"labelSingular": "Contact",
|
|
||||||
"namePlural": "Contacts",
|
|
||||||
"nameSingular": "Person",
|
|
||||||
"uniqueIdentifier": "20202020-e89b-12d3-a456-426614175000",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should treat objects with the same name but different IDs as distinct', () => {
|
|
||||||
const objectA: FlatObjectMetadata = {
|
|
||||||
uniqueIdentifier: 'id-1',
|
|
||||||
flatIndexMetadatas: [],
|
|
||||||
nameSingular: 'Duplicate',
|
|
||||||
namePlural: 'Duplicates',
|
|
||||||
labelSingular: 'Duplicate',
|
|
||||||
labelPlural: 'Duplicates',
|
|
||||||
description: 'First object',
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
{
|
|
||||||
uniqueIdentifier: 'field-1',
|
|
||||||
name: 'fieldA',
|
|
||||||
label: 'Field A',
|
|
||||||
type: FieldMetadataType.FULL_NAME,
|
|
||||||
defaultValue: '',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const objectB: FlatObjectMetadata = {
|
|
||||||
uniqueIdentifier: 'id-2',
|
|
||||||
nameSingular: 'Duplicate',
|
|
||||||
namePlural: 'Duplicates',
|
|
||||||
labelSingular: 'Duplicate',
|
|
||||||
labelPlural: 'Duplicates',
|
|
||||||
flatIndexMetadatas: [],
|
|
||||||
description: 'Second object',
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
{
|
|
||||||
uniqueIdentifier: 'field-2',
|
|
||||||
name: 'fieldB',
|
|
||||||
label: 'Field B',
|
|
||||||
type: FieldMetadataType.ADDRESS,
|
|
||||||
defaultValue: '',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = service.build({ from: [], to: [objectA, objectB] });
|
|
||||||
|
|
||||||
expect(result.actions).toMatchInlineSnapshot(`
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "First object",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field A",
|
|
||||||
"name": "fieldA",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "field-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Duplicates",
|
|
||||||
"labelSingular": "Duplicate",
|
|
||||||
"namePlural": "Duplicates",
|
|
||||||
"nameSingular": "Duplicate",
|
|
||||||
"uniqueIdentifier": "id-1",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "Second object",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field B",
|
|
||||||
"name": "fieldB",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "field-2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Duplicates",
|
|
||||||
"labelSingular": "Duplicate",
|
|
||||||
"namePlural": "Duplicates",
|
|
||||||
"nameSingular": "Duplicate",
|
|
||||||
"uniqueIdentifier": "id-2",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field A",
|
|
||||||
"name": "fieldA",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "field-1",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "First object",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field A",
|
|
||||||
"name": "fieldA",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "field-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Duplicates",
|
|
||||||
"labelSingular": "Duplicate",
|
|
||||||
"namePlural": "Duplicates",
|
|
||||||
"nameSingular": "Duplicate",
|
|
||||||
"uniqueIdentifier": "id-1",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field B",
|
|
||||||
"name": "fieldB",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "field-2",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "Second object",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field B",
|
|
||||||
"name": "fieldB",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "field-2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Duplicates",
|
|
||||||
"labelSingular": "Duplicate",
|
|
||||||
"namePlural": "Duplicates",
|
|
||||||
"nameSingular": "Duplicate",
|
|
||||||
"uniqueIdentifier": "id-2",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
|
|
||||||
const deleteResult = service.build({ from: [objectA, objectB], to: [] });
|
|
||||||
|
|
||||||
expect(deleteResult.actions).toMatchInlineSnapshot(`
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "First object",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field A",
|
|
||||||
"name": "fieldA",
|
|
||||||
"type": "FULL_NAME",
|
|
||||||
"uniqueIdentifier": "field-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Duplicates",
|
|
||||||
"labelSingular": "Duplicate",
|
|
||||||
"namePlural": "Duplicates",
|
|
||||||
"nameSingular": "Duplicate",
|
|
||||||
"uniqueIdentifier": "id-1",
|
|
||||||
},
|
|
||||||
"type": "delete_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"description": "Second object",
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"defaultValue": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "Field B",
|
|
||||||
"name": "fieldB",
|
|
||||||
"type": "ADDRESS",
|
|
||||||
"uniqueIdentifier": "field-2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"labelPlural": "Duplicates",
|
|
||||||
"labelSingular": "Duplicate",
|
|
||||||
"namePlural": "Duplicates",
|
|
||||||
"nameSingular": "Duplicate",
|
|
||||||
"uniqueIdentifier": "id-2",
|
|
||||||
},
|
|
||||||
"type": "delete_object",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should emit no actions when from and to are deeply equal', () => {
|
|
||||||
const obj: FlatObjectMetadata = { ...baseObject };
|
|
||||||
const result = service.build({ from: [obj], to: [obj] });
|
|
||||||
|
|
||||||
expect(result.actions).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,522 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
|
|
||||||
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
|
||||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
|
||||||
import { FlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-index-metadata';
|
|
||||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
|
||||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
|
||||||
|
|
||||||
describe('Workspace migration builder indexes tests suite', () => {
|
|
||||||
let service: WorkspaceMigrationBuilderV2Service;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new WorkspaceMigrationBuilderV2Service();
|
|
||||||
});
|
|
||||||
|
|
||||||
const createMockObject = (
|
|
||||||
identifier: string,
|
|
||||||
fields: Partial<FlatFieldMetadata>[] = [],
|
|
||||||
indexes: FlatIndexMetadata[] = [],
|
|
||||||
): FlatObjectMetadata => ({
|
|
||||||
uniqueIdentifier: identifier,
|
|
||||||
flatIndexMetadatas: indexes,
|
|
||||||
flatFieldMetadatas: fields.map((field) => ({
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'defaultName',
|
|
||||||
label: 'Default Label',
|
|
||||||
isCustom: true,
|
|
||||||
isActive: true,
|
|
||||||
isNullable: true,
|
|
||||||
uniqueIdentifier: 'default-id',
|
|
||||||
...field,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
const createMockIndex = (
|
|
||||||
name: string,
|
|
||||||
fields: string[],
|
|
||||||
isUnique = false,
|
|
||||||
): FlatIndexMetadata => ({
|
|
||||||
name,
|
|
||||||
isUnique,
|
|
||||||
indexType: IndexType.BTREE,
|
|
||||||
indexWhereClause: null,
|
|
||||||
uniqueIdentifier: name,
|
|
||||||
flatIndexFieldMetadatas: fields.map((field, index) => ({
|
|
||||||
uniqueIdentifier: `${name}-field-${index}`,
|
|
||||||
order: index,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('buildWorkspaceMigrationV2IndexActions', () => {
|
|
||||||
it('should create index actions for created indexes', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [];
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name', ['name'], true)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "TEXT",
|
|
||||||
"uniqueIdentifier": "name",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [
|
|
||||||
{
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name",
|
|
||||||
"uniqueIdentifier": "idx_company_name",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "TEXT",
|
|
||||||
"uniqueIdentifier": "name",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "TEXT",
|
|
||||||
"uniqueIdentifier": "name",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [
|
|
||||||
{
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name",
|
|
||||||
"uniqueIdentifier": "idx_company_name",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name",
|
|
||||||
"uniqueIdentifier": "idx_company_name",
|
|
||||||
},
|
|
||||||
"type": "create_index",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create delete actions for deleted indexes', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name', ['name'], true)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name",
|
|
||||||
"uniqueIdentifier": "idx_company_name",
|
|
||||||
},
|
|
||||||
"type": "delete_index",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple index changes across different objects', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name_old', ['name'], true)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name_new', ['name'], true)],
|
|
||||||
),
|
|
||||||
createMockObject(
|
|
||||||
'person',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'email',
|
|
||||||
label: 'Email',
|
|
||||||
uniqueIdentifier: 'email',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_person_email', ['email'], true)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Email",
|
|
||||||
"name": "email",
|
|
||||||
"type": "TEXT",
|
|
||||||
"uniqueIdentifier": "email",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [
|
|
||||||
{
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_person_email-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_person_email",
|
|
||||||
"uniqueIdentifier": "idx_person_email",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"uniqueIdentifier": "person",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Email",
|
|
||||||
"name": "email",
|
|
||||||
"type": "TEXT",
|
|
||||||
"uniqueIdentifier": "email",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Email",
|
|
||||||
"name": "email",
|
|
||||||
"type": "TEXT",
|
|
||||||
"uniqueIdentifier": "email",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [
|
|
||||||
{
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_person_email-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_person_email",
|
|
||||||
"uniqueIdentifier": "idx_person_email",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"uniqueIdentifier": "person",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_person_email-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_person_email",
|
|
||||||
"uniqueIdentifier": "idx_person_email",
|
|
||||||
},
|
|
||||||
"type": "create_index",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name_new-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name_new",
|
|
||||||
"uniqueIdentifier": "idx_company_name_new",
|
|
||||||
},
|
|
||||||
"type": "create_index",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name_old-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name_old",
|
|
||||||
"uniqueIdentifier": "idx_company_name_old",
|
|
||||||
},
|
|
||||||
"type": "delete_index",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle empty objects', () => {
|
|
||||||
const result = service.build({ from: [], to: [] });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle objects with no index changes', () => {
|
|
||||||
const objects = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: objects, to: objects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle index updates', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name', ['name'], true)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name', ['name'], false)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": true,
|
|
||||||
"name": "idx_company_name",
|
|
||||||
"uniqueIdentifier": "idx_company_name",
|
|
||||||
},
|
|
||||||
"type": "delete_index",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatIndexMetadata": {
|
|
||||||
"flatIndexFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"order": 0,
|
|
||||||
"uniqueIdentifier": "idx_company_name-field-0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"indexType": "BTREE",
|
|
||||||
"indexWhereClause": null,
|
|
||||||
"isUnique": false,
|
|
||||||
"name": "idx_company_name",
|
|
||||||
"uniqueIdentifier": "idx_company_name",
|
|
||||||
},
|
|
||||||
"type": "create_index",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not generate any actions when indexes are identical', () => {
|
|
||||||
const objects = [
|
|
||||||
createMockObject(
|
|
||||||
'company',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[createMockIndex('idx_company_name', ['name'], true)],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: objects, to: objects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,431 +0,0 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
|
|
||||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
|
||||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
|
||||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
|
||||||
|
|
||||||
import { FlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-field-metadata';
|
|
||||||
import { FlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration-v2/types/flat-object-metadata';
|
|
||||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
|
||||||
|
|
||||||
describe('Workspace migration builder relations tests suite', () => {
|
|
||||||
let service: WorkspaceMigrationBuilderV2Service;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new WorkspaceMigrationBuilderV2Service();
|
|
||||||
});
|
|
||||||
|
|
||||||
const createMockObject = (
|
|
||||||
identifier: string,
|
|
||||||
fields: Partial<FlatFieldMetadata>[] = [],
|
|
||||||
): FlatObjectMetadata => ({
|
|
||||||
uniqueIdentifier: identifier,
|
|
||||||
flatIndexMetadatas: [],
|
|
||||||
flatFieldMetadatas: fields.map((field) => ({
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'defaultName',
|
|
||||||
label: 'Default Label',
|
|
||||||
isCustom: true,
|
|
||||||
isActive: true,
|
|
||||||
isNullable: true,
|
|
||||||
uniqueIdentifier: 'default-id',
|
|
||||||
...field,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('buildWorkspaceMigrationV2RelationActions', () => {
|
|
||||||
it('should create relation actions for created fields', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [];
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'employees',
|
|
||||||
label: 'Employees',
|
|
||||||
uniqueIdentifier: 'employees',
|
|
||||||
relationTargetFieldMetadataId: 'field-2',
|
|
||||||
relationTargetObjectMetadataId: 'obj-2',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Employees",
|
|
||||||
"name": "employees",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "employees",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Employees",
|
|
||||||
"name": "employees",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "employees",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Employees",
|
|
||||||
"name": "employees",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "employees",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create delete actions for deleted fields', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'employees',
|
|
||||||
label: 'Employees',
|
|
||||||
uniqueIdentifier: 'employees',
|
|
||||||
relationTargetFieldMetadataId: 'field-2',
|
|
||||||
relationTargetObjectMetadataId: 'obj-2',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
const toObjects: FlatObjectMetadata[] = [createMockObject('company')];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Employees",
|
|
||||||
"name": "employees",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "employees",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "delete_field",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple relation changes across different objects', () => {
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'oldRelation',
|
|
||||||
label: 'Old Relation',
|
|
||||||
uniqueIdentifier: 'old-relation',
|
|
||||||
relationTargetFieldMetadataId: 'field-1',
|
|
||||||
relationTargetObjectMetadataId: 'obj-1',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'newRelation',
|
|
||||||
label: 'New Relation',
|
|
||||||
uniqueIdentifier: 'new-relation',
|
|
||||||
relationTargetFieldMetadataId: 'field-2',
|
|
||||||
relationTargetObjectMetadataId: 'obj-2',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
createMockObject('person', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'manager',
|
|
||||||
label: 'Manager',
|
|
||||||
uniqueIdentifier: 'manager',
|
|
||||||
relationTargetFieldMetadataId: 'field-3',
|
|
||||||
relationTargetObjectMetadataId: 'obj-3',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Manager",
|
|
||||||
"name": "manager",
|
|
||||||
"relationTargetFieldMetadataId": "field-3",
|
|
||||||
"relationTargetObjectMetadataId": "obj-3",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "manager",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "person",
|
|
||||||
},
|
|
||||||
"type": "create_object",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Manager",
|
|
||||||
"name": "manager",
|
|
||||||
"relationTargetFieldMetadataId": "field-3",
|
|
||||||
"relationTargetObjectMetadataId": "obj-3",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "manager",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Manager",
|
|
||||||
"name": "manager",
|
|
||||||
"relationTargetFieldMetadataId": "field-3",
|
|
||||||
"relationTargetObjectMetadataId": "obj-3",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "manager",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "person",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "New Relation",
|
|
||||||
"name": "newRelation",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "new-relation",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "New Relation",
|
|
||||||
"name": "newRelation",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "new-relation",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "create_field",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Old Relation",
|
|
||||||
"name": "oldRelation",
|
|
||||||
"relationTargetFieldMetadataId": "field-1",
|
|
||||||
"relationTargetObjectMetadataId": "obj-1",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "old-relation",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "New Relation",
|
|
||||||
"name": "newRelation",
|
|
||||||
"relationTargetFieldMetadataId": "field-2",
|
|
||||||
"relationTargetObjectMetadataId": "obj-2",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "new-relation",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "delete_field",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle empty objects', () => {
|
|
||||||
const result = service.build({ from: [], to: [] });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle objects with no relation changes', () => {
|
|
||||||
const objects = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
uniqueIdentifier: 'name',
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: objects, to: objects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle relation field updates', () => {
|
|
||||||
const baseField = {
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
name: 'employees',
|
|
||||||
label: 'Employees',
|
|
||||||
uniqueIdentifier: 'employees',
|
|
||||||
isCustom: true,
|
|
||||||
isActive: true,
|
|
||||||
isNullable: true,
|
|
||||||
description: 'Company employees',
|
|
||||||
};
|
|
||||||
|
|
||||||
const fromObjects: FlatObjectMetadata[] = [
|
|
||||||
createMockObject('company', [
|
|
||||||
{
|
|
||||||
...baseField,
|
|
||||||
relationTargetFieldMetadataId: 'field-1',
|
|
||||||
relationTargetObjectMetadataId: 'obj-1',
|
|
||||||
settings: {
|
|
||||||
relationType: RelationType.ONE_TO_MANY,
|
|
||||||
onDelete: RelationOnDeleteAction.CASCADE,
|
|
||||||
} as FieldMetadataSettings<FieldMetadataType.RELATION>,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
const toObjects: FlatObjectMetadata[] = [
|
|
||||||
{
|
|
||||||
...fromObjects[0],
|
|
||||||
flatFieldMetadatas: [
|
|
||||||
{
|
|
||||||
...baseField,
|
|
||||||
name: 'updatedName',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = service.build({ from: fromObjects, to: toObjects });
|
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"flatFieldMetadata": {
|
|
||||||
"description": "Company employees",
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Employees",
|
|
||||||
"name": "updatedName",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "employees",
|
|
||||||
},
|
|
||||||
"flatObjectMetadata": {
|
|
||||||
"flatFieldMetadatas": [
|
|
||||||
{
|
|
||||||
"description": "Company employees",
|
|
||||||
"isActive": true,
|
|
||||||
"isCustom": true,
|
|
||||||
"isNullable": true,
|
|
||||||
"label": "Employees",
|
|
||||||
"name": "updatedName",
|
|
||||||
"type": "RELATION",
|
|
||||||
"uniqueIdentifier": "employees",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"flatIndexMetadatas": [],
|
|
||||||
"uniqueIdentifier": "company",
|
|
||||||
},
|
|
||||||
"type": "update_field",
|
|
||||||
"updates": [
|
|
||||||
{
|
|
||||||
"from": "employees",
|
|
||||||
"property": "name",
|
|
||||||
"to": "updatedName",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { faker } from '@faker-js/faker/.';
|
import { faker } from '@faker-js/faker';
|
||||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
export const extractRecordIdsAndDatesAsExpectAny = (
|
||||||
|
record: Record<string, unknown> | Array<Record<string, unknown>>,
|
||||||
|
): any => {
|
||||||
|
if (Array.isArray(record)) {
|
||||||
|
return record.map(extractRecordIdsAndDatesAsExpectAny);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof record !== 'object') {
|
||||||
|
throw new Error(
|
||||||
|
'extractRecordIdsAndDatesAsExpectAny should be called with an array or a record only',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(record).reduce((acc, [key, value]) => {
|
||||||
|
if (!isDefined(value)) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.endsWith('Id') || key === 'id') {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[key]: expect.any(String),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[key]: expect.any(Date),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object' || Array.isArray(value)) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[key]: extractRecordIdsAndDatesAsExpectAny(
|
||||||
|
value as Record<string, unknown>,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { EachTestingContext } from '@/testing/types/EachTestingContext.type';
|
||||||
|
|
||||||
|
export const eachTestingContextFilter = <T>(
|
||||||
|
testCases: EachTestingContext<T>[],
|
||||||
|
) => {
|
||||||
|
const onlyTestsCases = testCases.filter((testCase) => testCase.only === true);
|
||||||
|
|
||||||
|
if (process.env.CI && onlyTestsCases.length > 0) {
|
||||||
|
console.warn(
|
||||||
|
'Should never push tests cases with an only to true, only to use in dev env\n returning the whole test suite anyway',
|
||||||
|
);
|
||||||
|
return testCases;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlyTestsCases.length > 0) {
|
||||||
|
return onlyTestsCases;
|
||||||
|
}
|
||||||
|
|
||||||
|
return testCases;
|
||||||
|
};
|
||||||
@ -7,5 +7,6 @@
|
|||||||
* |___/
|
* |___/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export { eachTestingContextFilter } from './EachTestingContextFilter';
|
||||||
export type { EachTestingContext } from './types/EachTestingContext.type';
|
export type { EachTestingContext } from './types/EachTestingContext.type';
|
||||||
export type { SuccessfulAndFailingTestCases } from './types/SuccessfulAndFailingTestCases';
|
export type { SuccessfulAndFailingTestCases } from './types/SuccessfulAndFailingTestCases';
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export type EachTestingContext<T> = {
|
export type EachTestingContext<T> = {
|
||||||
title: string;
|
title: string;
|
||||||
context: T;
|
context: T;
|
||||||
|
only?: true;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user