Quellcode durchsuchen

fix(bin): datax-sync-template-gen -o 三态 stdout 始终输出

不传 / 不带值 / 带值三态都先 stdout 打 md + ini,传 -o 时再额外落盘 + stderr 提示路径。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tianyu.chu vor 1 Woche
Ursprung
Commit
743a23d4dc
2 geänderte Dateien mit 81 neuen und 11 gelöschten Zeilen
  1. 11 11
      bin/datax-sync-template-gen.py
  2. 70 0
      tests/unit/datax/test_sync_template_gen.py

+ 11 - 11
bin/datax-sync-template-gen.py

@@ -22,10 +22,10 @@ CLI:
   -mask-conf  mask 配置 ini 路径({table}.mask.ini,可选)。传入时按配置
               剔除 trim 字段 + 渲染 [mask] 段,md 脱敏类型列填好;不传时
               全字段输出,md 脱敏类型列空白。**文件不存在直接报错**。
-  -o          输出目录(可选):
-              - 不传:stdout 同时打印 md 表 + ini 模板
-              - 传 -o 不带值:workspace/{yyyymmdd}/,写 {table}.md + .ini
-              - 传 -o <DIR>:自定义目录,写两个文件
+  -o          输出目录(可选;任意三态下 stdout 都同时打印 md + ini):
+              - 不传:stdout
+              - 传 -o 不带值:stdout + 落盘 workspace/{yyyymmdd}/{table}.{md,ini}
+              - 传 -o <DIR>:stdout + 落盘 <DIR>/{table}.{md,ini}
 """
 import argparse
 import os
@@ -214,7 +214,7 @@ def main():
     parser.add_argument('-t', required=True, metavar='SCHEMA.TABLE',
                         help='schema 限定的表名(如 public.card_group_order_info)')
     parser.add_argument('-o', nargs='?', const=WORKSPACE_DEFAULT, default=None, metavar='DIR',
-                        help='输出目录(不传 stdout 同时打印 md 表 + ini 模板;不带值 workspace/{yyyymmdd}/ 写两文件;带值自定义目录写两文件)')
+                        help='输出目录(任意三态 stdout 始终打印 md + ini;不传仅 stdout;不带值额外落盘 workspace/{yyyymmdd}/;带值额外落盘 <DIR>/)')
     parser.add_argument('-mask-conf', default=None, metavar='PATH', dest='mask_conf',
                         help='mask 配置 ini 路径({table}.mask.ini)。传入时按配置剔除 trim 字段 + 渲染 [mask] 段,md 脱敏类型列填好;不传时全字段输出,md 脱敏类型列空白')
     args = parser.parse_args()
@@ -269,12 +269,12 @@ def main():
     md_content = render_schema_md(full_rows, mask_dict)
     ini_content = render_template(args.ds, database, schema, table, columns, pk, non_trim_mask)
 
-    if args.o is None:
-        # stdout:先 md 表后 ini 模板
-        sys.stdout.write(md_content)
-        sys.stdout.write('\n')
-        sys.stdout.write(ini_content)
-    else:
+    # stdout 始终打印(先 md 表后 ini 模板),传 -o 时再额外落盘
+    sys.stdout.write(md_content)
+    sys.stdout.write('\n')
+    sys.stdout.write(ini_content)
+
+    if args.o is not None:
         os.makedirs(args.o, exist_ok=True)
         md_path = os.path.join(args.o, table + '.md')
         ini_path = os.path.join(args.o, table + '.ini')

+ 70 - 0
tests/unit/datax/test_sync_template_gen.py

@@ -171,3 +171,73 @@ def test_render_template_empty_pk():
         table='t', columns=[('a', '')], pk='',
     )
     assert 'splitPk = \n' in out
+
+
+def _patch_main_dependencies(monkeypatch):
+    """共享 mock:让 main() 不连真 PG / 真 datasource。"""
+    fake_ds = MagicMock()
+    fake_ds.parse.return_value = {
+        GEN.DS_POSTGRE_SQL_JDBC_URL: 'jdbc:postgresql://10.0.0.1:5432/mydb',
+        'username': 'u',
+        'password': 'p',
+    }
+    monkeypatch.setattr(GEN, 'resolve_datasource', lambda ref: fake_ds)
+
+    fake_conn = MagicMock()
+    fake_cur = fake_conn.cursor.return_value
+    fake_cur.fetchall.return_value = [
+        (1, 'id', 'id', 'bigint', 'PK'),
+        (2, 'name', '名称', 'character varying', ''),
+    ]
+    fake_pg8000 = MagicMock()
+    fake_pg8000.dbapi.connect.return_value = fake_conn
+    monkeypatch.setitem(sys.modules, 'pg8000', fake_pg8000)
+    monkeypatch.setitem(sys.modules, 'pg8000.dbapi', fake_pg8000.dbapi)
+
+
+def test_main_stdout_only_when_no_o(monkeypatch, capsys):
+    _patch_main_dependencies(monkeypatch)
+    monkeypatch.setattr(sys, 'argv', [
+        'datax-sync-template-gen.py',
+        '-ds', 'postgresql/prod-hobby',
+        '-t', 'public.users',
+    ])
+    GEN.main()
+    captured = capsys.readouterr()
+    assert '| 序号 | 字段名 |' in captured.out
+    assert '[reader]' in captured.out
+    assert '已写入' not in captured.err
+
+
+def test_main_stdout_and_disk_when_o_with_dir(monkeypatch, capsys, tmp_path):
+    _patch_main_dependencies(monkeypatch)
+    out_dir = tmp_path / 'out'
+    monkeypatch.setattr(sys, 'argv', [
+        'datax-sync-template-gen.py',
+        '-ds', 'postgresql/prod-hobby',
+        '-t', 'public.users',
+        '-o', str(out_dir),
+    ])
+    GEN.main()
+    captured = capsys.readouterr()
+    assert '| 序号 | 字段名 |' in captured.out
+    assert '[reader]' in captured.out
+    assert '已写入' in captured.err
+    assert (out_dir / 'users.md').exists()
+    assert (out_dir / 'users.ini').exists()
+
+
+def test_main_stdout_and_disk_when_o_no_value(monkeypatch, capsys, tmp_path):
+    _patch_main_dependencies(monkeypatch)
+    monkeypatch.setattr(GEN, 'WORKSPACE_DEFAULT', str(tmp_path / 'workspace'))
+    monkeypatch.setattr(sys, 'argv', [
+        'datax-sync-template-gen.py',
+        '-ds', 'postgresql/prod-hobby',
+        '-t', 'public.users',
+        '-o',
+    ])
+    GEN.main()
+    captured = capsys.readouterr()
+    assert '| 序号 | 字段名 |' in captured.out
+    assert '[reader]' in captured.out
+    assert '已写入' in captured.err