diff --git a/packages/e2e/dumps/sources/multiple-dependencies/collections/panels.json b/packages/e2e/dumps/sources/multiple-dependencies/collections/panels.json index e69de29b..fe51488c 100644 --- a/packages/e2e/dumps/sources/multiple-dependencies/collections/panels.json +++ b/packages/e2e/dumps/sources/multiple-dependencies/collections/panels.json @@ -0,0 +1 @@ +[] diff --git a/packages/e2e/spec/dependencies/index.ts b/packages/e2e/spec/dependencies/index.ts index 8ae316df..e5c04c67 100644 --- a/packages/e2e/spec/dependencies/index.ts +++ b/packages/e2e/spec/dependencies/index.ts @@ -1,2 +1 @@ export * from './push-with-dependencies.js'; -export * from './update-with-dependencies.js'; diff --git a/packages/e2e/spec/entrypoint.spec.ts b/packages/e2e/spec/entrypoint.spec.ts index 39e89b5a..c676628b 100644 --- a/packages/e2e/spec/entrypoint.spec.ts +++ b/packages/e2e/spec/entrypoint.spec.ts @@ -2,21 +2,18 @@ import 'dotenv/config'; import './helpers/env.js'; import { Context } from './helpers/index.js'; import { - pullAndPushWithoutData, - pullAndPushWithoutChanges, preserveIds, + pullAndPushWithChanges, + pullAndPushWithDeletions, + pullAndPushWithoutChanges, + pullAndPushWithoutData, pullBasic, - pushFlushAndPush, pullWithNewData, + pushFlushAndPush, pushOnEmptyInstance, pushTwiceOnEmptyInstance, - pullAndPushWithChanges, - pullAndPushWithDeletions, } from './pull-diff-push/index.js'; -import { - pushWithDependencies, - updateWithDependencies, -} from './dependencies/index.js'; +import { pushWithDependencies } from './dependencies/index.js'; import { collectionsOnDump, collectionsOnLoad, @@ -36,6 +33,10 @@ import { removePermissionDuplicates, } from './permissions/index.js'; import { removeTrackedItem } from './untrack/index.js'; +import { + createOperationsWithConflicts, + updateOperationsWithConflicts, +} from './operations/index.js'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; @@ -70,7 +71,9 @@ describe('Tests entrypoint ->', () => { pushTwiceOnEmptyInstance(context); pushWithDependencies(context); - updateWithDependencies(context); + + updateOperationsWithConflicts(context); + createOperationsWithConflicts(context); collectionsOnDump(context); collectionsOnSave(context); diff --git a/packages/e2e/spec/operations/create-operations-with-conflicts.ts b/packages/e2e/spec/operations/create-operations-with-conflicts.ts new file mode 100644 index 00000000..5c7e88db --- /dev/null +++ b/packages/e2e/spec/operations/create-operations-with-conflicts.ts @@ -0,0 +1,90 @@ +import { + Context, + getDumpedSystemCollectionsContents, + info, + readAllSystemCollections, + SystemCollectionsContentWithSyncId, +} from '../helpers/index.js'; +import { deleteOperation, updateOperation } from '@directus/sdk'; + +type OperationFromJson = SystemCollectionsContentWithSyncId['operations'][0]; + +export const createOperationsWithConflicts = (context: Context) => { + it('create an operation that conflicts with other', async () => { + // Init sync client + const sync = await context.getSync('sources/multiple-dependencies', false); + const directus = context.getDirectus(); + const client = directus.get(); + await sync.push(); + + // Get the sync id map + let idMaps = await directus.getSyncIdMaps('operations'); + const getLocalId = (syncId: string) => { + const localId = idMaps.find((m) => m.sync_id === syncId)?.local_id; + if (!localId) throw new Error(`Local id not found for sync id ${syncId}`); + return localId; + }; + + // Remove the second operation and make the first one point to the third + const { operations } = getDumpedSystemCollectionsContents( + sync.getDumpPath(), + ); + const [operation1, operation2, operation3] = operations as [ + OperationFromJson, + OperationFromJson, + OperationFromJson, + ]; + await client.request( + updateOperation(getLocalId(operation1._syncId), { + resolve: null, + }), + ); + await client.request(deleteOperation(getLocalId(operation2._syncId))); + await client.request( + updateOperation(getLocalId(operation1._syncId), { + resolve: getLocalId(operation3._syncId), + }), + ); + + // Push back the data + const beforePushDate = new Date(); + const output = await sync.push(); + + expect(output).toContain(info(`[operations] Deleted 1 dangling items`)); + expect(output).toContain(info(`[operations] Created 1 items`)); + expect(output).toContain(info(`[operations] Updated 1 items`)); + expect(output).toContain(info(`[operations] Deleted 0 items`)); + + // Ensure that no activities were created + const activities = (await directus.getActivities(beforePushDate)).filter( + (a) => a.collection === 'directus_operations', + ); + expect(activities.filter((a) => a.action === 'create').length).toEqual(1); + expect(activities.filter((a) => a.action === 'delete')).toEqual([]); + + // One extra to nullify the resolve column and avoid unique constraint conflict + expect(activities.filter((a) => a.action === 'update').length).toEqual(2); + + // Get the operations ids + idMaps = await directus.getSyncIdMaps('operations'); + const id1 = getLocalId(operation1._syncId); + const id2 = getLocalId(operation2._syncId); + const id3 = getLocalId(operation3._syncId); + + // Get the new operations + const { operations: newOperations } = + await readAllSystemCollections(client); + const newOperation1 = newOperations.find((o) => o.id === id1); + const newOperation2 = newOperations.find((o) => o.id === id2); + const newOperation3 = newOperations.find((o) => o.id === id3); + + // Check if hte chain is correct + expect(newOperation1).toBeDefined(); + expect(newOperation2).toBeDefined(); + expect(newOperation3).toBeDefined(); + + expect(newOperation1?.resolve).toEqual(id2); + expect(newOperation2?.resolve).toEqual(id3); + expect(newOperation3?.resolve).toBeNull(); + }); +}; diff --git a/packages/e2e/spec/operations/index.ts b/packages/e2e/spec/operations/index.ts new file mode 100644 index 00000000..8dc53246 --- /dev/null +++ b/packages/e2e/spec/operations/index.ts @@ -0,0 +1,2 @@ +export * from './update-operations-with-conflicts.js'; +export * from './create-operations-with-conflicts.js'; diff --git a/packages/e2e/spec/dependencies/update-with-dependencies.ts b/packages/e2e/spec/operations/update-operations-with-conflicts.ts similarity index 90% rename from packages/e2e/spec/dependencies/update-with-dependencies.ts rename to packages/e2e/spec/operations/update-operations-with-conflicts.ts index 088560bc..b3d3520b 100644 --- a/packages/e2e/spec/dependencies/update-with-dependencies.ts +++ b/packages/e2e/spec/operations/update-operations-with-conflicts.ts @@ -1,7 +1,7 @@ import { Context, info } from '../helpers/index.js'; -export const updateWithDependencies = (context: Context) => { - it('reverse 2 operations dependencies', async () => { +export const updateOperationsWithConflicts = (context: Context) => { + it('reverse 2 operations conflicts', async () => { // Init sync client const syncInit = await context.getSync( 'sources/multiple-dependencies',