Files
Ready2Blob/src/schemas/registry.test.ts
T
kawa b8c64937ea feat(13-01): add 18 backends to BACKEND_REGISTRY with category field
- Removed explicit BackendType union literal; derived via keyof typeof BACKEND_REGISTRY
- Added BackendCategory type and category field to all 18 backend entries
- Added BackendMeta interface for individual entry typing
- Removed circular Record<BackendType,...> cast; replaced with as const
- Added 11 new backends: azure-files, swift, gdrive, dropbox, box, pcloud, ftp, webdav, smb, http, seafile
- Updated registry.test.ts: 48 tests covering all 18 backends, category assertions, field validation
2026-04-01 16:16:58 +02:00

358 lines
13 KiB
TypeScript

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);
}
});
});
});