在前后端开发联调前 的提交20260223

This commit is contained in:
Neo
2026-02-23 23:02:20 +08:00
parent 254ccb1e77
commit fafc95e64c
1142 changed files with 10366960 additions and 36957 deletions

View File

@@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
"""
Feature: dataflow-field-completion, Property 3: TABLE_MAP 覆盖完整性
**Validates: Requirements 7.2, 8.2**
对于任意在 TABLE_MAP 中注册的 DWD 表,该表的所有非 SCD2 列要么在 FACT_MAPPINGS
中有显式映射,要么在对应 ODS 表中存在同名列(自动映射)。
本测试聚焦以下可静态验证的属性:
1. TABLE_MAP 所有条目的 ODS 源表非空
2. C 类表在 TABLE_MAP 中注册
3. C 类表在 FACT_MAPPINGS 中有条目
4. C 类表映射字段数量与期望一致
5. TABLE_MAP 与 FACT_MAPPINGS 交叉一致性
6. hypothesis 属性测试:随机 TABLE_MAP 条目结构验证
"""
from __future__ import annotations
import sys
from pathlib import Path
from hypothesis import given, settings, HealthCheck
import hypothesis.strategies as st
# ── 将 ETL 模块加入 sys.path ──
_ETL_ROOT = Path(__file__).resolve().parent.parent / "apps" / "etl" / "connectors" / "feiqiu"
if str(_ETL_ROOT) not in sys.path:
sys.path.insert(0, str(_ETL_ROOT))
from tasks.dwd.dwd_load_task import DwdLoadTask
# ── C 类表定义 ──
C_CLASS_TABLES = [
"dwd.dwd_goods_stock_summary",
"dwd.dwd_goods_stock_movement",
]
# ── C 类表期望映射字段 ──
# goods_stock_summary → dwd_goods_stock_summary14 个字段)
_GOODS_STOCK_SUMMARY_EXPECTED_COLS = {
"site_goods_id", "goods_name", "goods_unit", "goods_category_id",
"goods_category_second_id", "category_name", "range_start_stock",
"range_end_stock", "range_in", "range_out", "range_sale",
"range_sale_money", "range_inventory", "current_stock",
}
# goods_stock_movements → dwd_goods_stock_movement19 个字段)
_GOODS_STOCK_MOVEMENT_EXPECTED_COLS = {
"site_goods_stock_id", "tenant_id", "site_id", "site_goods_id",
"goods_name", "goods_category_id", "goods_second_category_id",
"unit", "price", "stock_type", "change_num", "start_num", "end_num",
"change_num_a", "start_num_a", "end_num_a", "remark", "operator_name",
"create_time",
}
# ── 收集所有 TABLE_MAP 条目 ──
_ALL_TABLE_MAP_ENTRIES = list(DwdLoadTask.TABLE_MAP.items())
# ══════════════════════════════════════════════════════════════════
# Property 3.1: TABLE_MAP 所有条目的 ODS 源表非空
# ══════════════════════════════════════════════════════════════════
def test_all_table_map_entries_have_ods_source():
"""TABLE_MAP 中每个 DWD 表都有非空的 ODS 源表名。"""
for dwd_table, ods_table in DwdLoadTask.TABLE_MAP.items():
assert ods_table and isinstance(ods_table, str) and ods_table.strip(), \
f"{dwd_table}: TABLE_MAP 中的 ODS 源表为空或无效"
# ══════════════════════════════════════════════════════════════════
# Property 3.2: C 类表在 TABLE_MAP 中注册
# ══════════════════════════════════════════════════════════════════
def test_c_class_tables_registered_in_table_map():
"""
**Validates: Requirements 7.2, 8.2**
C 类表dwd_goods_stock_summary、dwd_goods_stock_movement
必须在 TABLE_MAP 中注册。
"""
for table in C_CLASS_TABLES:
assert table in DwdLoadTask.TABLE_MAP, \
f"C 类表 {table} 未在 TABLE_MAP 中注册"
ods_table = DwdLoadTask.TABLE_MAP[table]
assert ods_table and isinstance(ods_table, str), \
f"C 类表 {table}: TABLE_MAP 中的 ODS 源表无效"
# ══════════════════════════════════════════════════════════════════
# Property 3.3: C 类表在 FACT_MAPPINGS 中有条目
# ══════════════════════════════════════════════════════════════════
def test_c_class_tables_have_fact_mappings():
"""
**Validates: Requirements 7.2, 8.2**
C 类表必须在 FACT_MAPPINGS 中有至少一个映射条目。
"""
for table in C_CLASS_TABLES:
entries = DwdLoadTask.FACT_MAPPINGS.get(table, [])
assert len(entries) > 0, \
f"C 类表 {table} 在 FACT_MAPPINGS 中无条目"
# ══════════════════════════════════════════════════════════════════
# Property 3.4: goods_stock_summary 14 个字段全覆盖
# ══════════════════════════════════════════════════════════════════
def test_goods_stock_summary_mapping_coverage():
"""
**Validates: Requirements 7.2**
dwd.dwd_goods_stock_summary 的 FACT_MAPPINGS 应覆盖全部 14 个期望字段。
"""
table = "dwd.dwd_goods_stock_summary"
entries = DwdLoadTask.FACT_MAPPINGS.get(table, [])
actual_cols = {e[0].lower() for e in entries}
# 验证数量
assert len(entries) == 14, \
f"{table}: 期望 14 个映射条目,实际 {len(entries)}"
# 验证字段覆盖
missing = _GOODS_STOCK_SUMMARY_EXPECTED_COLS - actual_cols
assert not missing, \
f"{table}: 缺少映射字段 {missing}"
extra = actual_cols - _GOODS_STOCK_SUMMARY_EXPECTED_COLS
assert not extra, \
f"{table}: 存在多余映射字段 {extra}"
# ══════════════════════════════════════════════════════════════════
# Property 3.5: goods_stock_movement 19 个字段全覆盖
# ══════════════════════════════════════════════════════════════════
def test_goods_stock_movement_mapping_coverage():
"""
**Validates: Requirements 8.2**
dwd.dwd_goods_stock_movement 的 FACT_MAPPINGS 应覆盖全部 19 个期望字段。
"""
table = "dwd.dwd_goods_stock_movement"
entries = DwdLoadTask.FACT_MAPPINGS.get(table, [])
actual_cols = {e[0].lower() for e in entries}
# 验证数量
assert len(entries) == 19, \
f"{table}: 期望 19 个映射条目,实际 {len(entries)}"
# 验证字段覆盖
missing = _GOODS_STOCK_MOVEMENT_EXPECTED_COLS - actual_cols
assert not missing, \
f"{table}: 缺少映射字段 {missing}"
extra = actual_cols - _GOODS_STOCK_MOVEMENT_EXPECTED_COLS
assert not extra, \
f"{table}: 存在多余映射字段 {extra}"
# ══════════════════════════════════════════════════════════════════
# Property 3.6: FACT_MAPPINGS 是 TABLE_MAP 的子集
# ══════════════════════════════════════════════════════════════════
def test_fact_mappings_subset_of_table_map():
"""FACT_MAPPINGS 中的所有 DWD 表都必须在 TABLE_MAP 中注册。"""
for table in DwdLoadTask.FACT_MAPPINGS:
assert table in DwdLoadTask.TABLE_MAP, \
f"FACT_MAPPINGS 中的 {table} 未在 TABLE_MAP 中注册"
# ══════════════════════════════════════════════════════════════════
# Hypothesis 属性测试:随机 TABLE_MAP 条目结构验证
# ══════════════════════════════════════════════════════════════════
_table_map_entry_strategy = st.sampled_from(_ALL_TABLE_MAP_ENTRIES)
@given(entry=_table_map_entry_strategy)
@settings(max_examples=200, suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_random_table_map_entry_valid(entry):
"""
**Validates: Requirements 7.2, 8.2**
对于任意随机选取的 TABLE_MAP 条目,验证:
- DWD 表名为 "dwd." 前缀的非空字符串
- ODS 源表名为 "ods." 前缀的非空字符串
- 如果该表在 FACT_MAPPINGS 中有条目,每个条目都是合法的三元组
"""
dwd_table, ods_table = entry
# DWD 表名格式验证
assert isinstance(dwd_table, str) and dwd_table.startswith("dwd."), \
f"TABLE_MAP key {dwd_table!r} 不以 'dwd.' 开头"
# ODS 源表名格式验证
assert isinstance(ods_table, str) and ods_table.startswith("ods."), \
f"TABLE_MAP[{dwd_table}] = {ods_table!r} 不以 'ods.' 开头"
# 如果有 FACT_MAPPINGS 条目,验证结构
entries = DwdLoadTask.FACT_MAPPINGS.get(dwd_table, [])
for i, e in enumerate(entries):
assert isinstance(e, (tuple, list)) and len(e) == 3, \
f"{dwd_table}[{i}]: FACT_MAPPINGS 条目应为三元组"
dwd_col, ods_expr, cast_type = e
assert isinstance(dwd_col, str) and dwd_col.strip(), \
f"{dwd_table}[{i}]: dwd_col 为空"
assert isinstance(ods_expr, str) and ods_expr.strip(), \
f"{dwd_table}[{i}]: ods_expr 为空"
assert cast_type is None or isinstance(cast_type, str), \
f"{dwd_table}[{i}].{dwd_col}: cast_type 类型无效"