feat(13-01): auto-generate BACKEND_SCHEMAS, update RCLONE_TYPE_MAP for all 18 backends

- BACKEND_SCHEMAS now auto-generated via Object.fromEntries over registry keys
- RCLONE_TYPE_MAP expanded with 11 new entries for all 18 backend types
- RCLONE_TYPE_MAP exported for direct testing
- index.test.ts: added auto-generation coverage (3 tests) + 4 new backend schema smoke tests
- rclone-conf.test.ts: added RCLONE_TYPE_MAP exhaustiveness test + gdrive/ftp/smb conf output tests
- Rule 1 fix: BackendSelectionStep test used /next/i regex matching WebDAV "NextCloud" card buttons
This commit is contained in:
2026-04-01 16:20:18 +02:00
parent b8c64937ea
commit 3010cdb0c6
5 changed files with 251 additions and 16 deletions
+91
View File
@@ -1,5 +1,34 @@
import { describe, it, expect } from 'vitest';
import { BACKEND_SCHEMAS } from './index';
import { BACKEND_REGISTRY, BackendType } from './registry';
import { z } from 'zod';
// --- Auto-generation coverage ---
describe('BACKEND_SCHEMAS auto-generation', () => {
it('has an entry for every key in BACKEND_REGISTRY (no undefined)', () => {
const registryKeys = Object.keys(BACKEND_REGISTRY) as BackendType[];
for (const key of registryKeys) {
expect(BACKEND_SCHEMAS[key], `BACKEND_SCHEMAS.${key}`).toBeDefined();
}
});
it('each schema is a ZodObject (not undefined or null)', () => {
const registryKeys = Object.keys(BACKEND_REGISTRY) as BackendType[];
for (const key of registryKeys) {
const schema = BACKEND_SCHEMAS[key];
expect(schema, `BACKEND_SCHEMAS.${key}`).toBeInstanceOf(z.ZodObject);
}
});
it('schema count matches registry count', () => {
const registryCount = Object.keys(BACKEND_REGISTRY).length;
const schemasCount = Object.keys(BACKEND_SCHEMAS).length;
expect(schemasCount).toBe(registryCount);
});
});
// --- Existing schema behaviour tests (regression) ---
describe('Azure Blob Zod schema', () => {
it('accepts valid account + access key', () => {
@@ -70,3 +99,65 @@ describe('S3-compatible Zod schema', () => {
expect(result.success).toBe(false);
});
});
// --- New backend schema smoke tests ---
describe('FTP Zod schema (new)', () => {
it('accepts valid FTP credentials (host only — all others optional)', () => {
const result = BACKEND_SCHEMAS.ftp.safeParse({ host: 'ftp.example.com' });
expect(result.success).toBe(true);
});
it('rejects missing host (required)', () => {
const result = BACKEND_SCHEMAS.ftp.safeParse({ user: 'anon' });
expect(result.success).toBe(false);
});
});
describe('SMB Zod schema (new)', () => {
it('accepts valid SMB credentials', () => {
const result = BACKEND_SCHEMAS.smb.safeParse({
host: 'fileserver.example.com',
user: 'admin',
});
expect(result.success).toBe(true);
});
it('rejects missing host (required)', () => {
const result = BACKEND_SCHEMAS.smb.safeParse({ user: 'admin' });
expect(result.success).toBe(false);
});
});
describe('Seafile Zod schema (new)', () => {
it('accepts valid Seafile credentials', () => {
const result = BACKEND_SCHEMAS.seafile.safeParse({
url: 'https://cloud.seafile.com',
user: 'user@example.com',
pass: 'mypassword',
});
expect(result.success).toBe(true);
});
it('rejects missing url (required)', () => {
const result = BACKEND_SCHEMAS.seafile.safeParse({
user: 'user@example.com',
pass: 'mypassword',
});
expect(result.success).toBe(false);
});
});
describe('Dropbox Zod schema (new)', () => {
it('accepts valid dropbox token', () => {
const result = BACKEND_SCHEMAS.dropbox.safeParse({
token: '{"access_token":"TOKEN","token_type":"Bearer"}',
});
expect(result.success).toBe(true);
});
it('rejects missing token (required)', () => {
const result = BACKEND_SCHEMAS.dropbox.safeParse({});
expect(result.success).toBe(false);
});
});
+6 -10
View File
@@ -2,6 +2,7 @@
// Zod schemas derived programmatically from the Backend Schema Registry.
// DO NOT hand-write z.object() calls with hardcoded field names — all shapes come from the registry.
// Adding a field to BACKEND_REGISTRY automatically adds it to validation.
// BACKEND_SCHEMAS is auto-generated from registry keys — no manual per-backend calls needed.
import { z } from 'zod';
import { BACKEND_REGISTRY, BackendType } from './registry';
@@ -27,13 +28,8 @@ function buildZodSchema(backendType: BackendType): z.ZodObject<Record<string, z.
return z.object(shape);
}
export const BACKEND_SCHEMAS = {
azureblob: buildZodSchema('azureblob'),
s3: buildZodSchema('s3'),
's3-compatible': buildZodSchema('s3-compatible'),
onedrive: buildZodSchema('onedrive'),
sftp: buildZodSchema('sftp'),
gcs: buildZodSchema('gcs'),
b2: buildZodSchema('b2'),
} as const;
// Auto-generated from registry keys — expands automatically when BACKEND_REGISTRY grows.
// No manual buildZodSchema() call needed per new backend.
export const BACKEND_SCHEMAS = Object.fromEntries(
(Object.keys(BACKEND_REGISTRY) as BackendType[]).map(t => [t, buildZodSchema(t)])
) as Record<BackendType, ReturnType<typeof buildZodSchema>>;