ShiErFeng/shierfeng-fastapi-backend/tests/cli/root/test_contract_job_gen.py
2026-07-24 09:43:10 +08:00

369 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import subprocess
from collections.abc import Callable
from cli.exit_codes import ARGUMENT_ERROR, DATABASE_ERROR, GUARD_REJECTED, RUNTIME_ERROR, SUCCESS
GEN_TEST_TABLE_NAME = 'demo_table'
GEN_CREATE_SQL = 'CREATE TABLE demo_cli_test (id bigint);'
MISSING_JOB_ID = 999999999
MISSING_GEN_TABLE_ID = 999999999
def test_job_detail_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
list_completed = run_cli_command('job', 'list', '--env=dev', '--output=json')
list_payload = parse_json_stdout(list_completed)
job_id = MISSING_JOB_ID
if list_payload.get('ok') and isinstance(list_payload.get('items'), list) and list_payload['items']:
first_job = list_payload['items'][0]
if isinstance(first_job, dict) and isinstance(first_job.get('jobId'), int):
job_id = first_job['jobId']
completed = run_cli_command('job', 'detail', str(job_id), '--env=dev', '--output=json')
payload = parse_json_stdout(completed)
assert completed.returncode in {SUCCESS, DATABASE_ERROR, RUNTIME_ERROR}
assert isinstance(payload['ok'], bool)
if 'error' in payload:
assert isinstance(payload['message'], str)
assert isinstance(payload['error'], str)
return
if payload['ok'] is False:
assert set(payload) == {'ok', 'message', 'jobId'}
assert payload['jobId'] == job_id
return
assert set(payload) == {'ok', 'job'}
assert isinstance(payload['job'], dict)
assert payload['job']['jobId'] == job_id
assert isinstance(payload['job'].get('jobName'), str)
assert isinstance(payload['job'].get('invokeTarget'), str)
def test_job_logs_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command('job', 'logs', '--env=dev', '--output=json')
payload = parse_json_stdout(completed)
assert completed.returncode in {SUCCESS, DATABASE_ERROR}
assert isinstance(payload['ok'], bool)
if 'error' in payload:
assert isinstance(payload['message'], str)
assert isinstance(payload['error'], str)
return
assert set(payload) == {'ok', 'filters', 'count', 'items'}
assert isinstance(payload['filters'], dict)
assert isinstance(payload['count'], int)
assert isinstance(payload['items'], list)
if payload['items']:
first_item = payload['items'][0]
assert isinstance(first_item, dict)
assert isinstance(first_item.get('jobLogId'), int)
assert isinstance(first_item.get('jobName'), str | None)
assert isinstance(first_item.get('jobMessage'), str | None)
def test_gen_list_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command('gen', 'list', '--env=dev', '--output=json')
payload = parse_json_stdout(completed)
assert completed.returncode in {SUCCESS, DATABASE_ERROR}
assert payload['env'] == 'dev'
assert isinstance(payload['ok'], bool)
if 'error' in payload:
assert isinstance(payload['message'], str)
assert isinstance(payload['error'], str)
return
assert set(payload) == {'ok', 'filters', 'count', 'items', 'env'}
assert isinstance(payload['filters'], dict)
assert isinstance(payload['count'], int)
assert isinstance(payload['items'], list)
if payload['items']:
first_item = payload['items'][0]
assert isinstance(first_item, dict)
assert isinstance(first_item.get('tableId'), int)
assert isinstance(first_item.get('tableName'), str)
def test_gen_db_list_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command('gen', 'db-list', '--env=dev', '--output=json')
payload = parse_json_stdout(completed)
assert completed.returncode in {SUCCESS, DATABASE_ERROR}
assert payload['env'] == 'dev'
assert isinstance(payload['ok'], bool)
if 'error' in payload:
assert isinstance(payload['message'], str)
assert isinstance(payload['error'], str)
return
assert set(payload) == {'ok', 'filters', 'count', 'items', 'env'}
assert isinstance(payload['filters'], dict)
assert isinstance(payload['count'], int)
assert isinstance(payload['items'], list)
if payload['items']:
first_item = payload['items'][0]
assert isinstance(first_item, dict)
assert isinstance(first_item.get('tableName'), str)
def test_gen_detail_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
list_completed = run_cli_command('gen', 'list', '--env=dev', '--output=json')
list_payload = parse_json_stdout(list_completed)
table_id = MISSING_GEN_TABLE_ID
if list_payload.get('ok') and isinstance(list_payload.get('items'), list) and list_payload['items']:
first_item = list_payload['items'][0]
if isinstance(first_item, dict) and isinstance(first_item.get('tableId'), int):
table_id = first_item['tableId']
completed = run_cli_command('gen', 'detail', str(table_id), '--env=dev', '--output=json')
payload = parse_json_stdout(completed)
assert completed.returncode in {SUCCESS, DATABASE_ERROR, RUNTIME_ERROR}
assert payload['env'] == 'dev'
assert isinstance(payload['ok'], bool)
if 'error' in payload:
assert isinstance(payload['message'], str)
assert isinstance(payload['error'], str)
return
if payload['ok'] is False:
assert set(payload) == {'ok', 'message', 'tableId', 'env'}
assert payload['tableId'] == table_id
return
assert set(payload) == {'ok', 'tableId', 'tableName', 'columnCount', 'tableCount', 'detail', 'env'}
assert payload['tableId'] == table_id
assert isinstance(payload['tableName'], str)
assert isinstance(payload['columnCount'], int)
assert isinstance(payload['tableCount'], int)
assert isinstance(payload['detail'], dict)
assert isinstance(payload['detail'].get('info'), dict)
assert isinstance(payload['detail'].get('rows'), list)
assert isinstance(payload['detail'].get('tables'), list)
def test_gen_sync_db_is_rejected_without_yes_in_non_interactive_mode(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command('gen', 'sync-db', 'demo_table', '--env=dev', '--output=json')
payload = parse_json_stdout(completed)
assert completed.returncode == GUARD_REJECTED
assert payload['ok'] is False
assert payload['message'] == '已取消危险命令执行gen sync-db'
assert '--yes' in payload['hint']
def test_gen_export_dry_run_text_output_has_stable_structure(
run_text_cli_command: Callable[..., subprocess.CompletedProcess[str]],
) -> None:
completed = run_text_cli_command(
'gen',
'export',
GEN_TEST_TABLE_NAME,
'--env=dev',
'--dry-run',
'--yes',
'--output=text',
)
assert completed.returncode == SUCCESS
assert completed.stdout.startswith('OK SUCCESS\n')
assert 'env: dev\n' in completed.stdout
assert 'mode: zip\n' in completed.stdout
assert 'dry_run: true\n' in completed.stdout
assert 'message: 代码导出演练完成,未执行实际导出\n' in completed.stdout
assert 'table_names:\n' in completed.stdout
assert f' - {GEN_TEST_TABLE_NAME}\n' in completed.stdout
assert 'output_file:' in completed.stdout
assert f'gen_code_{GEN_TEST_TABLE_NAME}.zip' in completed.stdout
def test_gen_export_dry_run_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command(
'gen',
'export',
GEN_TEST_TABLE_NAME,
'--env=dev',
'--dry-run',
'--yes',
'--output=json',
)
payload = parse_json_stdout(completed)
assert completed.returncode == SUCCESS
assert payload['ok'] is True
assert payload['env'] == 'dev'
assert payload['dryRun'] is True
assert payload['mode'] == 'zip'
assert payload['message'] == '代码导出演练完成,未执行实际导出'
assert payload['tableNames'] == [GEN_TEST_TABLE_NAME]
assert isinstance(payload['outputFile'], str)
assert payload['outputFile'].endswith(f'gen_code_{GEN_TEST_TABLE_NAME}.zip')
def test_gen_create_table_dry_run_text_output_has_stable_structure(
run_text_cli_command: Callable[..., subprocess.CompletedProcess[str]],
) -> None:
completed = run_text_cli_command(
'gen',
'create-table',
'--env=dev',
'--dry-run',
'--yes',
'--sql',
GEN_CREATE_SQL,
'--output=text',
)
assert completed.returncode == SUCCESS
assert completed.stdout.startswith('OK SUCCESS\n')
assert 'env: dev\n' in completed.stdout
assert 'message: 建表语句演练完成,未执行实际建表\n' in completed.stdout
assert 'dry_run: true\n' in completed.stdout
assert 'statement_count: 1\n' in completed.stdout
assert 'table_names:\n' in completed.stdout
assert ' - demo_cli_test\n' in completed.stdout
assert f'sql: {GEN_CREATE_SQL}\n' in completed.stdout
def test_gen_create_table_dry_run_json_output_has_stable_contract(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command(
'gen',
'create-table',
'--env=dev',
'--dry-run',
'--yes',
'--sql',
GEN_CREATE_SQL,
'--output=json',
)
payload = parse_json_stdout(completed)
assert completed.returncode == SUCCESS
assert payload['ok'] is True
assert payload['env'] == 'dev'
assert payload['dryRun'] is True
assert payload['message'] == '建表语句演练完成,未执行实际建表'
assert payload['statementCount'] == 1
assert payload['tableNames'] == ['demo_cli_test']
assert payload['sql'] == GEN_CREATE_SQL
def test_gen_create_table_rejects_non_create_sql_in_text_output(
run_text_cli_command: Callable[..., subprocess.CompletedProcess[str]],
) -> None:
completed = run_text_cli_command(
'gen',
'create-table',
'--env=dev',
'--dry-run',
'--yes',
'--sql',
'DROP TABLE demo_cli_test;',
'--output=text',
)
assert completed.returncode == ARGUMENT_ERROR
assert completed.stdout == (
'FAIL FAILED\nmessage: 创建表结构失败\nerror: 建表语句不合法,仅允许 CREATE TABLE 语句\nenv: dev\n'
)
def test_gen_create_table_rejects_non_create_sql_in_json_output(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command(
'gen',
'create-table',
'--env=dev',
'--dry-run',
'--yes',
'--sql',
'DROP TABLE demo_cli_test;',
'--output=json',
)
payload = parse_json_stdout(completed)
assert completed.returncode == ARGUMENT_ERROR
assert payload == {
'ok': False,
'message': '创建表结构失败',
'error': '建表语句不合法,仅允许 CREATE TABLE 语句',
'env': 'dev',
}
def test_gen_create_table_rejects_conflicting_sql_inputs_in_text_output(
run_text_cli_command: Callable[..., subprocess.CompletedProcess[str]],
) -> None:
completed = run_text_cli_command(
'gen',
'create-table',
'--env=dev',
'--dry-run',
'--yes',
'--sql',
GEN_CREATE_SQL,
'--sql-file',
'fake.sql',
'--output=text',
)
assert completed.returncode == ARGUMENT_ERROR
assert completed.stdout == (
'FAIL FAILED\nmessage: 创建表结构失败\nerror: 必须且只能传入 --sql 或 --sql-file 其中一种方式\nenv: dev\n'
)
def test_gen_create_table_rejects_conflicting_sql_inputs_in_json_output(
run_cli_command: Callable[..., subprocess.CompletedProcess[str]],
parse_json_stdout: Callable[[subprocess.CompletedProcess[str]], dict],
) -> None:
completed = run_cli_command(
'gen',
'create-table',
'--env=dev',
'--dry-run',
'--yes',
'--sql',
GEN_CREATE_SQL,
'--sql-file',
'fake.sql',
'--output=json',
)
payload = parse_json_stdout(completed)
assert completed.returncode == ARGUMENT_ERROR
assert payload == {
'ok': False,
'message': '创建表结构失败',
'error': '必须且只能传入 --sql 或 --sql-file 其中一种方式',
'env': 'dev',
}