appwrite-utils-cli
Version:
Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.
277 lines (223 loc) • 9.69 kB
text/typescript
import { jest } from '@jest/globals';
import { TestUtils } from '../testUtils';
import { AdapterFactory } from '../../src/adapters/AdapterFactory';
import { LegacyAdapter } from '../../src/adapters/LegacyAdapter';
import { TablesDBAdapter } from '../../src/adapters/TablesDBAdapter';
import { DatabaseAdapter } from '../../src/adapters/DatabaseAdapter';
// Mock the adapters
jest.mock('../../src/adapters/LegacyAdapter');
jest.mock('../../src/adapters/TablesDBAdapter');
jest.mock('../../src/adapters/DatabaseAdapter');
// Mock version detection
jest.mock('../../src/utils/versionDetection', () => ({
detectAppwriteVersionCached: jest.fn(),
fetchServerVersion: jest.fn(),
isVersionAtLeast: jest.fn(),
}));
import { detectAppwriteVersionCached, isVersionAtLeast } from '../../src/utils/versionDetection';
describe('AdapterFactory - Dual API Support', () => {
let mockConfig: any;
beforeEach(() => {
jest.clearAllMocks();
mockConfig = TestUtils.createTestAppwriteConfig();
});
describe('Adapter Selection Logic', () => {
it('should create LegacyAdapter for old Appwrite versions', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.5.0',
apiMode: 'database',
});
(isVersionAtLeast as jest.Mock).mockReturnValue(false);
const adapter = await AdapterFactory.createAdapter(mockConfig);
expect(LegacyAdapter).toHaveBeenCalledWith(expect.anything(), mockConfig);
expect(adapter).toBeInstanceOf(LegacyAdapter);
});
it('should create DatabaseAdapter for Appwrite 1.6.0+ in database mode', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.6.0',
apiMode: 'database',
});
(isVersionAtLeast as jest.Mock).mockReturnValue(true);
const adapter = await AdapterFactory.createAdapter(mockConfig);
expect(DatabaseAdapter).toHaveBeenCalledWith(expect.anything(), mockConfig);
expect(adapter).toBeInstanceOf(DatabaseAdapter);
});
it('should create TablesDBAdapter for Appwrite 1.8.0+ in tablesdb mode', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.8.0',
apiMode: 'tablesdb',
});
(isVersionAtLeast as jest.Mock).mockReturnValue(true);
const adapter = await AdapterFactory.createAdapter(mockConfig);
expect(TablesDBAdapter).toHaveBeenCalledWith(expect.anything(), mockConfig);
expect(adapter).toBeInstanceOf(TablesDBAdapter);
});
it('should force TablesDBAdapter when explicitly specified in config', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.6.0',
apiMode: 'database',
});
const configWithForce = {
...mockConfig,
forceTablesAPI: true,
};
const adapter = await AdapterFactory.createAdapter(configWithForce);
expect(TablesDBAdapter).toHaveBeenCalledWith(expect.anything(), configWithForce);
expect(adapter).toBeInstanceOf(TablesDBAdapter);
});
it('should force DatabaseAdapter when explicitly specified in config', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.8.0',
apiMode: 'tablesdb',
});
const configWithForce = {
...mockConfig,
forceDatabaseAPI: true,
};
const adapter = await AdapterFactory.createAdapter(configWithForce);
expect(DatabaseAdapter).toHaveBeenCalledWith(expect.anything(), configWithForce);
expect(adapter).toBeInstanceOf(DatabaseAdapter);
});
});
describe('Configuration Validation', () => {
it('should validate database configuration for DatabaseAdapter', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.6.0',
apiMode: 'database',
});
const configWithMissingDB = {
...mockConfig,
databases: [],
};
await expect(AdapterFactory.createAdapter(configWithMissingDB))
.rejects.toThrow('Database configuration required');
});
it('should validate table configurations for TablesDBAdapter', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.8.0',
apiMode: 'tablesdb',
});
const configWithTablesButNoDatabaseIds = {
...mockConfig,
collections: [
TestUtils.createTestTable({
databaseId: undefined, // Missing required databaseId
})
],
};
await expect(AdapterFactory.createAdapter(configWithTablesButNoDatabaseIds))
.rejects.toThrow('Table configurations must include databaseId');
});
});
describe('Mixed Configuration Handling', () => {
it('should handle mixed collections and tables for appropriate adapter', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.8.0',
apiMode: 'tablesdb',
});
const mixedConfig = {
...mockConfig,
collections: [
TestUtils.createTestCollection(), // Regular collection
TestUtils.createTestTable(), // Table with _isFromTablesDir
],
};
const adapter = await AdapterFactory.createAdapter(mixedConfig);
expect(TablesDBAdapter).toHaveBeenCalledWith(expect.anything(), mixedConfig);
expect(adapter).toBeInstanceOf(TablesDBAdapter);
});
it('should filter out inappropriate configurations per adapter', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.6.0',
apiMode: 'database',
});
const mixedConfig = {
...mockConfig,
collections: [
TestUtils.createTestCollection(), // Regular collection
TestUtils.createTestTable(), // Table (should be filtered for DatabaseAdapter)
],
};
const adapter = await AdapterFactory.createAdapter(mixedConfig);
expect(DatabaseAdapter).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
collections: expect.arrayContaining([
expect.not.objectContaining({ _isFromTablesDir: true })
])
})
);
});
});
describe('Error Handling', () => {
it('should handle version detection failures gracefully', async () => {
(detectAppwriteVersionCached as jest.Mock).mockRejectedValue(new Error('Network error'));
// Should fall back to LegacyAdapter
const adapter = await AdapterFactory.createAdapter(mockConfig);
expect(LegacyAdapter).toHaveBeenCalledWith(expect.anything(), mockConfig);
expect(adapter).toBeInstanceOf(LegacyAdapter);
});
it('should handle invalid server versions', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: 'invalid-version',
apiMode: 'unknown',
});
const adapter = await AdapterFactory.createAdapter(mockConfig);
expect(LegacyAdapter).toHaveBeenCalledWith(expect.anything(), mockConfig);
expect(adapter).toBeInstanceOf(LegacyAdapter);
});
});
describe('Client Configuration', () => {
it('should pass correct client configuration to each adapter', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.8.0',
apiMode: 'tablesdb',
});
await AdapterFactory.createAdapter(mockConfig);
expect(TablesDBAdapter).toHaveBeenCalledWith(
expect.objectContaining({
setEndpoint: expect.any(Function),
setProject: expect.any(Function),
setKey: expect.any(Function),
}),
mockConfig
);
});
it('should handle custom endpoint configurations', async () => {
const customConfig = {
...mockConfig,
appwriteEndpoint: 'https://custom.appwrite.server/v1',
};
await AdapterFactory.createAdapter(customConfig);
// Verify that the adapter receives the custom endpoint configuration
expect(TablesDBAdapter || DatabaseAdapter || LegacyAdapter).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
appwriteEndpoint: 'https://custom.appwrite.server/v1',
})
);
});
});
describe('Caching and Performance', () => {
it('should cache adapter instances for same configuration', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.6.0',
apiMode: 'database',
});
const adapter1 = await AdapterFactory.createAdapter(mockConfig);
const adapter2 = await AdapterFactory.createAdapter(mockConfig);
// Should reuse the same instance or create efficiently
expect(DatabaseAdapter).toHaveBeenCalledTimes(2);
});
it('should use cached version detection results', async () => {
(detectAppwriteVersionCached as jest.Mock).mockResolvedValue({
serverVersion: '1.6.0',
apiMode: 'database',
});
await AdapterFactory.createAdapter(mockConfig);
await AdapterFactory.createAdapter(mockConfig);
// Version detection should be called but may be cached
expect(detectAppwriteVersionCached).toHaveBeenCalled();
});
});
});