1
import type { Diff } from 'deep-diff';
2
import type { Knex } from 'knex';
3
import knex from 'knex';
4
import { createTracker, MockClient, Tracker } from 'knex-mock-client';
5
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
6
import { ForbiddenError } from '@directus/errors';
7
import type { Collection } from '../types/collection.js';
8
import type { Snapshot, SnapshotDiffWithHash } from '../types/snapshot.js';
9
import { applyDiff } from '../utils/apply-diff.js';
10
import { getSnapshot } from '../utils/get-snapshot.js';
11
import { SchemaService } from './schema.js';
13
vi.mock('directus/version', () => ({ version: '0.0.0' }));
15
vi.mock('../../src/database/index.js', () => {
16
return { __esModule: true, default: vi.fn(), getDatabaseClient: vi.fn().mockReturnValue('postgres') };
19
vi.mock('../utils/get-snapshot.js', () => ({
23
vi.mock('../utils/apply-diff.js', () => ({
27
class Client_PG extends MockClient {}
41
const testCollectionDiff = {
49
accountability: 'all',
54
item_duplication_fields: null,
59
schema: { name: 'test' },
62
] satisfies Diff<Collection>[],
66
db = knex.default({ client: Client_PG });
67
tracker = createTracker(db);
75
describe('Services / Schema', () => {
76
describe('snapshot', () => {
77
it('should throw ForbiddenError for non-admin user', async () => {
78
vi.mocked(getSnapshot).mockResolvedValueOnce(testSnapshot);
80
const service = new SchemaService({ knex: db, accountability: { role: 'test', admin: false } });
82
expect(service.snapshot()).rejects.toThrowError(ForbiddenError);
85
it('should return snapshot for admin user', async () => {
86
vi.mocked(getSnapshot).mockResolvedValueOnce(testSnapshot);
88
const service = new SchemaService({ knex: db, accountability: { role: 'admin', admin: true } });
90
expect(service.snapshot()).resolves.toEqual(testSnapshot);
94
describe('apply', () => {
95
const snapshotDiffWithHash = {
96
hash: '813b3cdf7013310fafde7813b7d5e6bd4eb1e73f',
98
collections: [testCollectionDiff],
102
} satisfies SnapshotDiffWithHash;
104
it('should throw ForbiddenError for non-admin user', async () => {
105
vi.mocked(getSnapshot).mockResolvedValueOnce(testSnapshot);
107
const service = new SchemaService({ knex: db, accountability: { role: 'test', admin: false } });
109
expect(service.apply(snapshotDiffWithHash)).rejects.toThrowError(ForbiddenError);
110
expect(vi.mocked(applyDiff)).not.toHaveBeenCalledOnce();
113
it('should apply for admin user', async () => {
114
vi.mocked(getSnapshot).mockResolvedValueOnce(testSnapshot);
116
const service = new SchemaService({ knex: db, accountability: { role: 'admin', admin: true } });
118
await service.apply(snapshotDiffWithHash);
120
expect(vi.mocked(applyDiff)).toHaveBeenCalledOnce();
124
describe('diff', () => {
125
const snapshotToApply = {
133
accountability: 'all',
138
item_duplication_fields: null,
150
} satisfies Snapshot;
152
it('should throw ForbiddenError for non-admin user', async () => {
153
const service = new SchemaService({ knex: db, accountability: { role: 'test', admin: false } });
155
expect(service.diff(snapshotToApply, { currentSnapshot: testSnapshot, force: true })).rejects.toThrowError(
160
it('should return diff for admin user', async () => {
161
const service = new SchemaService({ knex: db, accountability: { role: 'admin', admin: true } });
163
expect(service.diff(snapshotToApply, { currentSnapshot: testSnapshot, force: true })).resolves.toEqual({
164
collections: [testCollectionDiff],
170
it('should return null for empty diff', async () => {
171
const service = new SchemaService({ knex: db, accountability: { role: 'admin', admin: true } });
173
expect(service.diff(testSnapshot, { currentSnapshot: testSnapshot, force: true })).resolves.toBeNull();
177
describe('getHashedSnapshot', () => {
178
it('should return snapshot for admin user', async () => {
179
const service = new SchemaService({ knex: db, accountability: { role: 'admin', admin: true } });
181
expect(service.getHashedSnapshot(testSnapshot)).toEqual(
182
expect.objectContaining({
184
hash: expect.any(String),