Files
directus/api/src/utils/validate-diff.test.ts
2023-11-20 16:23:22 +01:00

324 lines
7.4 KiB
TypeScript

import { describe, expect, test } from 'vitest';
import type { Collection } from '../types/collection.js';
import type {
Snapshot,
SnapshotDiff,
SnapshotDiffWithHash,
SnapshotField,
SnapshotRelation,
SnapshotWithHash,
} from '../types/snapshot.js';
import { validateApplyDiff } from './validate-diff.js';
test('should fail on invalid diff schema', () => {
const diff = {} as SnapshotDiffWithHash;
const snapshot = {} as SnapshotWithHash;
expect(() => validateApplyDiff(diff, snapshot)).toThrowError('"hash" is required');
});
test('should fail on invalid hash', () => {
const diff = {
hash: 'abc',
diff: { collections: [{ collection: 'test', diff: [] }], fields: [], relations: [] },
} as SnapshotDiffWithHash;
const snapshot = { hash: 'xyz' } as SnapshotWithHash;
expect(() => validateApplyDiff(diff, snapshot)).toThrowError(
"Provided hash does not match the current instance's schema hash",
);
});
describe('should throw accurate error', () => {
const baseDiff = (partialDiff: Partial<SnapshotDiff>): SnapshotDiffWithHash => {
return {
hash: 'abc',
diff: {
fields: [],
collections: [],
relations: [],
...partialDiff,
},
};
};
const baseSnapshot = (partialSnapshot?: Partial<Snapshot>) => {
return {
hash: 'xyz',
collections: [] as Collection[],
fields: [] as SnapshotField[],
relations: [] as SnapshotRelation[],
...partialSnapshot,
} as SnapshotWithHash;
};
test('creating collection which already exists', () => {
const diff = baseDiff({
collections: [{ collection: 'test', diff: [{ kind: 'N', rhs: {} as Collection }] }],
});
const snapshot = baseSnapshot({ collections: [{ collection: 'test' } as Collection] });
expect(() => validateApplyDiff(diff, snapshot)).toThrowError(
'Provided diff is trying to create collection "test" but it already exists',
);
});
test('deleting collection which does not exist', () => {
const diff = baseDiff({
collections: [{ collection: 'test', diff: [{ kind: 'D', lhs: {} as Collection }] }],
});
expect(() => validateApplyDiff(diff, baseSnapshot())).toThrowError(
'Provided diff is trying to delete collection "test" but it does not exist',
);
});
test('creating field which already exists', () => {
const diff = baseDiff({
fields: [{ collection: 'test', field: 'test', diff: [{ kind: 'N', rhs: {} as SnapshotField }] }],
});
const snapshot = baseSnapshot({ fields: [{ collection: 'test', field: 'test' } as SnapshotField] });
expect(() => validateApplyDiff(diff, snapshot)).toThrowError(
'Provided diff is trying to create field "test.test" but it already exists',
);
});
test('deleting field which does not exist', () => {
const diff = baseDiff({
fields: [{ collection: 'test', field: 'test', diff: [{ kind: 'D', lhs: {} as SnapshotField }] }],
});
expect(() => validateApplyDiff(diff, baseSnapshot())).toThrowError(
'Provided diff is trying to delete field "test.test" but it does not exist',
);
});
test('creating relation which already exists', () => {
const diff = baseDiff({
relations: [
{
collection: 'test',
field: 'test',
related_collection: 'relation',
diff: [{ kind: 'N', rhs: {} as SnapshotRelation }],
},
],
});
const snapshot = baseSnapshot({
relations: [{ collection: 'test', field: 'test', related_collection: 'relation' } as SnapshotRelation],
});
expect(() => validateApplyDiff(diff, snapshot)).toThrowError(
'Provided diff is trying to create relation "test.test-> relation" but it already exists',
);
});
test('deleting relation which does not exist', () => {
const diff = baseDiff({
relations: [
{
collection: 'test',
field: 'test',
related_collection: 'relation',
diff: [{ kind: 'D', lhs: {} as SnapshotRelation }],
},
],
});
expect(() => validateApplyDiff(diff, baseSnapshot())).toThrowError(
'Provided diff is trying to delete relation "test.test-> relation" but it does not exist',
);
});
});
test('should not throw error for diffs with varying types of lhs/rhs', () => {
const diff: any = {
hash: 'abc',
diff: {
collections: [
{
collection: 'a',
diff: [
{
kind: 'E',
path: ['meta', 'color'],
lhs: null,
rhs: '#6644FF',
},
],
},
{
collection: 'a',
diff: [
{
kind: 'A',
path: ['meta', 'translations'],
index: 1,
item: {
kind: 'N',
rhs: {
language: 'de-DE',
translation: 'Collection A de-DE',
},
},
},
],
},
{
collection: 'b',
diff: [
{
kind: 'E',
path: ['meta', 'translations', 1, 'language'],
lhs: 'es-ES',
rhs: 'nl-NL',
},
{
kind: 'E',
path: ['meta', 'translations', 1, 'translation'],
lhs: 'nombre',
rhs: 'naam',
},
],
},
],
fields: [
{
collection: 'a',
field: 'new_field',
diff: [
{
kind: 'N',
rhs: {
collection: 'a',
field: 'new_field',
type: 'string',
meta: {},
schema: {},
},
},
],
},
{
collection: 'a',
field: 'update_field',
diff: [
{
kind: 'E',
path: ['meta', 'options'],
lhs: {
iconLeft: 'check_circle',
},
rhs: null,
},
],
},
{
collection: 'a',
field: 'delete_field',
diff: [
{
kind: 'D',
lhs: {
collection: 'a',
field: 'delete_field',
type: 'string',
meta: {},
schema: {},
},
},
],
},
],
relations: [
{
collection: 'a',
field: 'm2o',
related_collection: 'b',
diff: [
{
kind: 'E',
path: ['schema', 'on_delete'],
lhs: 'SET NULL',
rhs: 'CASCADE',
},
],
},
],
},
};
const snapshot = { hash: 'abc' } as SnapshotWithHash;
expect(() => validateApplyDiff(diff, snapshot)).not.toThrow();
});
test('should not throw error for relation diff with null related_collection (applicable for M2A junction tables)', () => {
const diff: any = {
hash: 'abc',
diff: {
collections: [],
fields: [],
relations: [
{
collection: 'pages_blocks',
field: 'item',
related_collection: null,
diff: [
{
kind: 'N',
rhs: {
collection: 'pages_blocks',
field: 'item',
related_collection: null,
meta: {
junction_field: 'pages_id',
many_collection: 'pages_blocks',
many_field: 'item',
one_allowed_collections: ['a', 'b'],
one_collection: null,
one_collection_field: 'collection',
one_deselect_action: 'nullify',
one_field: null,
sort_field: null,
},
},
},
],
},
],
},
};
const snapshot = { hash: 'abc' } as SnapshotWithHash;
expect(() => validateApplyDiff(diff, snapshot)).not.toThrow();
});
test('should detect empty diff', () => {
const diff = {
hash: 'abc',
diff: { collections: [], fields: [], relations: [] },
};
const snapshot = {} as SnapshotWithHash;
expect(validateApplyDiff(diff, snapshot)).toBe(false);
});
test('should pass on valid diff', () => {
const diff = {
hash: 'abc',
diff: { collections: [{ collection: 'test', diff: [] }], fields: [], relations: [] },
};
const snapshot = { hash: 'abc' } as SnapshotWithHash;
expect(validateApplyDiff(diff, snapshot)).toBe(true);
});