初始提交:QuickTeam 工单接口导入工具
用「WCF + 通用存储过程网关」接口替代 Selenium(D://Learning//Python//工单自动化)导入工单: 单 Sprint 12 个项目 175s 跑完(旧方案 25~35 分钟),失败可断点续跑且不重复建单。 - 分层:transport(HTTP/协议)/ api(存储过程语义)/ domain(纯规则)/ workflow(阶段编排)/ store(SQLite 台账)/ report / CLI - 幂等:台账驱动断点续跑;dry-run 与跳过行不污染进度;--rollback-on-fail 清理半成品 - 命令:plan / apply / report / status,真实写入需 --yes - tools:只读探针、存储过程源码导出(sp_helptext)、导入诊断 - docs:调研与设计文档(含联调踩到的 5 个坑)+ 6 个关键存储过程源码 实测:Sprint 2026 Sprint 10 的 12 个项目全部完成建单→导入→确认→推送 TFS, 工单 225134–225145,35 个功能模块,重跑 plan 全部 skip-pushed。69 个单测通过。
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"""台账测试:dry-run 隔离、跨 run 续跑、字段合并。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from qtimport.store import Store
|
||||
|
||||
|
||||
class StoreTest(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.folder = tempfile.TemporaryDirectory()
|
||||
self.store = Store(os.path.join(self.folder.name, "t.db"))
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.store.close()
|
||||
self.folder.cleanup()
|
||||
|
||||
def _job(self, run_id: str, stage: str, worksheet_id: int | None = 1) -> None:
|
||||
self.store.upsert_job(
|
||||
run_id,
|
||||
project_id="p1",
|
||||
project_name="P",
|
||||
system_name="2026 Sprint 10",
|
||||
source_file="f.xlsx",
|
||||
action="create",
|
||||
stage=stage,
|
||||
worksheet_id=worksheet_id,
|
||||
)
|
||||
|
||||
def test_latest_stage_across_runs(self) -> None:
|
||||
self.store.start_run("r1", "s", "d", dry_run=False)
|
||||
self._job("r1", "WS_CREATED")
|
||||
self.store.start_run("r2", "s", "d", dry_run=False)
|
||||
self._job("r2", "WS_FILLED")
|
||||
self.assertEqual(self.store.latest_stage("p1", "2026 Sprint 10"), "WS_FILLED")
|
||||
|
||||
def test_dry_run_does_not_pollute_ledger(self) -> None:
|
||||
self.store.start_run("real", "s", "d", dry_run=False)
|
||||
self._job("real", "WS_FILLED")
|
||||
self.store.start_run("dry", "s", "d", dry_run=True)
|
||||
self._job("dry", "TFS_PUSHED")
|
||||
self.assertEqual(
|
||||
self.store.latest_stage("p1", "2026 Sprint 10"),
|
||||
"WS_FILLED",
|
||||
"dry-run 不能把项目标成已推送",
|
||||
)
|
||||
|
||||
def test_worksheet_id_survives_partial_update(self) -> None:
|
||||
self.store.start_run("r1", "s", "d", dry_run=False)
|
||||
self._job("r1", "WS_CREATED", worksheet_id=777)
|
||||
self._job("r1", "WS_FILLED", worksheet_id=None)
|
||||
job = self.store.latest_job("p1", "2026 Sprint 10")
|
||||
self.assertEqual(job["worksheet_id"], 777)
|
||||
self.assertEqual(job["stage"], "WS_FILLED")
|
||||
|
||||
def test_skipped_row_does_not_mask_progress(self) -> None:
|
||||
"""跳过行不带进度:不能把「已推送」覆盖成「SKIPPED」,否则下次会重推 TFS。"""
|
||||
self.store.start_run("r1", "s", "d", dry_run=False)
|
||||
self._job("r1", "TFS_PUSHED")
|
||||
self.store.start_run("r2", "s", "d", dry_run=False)
|
||||
self._job("r2", "SKIPPED")
|
||||
job = self.store.latest_job("p1", "2026 Sprint 10")
|
||||
self.assertEqual(job["stage"], "TFS_PUSHED")
|
||||
|
||||
def test_events_ordered(self) -> None:
|
||||
self.store.start_run("r1", "s", "d", dry_run=False)
|
||||
self.store.add_event("r1", stage="A", action="ok", detail="1")
|
||||
self.store.add_event("r1", stage="B", action="ok", detail="2")
|
||||
self.assertEqual([e["stage"] for e in self.store.events("r1")], ["A", "B"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user