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,8 +1,8 @@
|
||||
// src/generators/rclone-conf.test.ts
|
||||
// Tests for buildRcloneConf — rclone INI config generator.
|
||||
// RED: imports will fail until Plan 02-02 creates rclone-conf.ts.
|
||||
|
||||
import { buildRcloneConf } from './rclone-conf';
|
||||
import { buildRcloneConf, RCLONE_TYPE_MAP } from './rclone-conf';
|
||||
import { BACKEND_REGISTRY, BackendType } from '../schemas/registry';
|
||||
import { INITIAL_STATE } from '../store/types';
|
||||
import type { WizardState } from '../store/types';
|
||||
|
||||
@@ -273,3 +273,137 @@ describe('buildRcloneConf — b2', () => {
|
||||
expect(buildRcloneConf(b2State)).toContain('key = APP_KEY_SECRET');
|
||||
});
|
||||
});
|
||||
|
||||
// --- RCLONE_TYPE_MAP coverage ---
|
||||
|
||||
describe('RCLONE_TYPE_MAP', () => {
|
||||
it('has an entry for every BackendType in BACKEND_REGISTRY', () => {
|
||||
const registryKeys = Object.keys(BACKEND_REGISTRY) as BackendType[];
|
||||
for (const key of registryKeys) {
|
||||
expect(RCLONE_TYPE_MAP[key], `RCLONE_TYPE_MAP["${key}"]`).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('maps gdrive to drive (not gdrive)', () => {
|
||||
expect(RCLONE_TYPE_MAP['gdrive']).toBe('drive');
|
||||
});
|
||||
|
||||
it('maps azure-files to azurefiles (no hyphen)', () => {
|
||||
expect(RCLONE_TYPE_MAP['azure-files']).toBe('azurefiles');
|
||||
});
|
||||
|
||||
it('maps s3-compatible to s3 (provider=Other differentiates)', () => {
|
||||
expect(RCLONE_TYPE_MAP['s3-compatible']).toBe('s3');
|
||||
});
|
||||
|
||||
it('maps gcs to google cloud storage (with spaces)', () => {
|
||||
expect(RCLONE_TYPE_MAP['gcs']).toBe('google cloud storage');
|
||||
});
|
||||
});
|
||||
|
||||
// --- New backend rclone.conf output tests ---
|
||||
|
||||
const gdriveState: WizardState = {
|
||||
...INITIAL_STATE,
|
||||
remote: {
|
||||
name: 'my-gdrive',
|
||||
backendType: 'gdrive',
|
||||
params: {
|
||||
token: '{"access_token":"GTOKEN","token_type":"Bearer"}',
|
||||
root_folder_id: '',
|
||||
service_account_credentials: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ftpState: WizardState = {
|
||||
...INITIAL_STATE,
|
||||
remote: {
|
||||
name: 'my-ftp',
|
||||
backendType: 'ftp',
|
||||
params: {
|
||||
host: 'ftp.example.com',
|
||||
user: 'ftpuser',
|
||||
pass: 'ftppass',
|
||||
port: '',
|
||||
explicit_tls: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const smbState: WizardState = {
|
||||
...INITIAL_STATE,
|
||||
remote: {
|
||||
name: 'my-smb',
|
||||
backendType: 'smb',
|
||||
params: {
|
||||
host: 'fileserver.local',
|
||||
user: 'domain\\admin',
|
||||
pass: 'secret',
|
||||
domain: 'CORP',
|
||||
port: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('buildRcloneConf — gdrive (new)', () => {
|
||||
it('contains type = drive (not type = gdrive)', () => {
|
||||
const output = buildRcloneConf(gdriveState);
|
||||
expect(output).toContain('type = drive');
|
||||
expect(output).not.toContain('type = gdrive');
|
||||
});
|
||||
it('contains [my-gdrive] section header', () => {
|
||||
expect(buildRcloneConf(gdriveState)).toContain('[my-gdrive]');
|
||||
});
|
||||
it('contains token value', () => {
|
||||
expect(buildRcloneConf(gdriveState)).toContain('token = ');
|
||||
});
|
||||
it('omits empty root_folder_id', () => {
|
||||
expect(buildRcloneConf(gdriveState)).not.toContain('root_folder_id');
|
||||
});
|
||||
it('omits empty service_account_credentials', () => {
|
||||
expect(buildRcloneConf(gdriveState)).not.toContain('service_account_credentials');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildRcloneConf — ftp (new)', () => {
|
||||
it('contains type = ftp', () => {
|
||||
expect(buildRcloneConf(ftpState)).toContain('type = ftp');
|
||||
});
|
||||
it('contains host', () => {
|
||||
expect(buildRcloneConf(ftpState)).toContain('host = ftp.example.com');
|
||||
});
|
||||
it('contains user', () => {
|
||||
expect(buildRcloneConf(ftpState)).toContain('user = ftpuser');
|
||||
});
|
||||
it('contains pass', () => {
|
||||
expect(buildRcloneConf(ftpState)).toContain('pass = ftppass');
|
||||
});
|
||||
it('omits empty port', () => {
|
||||
expect(buildRcloneConf(ftpState)).not.toContain('port =');
|
||||
});
|
||||
it('omits empty explicit_tls', () => {
|
||||
expect(buildRcloneConf(ftpState)).not.toContain('explicit_tls');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildRcloneConf — smb (new)', () => {
|
||||
it('contains type = smb', () => {
|
||||
expect(buildRcloneConf(smbState)).toContain('type = smb');
|
||||
});
|
||||
it('contains host', () => {
|
||||
expect(buildRcloneConf(smbState)).toContain('host = fileserver.local');
|
||||
});
|
||||
it('contains user', () => {
|
||||
expect(buildRcloneConf(smbState)).toContain('user = domain\\admin');
|
||||
});
|
||||
it('contains pass', () => {
|
||||
expect(buildRcloneConf(smbState)).toContain('pass = secret');
|
||||
});
|
||||
it('contains domain', () => {
|
||||
expect(buildRcloneConf(smbState)).toContain('domain = CORP');
|
||||
});
|
||||
it('omits empty port', () => {
|
||||
expect(buildRcloneConf(smbState)).not.toContain('port =');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,14 +7,27 @@ import type { WizardState } from '../store/types';
|
||||
// Maps our BackendType to rclone's internal type string.
|
||||
// IMPORTANT: s3-compatible uses 'type = s3' — rclone does not recognize 's3-compatible' as a type.
|
||||
// S3-compatible backends are differentiated by provider = Other in params.
|
||||
const RCLONE_TYPE_MAP: Record<string, string> = {
|
||||
// IMPORTANT: gdrive uses 'type = drive' — rclone's internal name for Google Drive.
|
||||
// IMPORTANT: gcs uses 'type = google cloud storage' (with spaces) — rclone's internal name.
|
||||
export const RCLONE_TYPE_MAP: Record<string, string> = {
|
||||
azureblob: 'azureblob',
|
||||
s3: 's3',
|
||||
's3-compatible': 's3',
|
||||
onedrive: 'onedrive',
|
||||
sftp: 'sftp',
|
||||
gcs: 'google cloud storage',
|
||||
b2: 'b2',
|
||||
'azure-files': 'azurefiles',
|
||||
swift: 'swift',
|
||||
onedrive: 'onedrive',
|
||||
gdrive: 'drive',
|
||||
dropbox: 'dropbox',
|
||||
box: 'box',
|
||||
pcloud: 'pcloud',
|
||||
sftp: 'sftp',
|
||||
ftp: 'ftp',
|
||||
webdav: 'webdav',
|
||||
smb: 'smb',
|
||||
http: 'http',
|
||||
seafile: 'seafile',
|
||||
};
|
||||
|
||||
export function buildRcloneConf(state: WizardState): string {
|
||||
|
||||
Reference in New Issue
Block a user