import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtemp, mkdir, readFile, writeFile, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { normalizeConfig } from '../lib/core.mjs'; import { parseArgs, prepareInput, persistGeneratedConfig } from '../migrate.mjs'; test('zero arguments default to read-only plan, with optional profile or explicit actions', () => { const args = parseArgs([]); assert.equal(args.command, 'plan'); assert.ok(args.configPath.endsWith('config.local.json')); assert.equal(parseArgs(['--profile', 'dev']).command, 'plan'); assert.equal(parseArgs(['init']).command, 'init'); assert.equal(parseArgs(['apply', '--limit', '50']).limit, 50); assert.equal(parseArgs(['apply', '--run-id', 'new-batch']).runId, 'new-batch'); assert.equal(parseArgs(['--help']).help, true); assert.throws(() => parseArgs(['plan', '--limit', '50'])); assert.throws(() => parseArgs(['delete'])); }); test('automatic configuration inherits project credentials without exporting a password variable', async () => { const root = await mkdtemp(join(tmpdir(), 'oss-runtime-')); try { const resources = join(root, 'yudao-server/src/main/resources'); await mkdir(resources, { recursive: true }); await writeFile(join(resources, 'application.yaml'), 'spring:\n profiles:\n active: dev\n'); await writeFile(join(resources, 'application-dev.yaml'), `server:\n port: 48083\nspring:\n datasource:\n dynamic:\n primary: master\n datasource:\n master:\n url: jdbc:mysql://127.0.0.1:3306/fixture_db\n username: fixture_user\n password: fixture-secret\n`); const args = { command: 'init', configPath: join(root, 'config.local.json') }; const prepared = await prepareInput(args, root); assert.equal(prepared.projectManaged, true); assert.equal(prepared.exists, false); assert.equal(prepared.input.mysql.database, 'fixture_db'); assert.equal(prepared.input.mysql.password, 'fixture-secret'); assert.equal(prepared.input.sourceMode, undefined); // defaults must not overwrite a resumed run assert.equal(prepared.input._projectDownloadBaseUrl, 'http://127.0.0.1:48083/admin-api/infra/file'); const config = normalizeConfig({ ...prepared.input, sourceMode: 'database', sourceConfigIds: ['5'], targetConfigId: '30', runId: 'auto-test', _databaseIdentity: { server: 'test-uuid', schema: 'fixture_db' } }); await persistGeneratedConfig(config, args, prepared); const text = await readFile(args.configPath, 'utf8'); assert.ok(!text.includes('fixture-secret')); assert.ok(!text.includes('"mysql"')); const saved = JSON.parse(text); assert.equal(saved.runId, 'auto-test'); assert.equal(saved.projectProfile, 'dev'); assert.equal(saved.targetConfigId, '30'); const reloaded = await prepareInput(args, root); assert.equal(reloaded.input.mysql.password, 'fixture-secret'); assert.equal(reloaded.input.runId, 'auto-test'); } finally { await rm(root, { recursive: true, force: true }); } }); test('manual connection remains supported and is never overwritten with project settings', async () => { const root = await mkdtemp(join(tmpdir(), 'oss-manual-')); try { const path = join(root, 'manual.json'); const raw = { mysql: { host: 'manual.example', database: 'manual_db', user: 'manual', password: 'manual-secret' }, runId: 'manual-run', sourceConfigIds: ['5'], targetConfigId: '30', sourceMode: 'database' }; await writeFile(path, JSON.stringify(raw)); const args = { command: 'init', configPath: path, explicitConfig: true }; const prepared = await prepareInput(args, root); assert.equal(prepared.projectManaged, false); assert.equal(prepared.input.mysql.host, 'manual.example'); await persistGeneratedConfig(normalizeConfig(prepared.input), args, prepared); assert.deepEqual(JSON.parse(await readFile(path, 'utf8')), raw); await assert.rejects(prepareInput({ ...args, profile: 'prod' }, root), /不能同时使用/); } finally { await rm(root, { recursive: true, force: true }); } });