import { describe, it, expect } from 'vitest'; import { BACKEND_REGISTRY, BackendType, BackendCategory } from './registry'; // 7 cloud-object-storage + 5 cloud-drives + 6 protocol-based = 18 total const ALL_BACKENDS: BackendType[] = [ 'azureblob', 's3', 's3-compatible', 'gcs', 'b2', 'azure-files', 'swift', 'onedrive', 'gdrive', 'dropbox', 'box', 'pcloud', 'sftp', 'ftp', 'webdav', 'smb', 'http', 'seafile', ]; // Keep the original 7 for backwards compatibility checks const ORIGINAL_7_BACKENDS: BackendType[] = ['azureblob', 's3', 's3-compatible', 'onedrive', 'sftp', 'gcs', 'b2']; const VALID_CATEGORIES: BackendCategory[] = ['cloud-object-storage', 'cloud-drives', 'protocol-based']; describe('Backend Schema Registry', () => { it('has exactly 18 backend entries', () => { const keys = Object.keys(BACKEND_REGISTRY) as BackendType[]; expect(keys).toHaveLength(18); }); it('exports all 17 required backend types', () => { for (const backend of ALL_BACKENDS) { expect(BACKEND_REGISTRY[backend]).toBeDefined(); } }); it('exports all original 7 backend types (regression)', () => { for (const backend of ORIGINAL_7_BACKENDS) { expect(BACKEND_REGISTRY[backend]).toBeDefined(); } }); it('each backend has displayName, description, category, and at least one field', () => { for (const backend of ALL_BACKENDS) { const entry = BACKEND_REGISTRY[backend]; expect(entry.displayName, `${backend}.displayName`).toBeTruthy(); expect(entry.description, `${backend}.description`).toBeTruthy(); expect(entry.category, `${backend}.category`).toBeTruthy(); expect(entry.fields.length, `${backend}.fields.length`).toBeGreaterThan(0); } }); it('each backend has a valid category value', () => { for (const backend of ALL_BACKENDS) { expect(VALID_CATEGORIES).toContain(BACKEND_REGISTRY[backend].category); } }); it('cloud-object-storage backends are correctly categorized', () => { const expected: BackendType[] = ['azureblob', 's3', 's3-compatible', 'gcs', 'b2', 'azure-files', 'swift']; for (const b of expected) { expect(BACKEND_REGISTRY[b].category).toBe('cloud-object-storage'); } }); it('cloud-drives backends are correctly categorized', () => { const expected: BackendType[] = ['onedrive', 'gdrive', 'dropbox', 'box', 'pcloud']; for (const b of expected) { expect(BACKEND_REGISTRY[b].category).toBe('cloud-drives'); } }); it('protocol-based backends are correctly categorized', () => { const expected: BackendType[] = ['sftp', 'ftp', 'webdav', 'smb', 'http', 'seafile']; for (const b of expected) { expect(BACKEND_REGISTRY[b].category).toBe('protocol-based'); } }); it('each backend has at least one field definition', () => { for (const backend of ALL_BACKENDS) { expect(BACKEND_REGISTRY[backend].fields.length).toBeGreaterThan(0); } }); it('each FieldDef has a non-empty key (snake_case, no camelCase)', () => { for (const backend of ALL_BACKENDS) { for (const field of BACKEND_REGISTRY[backend].fields) { expect(field.key).toBeTruthy(); // Reject camelCase — rclone keys are snake_case or lowercase expect(field.key).not.toMatch(/[A-Z]/); } } }); it('each FieldDef has a non-empty label', () => { for (const backend of ALL_BACKENDS) { for (const field of BACKEND_REGISTRY[backend].fields) { expect(field.label).toBeTruthy(); } } }); it('each backend entry has displayName and description metadata', () => { for (const backend of ALL_BACKENDS) { expect(BACKEND_REGISTRY[backend].displayName).toBeTruthy(); expect(BACKEND_REGISTRY[backend].description).toBeTruthy(); } }); it('Azure Blob has account field (required)', () => { const accountField = BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account'); expect(accountField).toBeDefined(); expect(accountField!.required).toBe(true); }); it('S3 has access_key_id, secret_access_key, and region fields', () => { const keys = BACKEND_REGISTRY.s3.fields.map(f => f.key); expect(keys).toContain('access_key_id'); expect(keys).toContain('secret_access_key'); expect(keys).toContain('region'); }); it('S3-compatible has endpoint field (required)', () => { const endpointField = BACKEND_REGISTRY['s3-compatible'].fields.find(f => f.key === 'endpoint'); expect(endpointField).toBeDefined(); expect(endpointField!.required).toBe(true); }); describe('OneDrive backend', () => { it('has token field (required)', () => { const f = BACKEND_REGISTRY.onedrive.fields.find(f => f.key === 'token'); expect(f).toBeDefined(); expect(f!.required).toBe(true); expect(f!.inputType).toBe('password'); }); it('has drive_id field (required)', () => { const f = BACKEND_REGISTRY.onedrive.fields.find(f => f.key === 'drive_id'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has drive_type field as select', () => { const f = BACKEND_REGISTRY.onedrive.fields.find(f => f.key === 'drive_type'); expect(f).toBeDefined(); expect(f!.inputType).toBe('select'); expect(f!.options).toBeDefined(); }); }); describe('SFTP backend', () => { it('has host field (required)', () => { const f = BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'host'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has user field (required)', () => { const f = BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'user'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has pass field (optional — SftpAuthToggle handles display)', () => { const f = BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'pass'); expect(f).toBeDefined(); expect(f!.required).toBe(false); }); it('has key_pem field (optional — SftpAuthToggle handles display)', () => { const f = BACKEND_REGISTRY.sftp.fields.find(f => f.key === 'key_pem'); expect(f).toBeDefined(); expect(f!.required).toBe(false); }); }); describe('Google Cloud Storage backend', () => { it('has project_number field (required)', () => { const f = BACKEND_REGISTRY.gcs.fields.find(f => f.key === 'project_number'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has service_account_credentials field (required, password)', () => { const f = BACKEND_REGISTRY.gcs.fields.find(f => f.key === 'service_account_credentials'); expect(f).toBeDefined(); expect(f!.required).toBe(true); expect(f!.inputType).toBe('password'); }); }); describe('Backblaze B2 backend', () => { it('has account field (required) — applicationKeyId', () => { const f = BACKEND_REGISTRY.b2.fields.find(f => f.key === 'account'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has key field (required) — application key secret', () => { const f = BACKEND_REGISTRY.b2.fields.find(f => f.key === 'key'); expect(f).toBeDefined(); expect(f!.required).toBe(true); expect(f!.inputType).toBe('password'); }); }); describe('Azure Files backend (new)', () => { it('has account field (required)', () => { const f = BACKEND_REGISTRY['azure-files'].fields.find(f => f.key === 'account'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has key field (required)', () => { const f = BACKEND_REGISTRY['azure-files'].fields.find(f => f.key === 'key'); expect(f).toBeDefined(); expect(f!.required).toBe(true); expect(f!.inputType).toBe('password'); }); }); describe('Swift backend (new)', () => { it('has user, key, auth, and tenant fields (all required)', () => { const fields = BACKEND_REGISTRY.swift.fields; const keys = fields.map(f => f.key); expect(keys).toContain('user'); expect(keys).toContain('key'); expect(keys).toContain('auth'); expect(keys).toContain('tenant'); }); it('has optional region field', () => { const f = BACKEND_REGISTRY.swift.fields.find(f => f.key === 'region'); expect(f).toBeDefined(); expect(f!.required).toBe(false); }); }); describe('Google Drive backend (new)', () => { it('has token field (optional — service account path also supported)', () => { const f = BACKEND_REGISTRY.gdrive.fields.find(f => f.key === 'token'); expect(f).toBeDefined(); expect(f!.inputType).toBe('password'); }); it('has service_account_credentials field (optional)', () => { const f = BACKEND_REGISTRY.gdrive.fields.find(f => f.key === 'service_account_credentials'); expect(f).toBeDefined(); expect(f!.required).toBe(false); }); it('has root_folder_id field (optional)', () => { const f = BACKEND_REGISTRY.gdrive.fields.find(f => f.key === 'root_folder_id'); expect(f).toBeDefined(); expect(f!.required).toBe(false); }); }); describe('Dropbox backend (new)', () => { it('has token field (required)', () => { const f = BACKEND_REGISTRY.dropbox.fields.find(f => f.key === 'token'); expect(f).toBeDefined(); expect(f!.required).toBe(true); expect(f!.inputType).toBe('password'); }); }); describe('Box backend (new)', () => { it('has token field (required)', () => { const f = BACKEND_REGISTRY.box.fields.find(f => f.key === 'token'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has box_sub_type select field (optional)', () => { const f = BACKEND_REGISTRY.box.fields.find(f => f.key === 'box_sub_type'); expect(f).toBeDefined(); expect(f!.inputType).toBe('select'); expect(f!.required).toBe(false); }); }); describe('pCloud backend (new)', () => { it('has token field (required)', () => { const f = BACKEND_REGISTRY.pcloud.fields.find(f => f.key === 'token'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has hostname select field (optional, US/EU options)', () => { const f = BACKEND_REGISTRY.pcloud.fields.find(f => f.key === 'hostname'); expect(f).toBeDefined(); expect(f!.inputType).toBe('select'); const values = f!.options!.map(o => o.value); expect(values).toContain('api.pcloud.com'); expect(values).toContain('eapi.pcloud.com'); }); }); describe('FTP backend (new)', () => { it('has host field (required)', () => { const f = BACKEND_REGISTRY.ftp.fields.find(f => f.key === 'host'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has user, pass, port fields (all optional)', () => { const fields = BACKEND_REGISTRY.ftp.fields; const optional = ['user', 'pass', 'port']; for (const key of optional) { const f = fields.find(f => f.key === key); expect(f, `ftp.${key}`).toBeDefined(); expect(f!.required, `ftp.${key}.required`).toBe(false); } }); it('has explicit_tls select field (optional)', () => { const f = BACKEND_REGISTRY.ftp.fields.find(f => f.key === 'explicit_tls'); expect(f).toBeDefined(); expect(f!.inputType).toBe('select'); }); }); describe('WebDAV backend (new)', () => { it('has url, user, pass fields (required)', () => { const fields = BACKEND_REGISTRY.webdav.fields; for (const key of ['url', 'user', 'pass']) { const f = fields.find(f => f.key === key); expect(f, `webdav.${key}`).toBeDefined(); expect(f!.required, `webdav.${key}.required`).toBe(true); } }); it('has vendor select field (optional) with nextcloud/sharepoint options', () => { const f = BACKEND_REGISTRY.webdav.fields.find(f => f.key === 'vendor'); expect(f).toBeDefined(); expect(f!.inputType).toBe('select'); const values = f!.options!.map(o => o.value); expect(values).toContain('nextcloud'); expect(values).toContain('sharepoint'); }); }); describe('SMB backend (new)', () => { it('has host field (required)', () => { const f = BACKEND_REGISTRY.smb.fields.find(f => f.key === 'host'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has user field (required)', () => { const f = BACKEND_REGISTRY.smb.fields.find(f => f.key === 'user'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); it('has pass, domain, port fields (optional)', () => { for (const key of ['pass', 'domain', 'port']) { const f = BACKEND_REGISTRY.smb.fields.find(f => f.key === key); expect(f, `smb.${key}`).toBeDefined(); expect(f!.required, `smb.${key}.required`).toBe(false); } }); }); describe('HTTP backend (new)', () => { it('has url field (required)', () => { const f = BACKEND_REGISTRY.http.fields.find(f => f.key === 'url'); expect(f).toBeDefined(); expect(f!.required).toBe(true); }); }); describe('Seafile backend (new)', () => { it('has url, user, pass fields (all required)', () => { for (const key of ['url', 'user', 'pass']) { const f = BACKEND_REGISTRY.seafile.fields.find(f => f.key === key); expect(f, `seafile.${key}`).toBeDefined(); expect(f!.required, `seafile.${key}.required`).toBe(true); } }); }); });