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:
@@ -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
@@ -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>>;
|
||||
|
||||
Reference in New Issue
Block a user