@mu-cabin/coding-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ # coding-test
2
+
3
+ A curated playbook for the **测试管理 (15)** category — the 29 test-management actions.
4
+
5
+ ## 1. The entities and how they connect
6
+
7
+ **`ProjectName` (the project *slug*, a string) is the universal required key on every one of the 29
8
+ actions.** There is **no `ProjectId`** anywhere in this category — every other identifier is numeric
9
+ (mostly `uint64`, a few `int64`, and some are integer *arrays* like `Cases`/`RunIds`).
10
+
11
+ | Entity (中文) | What it is | Its id param | Linked by |
12
+ |---|---|---|---|
13
+ | **测试计划 / TestRun** | a *plan/run* — a container of cases to execute | `RunId` | built from `Cases[]` (case ids) or `IncludeAll`; optionally tied to a git release (`GitReleaseId`) |
14
+ | **测试用例 / TestCase** | a reusable case *definition* (the "what to test") | `CaseId` | lives in a 分组 `SectionId`; linkable to a requirement `IssueId` |
15
+ | **测试任务 / Test** | an *instance* of a case inside a plan (the execution row) | `TestId` | has `CaseId` (source case) under a `RunId`; **this is what you record results on** |
16
+ | **分组 / Section** | a folder tree for cases | `SectionId` | self-nests via `ParentId`; optional `CaseDepotId` (用例库) |
17
+ | **测试报告 / Report** | aggregates one or more plans | `ReportId` | built from `RunIds[]` |
18
+ | **缺陷 / Defect** | an issue/defect | `DefectId` | attached to a `TestId` |
19
+
20
+ **Lifecycle:** define a `Case` → add it to a `Run` → it becomes a `Test` instance → record a result on
21
+ that `TestId` → aggregate `Runs` into a `Report`.
22
+
23
+ > ⚠️ **Every list/detail payload nests under `Data`** (e.g. `Data.Runs[]`, `Data.Case`). Nothing in
24
+ > this category returns a top-level array — match your `--select` accordingly.
25
+
26
+ ## 2. Test plans (测试计划 / TestRun)
27
+
28
+ ```bash
29
+ # list plans (filters are mutually exclusive: pick at most one of IterationId / IterationStatus /
30
+ # GitReleaseState / SectionId). State: 0 未开始 · 1 进行中 · 2 已测完.
31
+ coding call DescribeTestRunList --param ProjectName=demo --param Keyword=regression \
32
+ --select 'Data.Runs[].Id,Data.Runs[].Name,Data.Runs[].State,Data.Runs[].PassedCount,Data.Runs[].FailedCount,Data.Runs[].UntestedCount' --json
33
+
34
+ coding call DescribeTestRun --param ProjectName=demo --param RunId=88 --json # detail → Data.Run
35
+
36
+ # create a plan. IncludeAll=true takes every case; otherwise Cases is an Array of case ids (required).
37
+ coding call CreateTestRun --data '{"ProjectName":"demo","Name":"Release 2.0 regression",
38
+ "IncludeAll":false,"Cases":[1001,1002,1003],"ExecuteType":1}' --json # ExecuteType 1 手动 · 2 自动化流水线
39
+ # → Data.Run.Id is the new RunId
40
+
41
+ coding call ModifyTestRun --data '{"ProjectName":"demo","RunId":88,"Name":"Release 2.0 (rev b)"}' --json
42
+ ```
43
+
44
+ ## 3. Test cases (测试用例) & sections (分组)
45
+
46
+ ```bash
47
+ # sections form a tree (ParentId nests them). CaseDepotId scopes to a 用例库 if you have multiple.
48
+ coding call DescribeTestCaseSectionList --param ProjectName=demo --select 'Data.Sections[].Id,Data.Sections[].Name,Data.Sections[].ParentId' --json
49
+ coding call CreateTestCaseSection --data '{"ProjectName":"demo","Name":"Login flows","ParentId":0}' --json # → Data.Section
50
+
51
+ # list / read cases (paginated list carries Data.{Page,PageSize,Total}; detail adds Attachments[])
52
+ coding call DescribeTestCaseList --param ProjectName=demo --param SectionId=12 --param PageSize=50 \
53
+ --select 'Data.Cases[].Id,Data.Cases[].Title,Data.Cases[].Priority,Data.Cases[].TemplateType' --json
54
+ coding call DescribeTestCase --param ProjectName=demo --param CaseId=1001 --json # → Data.Case
55
+
56
+ # create a STEPS case: CustomSteps is an Array of {Content,Expected}. For a TEXT case use Steps/Expected strings.
57
+ coding call CreateTestCase --data '{"ProjectName":"demo","SectionId":12,"TemplateType":"STEPS","Title":"Login with valid creds",
58
+ "Priority":1,"CustomSteps":[{"Content":"Open /login","Expected":"Form shown"},{"Content":"Submit valid creds","Expected":"Dashboard"}]}' --json
59
+
60
+ # cases linked to a requirement (需求): IssueId is the requirement's id
61
+ coding call DescribeRequirementTestCaseList --param ProjectName=demo --param IssueId=42 --select 'Data.Cases[].Id,Data.Cases[].Title' --json
62
+ ```
63
+
64
+ > ⚠️ **Test-case `Priority` is the *inverse* of issue priority.** Here `0`=紧急 · `1`=高 · `2`=中 (default)
65
+ > · `3`=低 — opposite to the issue/`CreateIssue` scale in SKILL.md (`0`低…`3`紧急). Don't carry
66
+ > the issue mapping over. `TemplateType` ∈ `STEPS` (步骤用例) | `TEXT` (文本用例).
67
+
68
+ ## 4. Recording results — three different keys, read carefully
69
+
70
+ The result endpoints look similar but key off **different** ids. Getting this wrong is the #1 trap.
71
+ Result `Status` ∈ `UNTESTED` | `PASSED` | `BLOCKED` | `RETEST` | `FAILED`.
72
+
73
+ ```bash
74
+ # (a) by Test-task instance — the normal path. TestId comes from DescribeTestList (its `Id`).
75
+ coding call DescribeTestList --param ProjectName=demo --param RunId=88 --select 'Data.Tests[].Id,Data.Tests[].CaseId,Data.Tests[].Title,Data.Tests[].Status' --json
76
+ coding call CreateTestResult --data '{"ProjectName":"demo","TestId":5001,"Status":"PASSED"}' --json
77
+
78
+ # (b) step-level result on a STEPS test task (StepIndex starts at 1; StepStatus ∈ PASSED|FAILED only)
79
+ coding call CreateTestStepResult --data '{"ProjectName":"demo","TestId":5001,"StepIndex":1,"StepStatus":"PASSED","Actual":"Dashboard loaded"}' --json
80
+
81
+ # (c) CreateCaseResult — ⚠️ its `CaseId` param is actually the 测试任务 ID (a TestId), NOT a test-case
82
+ # id, and it also needs RunId. Despite the name, it behaves like (a) plus the plan context.
83
+ coding call CreateCaseResult --data '{"ProjectName":"demo","RunId":88,"CaseId":5001,"Status":"FAILED"}' --json
84
+
85
+ # (d) bulk status update across a plan — here CaseIds[] ARE real test-case ids, plus RunId.
86
+ coding call CreateTestResults --data '{"ProjectName":"demo","RunId":88,"CaseIds":[1001,1002],"Status":"PASSED"}' --json
87
+ ```
88
+
89
+ | Action | Keys off | `CaseId`/`CaseIds` means |
90
+ |---|---|---|
91
+ | `CreateTestResult` | `TestId` | — |
92
+ | `CreateTestStepResult` | `TestId` + `StepIndex` | — |
93
+ | `CreateCaseResult` | `RunId` + `CaseId` | **`CaseId` = a TestId** (测试任务 id) |
94
+ | `CreateTestResults` (bulk) | `RunId` + `CaseIds[]` | real test-**case** ids |
95
+
96
+ ## 5. Defects & reports
97
+
98
+ ```bash
99
+ # attach an existing defect (DefectId) to a test task (TestId), then list a task's defects
100
+ coding call CreateTestDefect --data '{"ProjectName":"demo","TestId":5001,"DefectId":7788}' --json
101
+ coding call DescribeTestDefectList --param ProjectName=demo --param TestId=5001 --select 'Data.Defects[].Id,Data.Defects[].Name,Data.Defects[].StatusName,Data.Defects[].AssignedTo' --json
102
+
103
+ # reports aggregate plans: RunIds is an Array of plan ids (required). Status ∈ CREATING|AVAILABLE|UNAVAILABLE.
104
+ coding call DescribeReportList --param ProjectName=demo --select 'Data.Reports[].Id,Data.Reports[].Name,Data.Reports[].Status' --json
105
+ coding call CreateReport --data '{"ProjectName":"demo","Name":"Release 2.0 report","RunIds":[88,89]}' --json # → Data.Report
106
+ coding call DescribeReport --param ProjectName=demo --param ReportId=301 --json # full detail incl. Data.Report.ReportOverview stats
107
+ ```
108
+
109
+ ## 6. Attachments (for cases & reports)
110
+
111
+ `AttachmentIds[]` on `CreateTestCase`/`ModifyTestCase`/`CreateReport` reference pre-uploaded files. The
112
+ upload is a two-step out-of-band flow:
113
+
114
+ ```bash
115
+ coding call CreateAttachmentPrepareSignUrl --param ProjectName=demo --param FileName=evidence.png --json
116
+ # → Data.AttachmentPrepare.{AttachmentId, PrepareSignUrl}. PUT the file bytes to PrepareSignUrl yourself
117
+ # (the CLI won't upload for you), then pass the returned AttachmentId in AttachmentIds:[…].
118
+ ```
119
+
120
+ ## 7. Destructive actions (exit-10 gated)
121
+
122
+ Five actions need `--yes` (preview with `--dry-run`, get explicit user approval, re-run the **same argv
123
+ with `--yes`** — see SKILL.md's exit-10 protocol):
124
+
125
+ - **`DeleteTestRun`** (`ProjectName`,`RunId`) — delete a plan.
126
+ - **`ArchiveTestRun`** (`ProjectName`,`RunId`) — archive/finalize a plan; gated even though it returns the
127
+ Run object, because the gate keys off the `Archive` verb.
128
+ - **`DeleteTestCase`** (`ProjectName`,`CaseId`), **`DeleteTestCaseSection`** (`ProjectName`,`SectionId`),
129
+ **`DeleteReport`** (`ProjectName`,`ReportId`).
130
+
131
+ ## 8. Gotchas
132
+
133
+ - **`ProjectName` (slug), never `ProjectId`** — this whole category keys off the project *name*.
134
+ - **The three "Test/Case/Run" ids are distinct.** `RunId` = a plan, `CaseId` = a case definition,
135
+ `TestId` = an execution instance inside a plan. `DescribeTestList.Data.Tests[].Id` is the `TestId`;
136
+ its `CaseId` field points back to the source case.
137
+ - **`CreateCaseResult.CaseId` is a TestId, not a case id** (§4). The bulk `CreateTestResults.CaseIds[]`
138
+ *are* real case ids. Don't swap them.
139
+ - **`Priority` is inverted vs issues** (§3): `0`紧急 … `3`低 here.
140
+ - **Array params**: `Cases`/`CaseIds`/`RunIds`/`AttachmentIds`/`CustomStepStatus` are all arrays even
141
+ when sending one element; `CustomSteps` is an array of `{Content,Expected}` objects.
142
+ - **Everything wraps under `Data`** — `Data.Runs[]`, `Data.Cases[]`, `Data.Sections[]`, `Data.Tests[]`,
143
+ `Data.Defects[]`, `Data.Reports[]`; singletons at `Data.Run`/`Data.Case`/`Data.Report`.
@@ -0,0 +1,118 @@
1
+ # coding-wiki
2
+
3
+ A curated playbook for the **Wiki (13)** category — the 12 wiki actions.
4
+
5
+ ## 1. Identifiers
6
+
7
+ - **`ProjectName`** (the project *slug*, a string) is required on **every** action — there is **no
8
+ `ProjectId`** in this category.
9
+ - **`Iid`** (int64, "wiki 编号") is the **per-project page handle** — what every *page-scoped* action
10
+ takes (`DescribeWiki`, `ModifyWiki`, `ModifyWikiTitle`, `ModifyWikiOrder`, `DeleteWiki`). The
11
+ exceptions are `DescribeWikiList` (`ProjectName` only) and the job-status reads (`ProjectName` +
12
+ `JobId`). Responses also carry a global **`Id`**; **never feed `Id` back as `Iid`.**
13
+ - **`ParentIid`** (int64) places a page in the tree; **`0` = top level / root**.
14
+
15
+ > ⚠️ **Response wrapping is uneven.** `DescribeWikiList` → `Data` is an **array** (the page tree);
16
+ > `DescribeWiki` / job-status → `Data` is an **object**; but `CreateUploadToken` returns top-level
17
+ > **`Token`** and the ZIP imports return top-level **`JobId`** — neither is under `Data`. Match
18
+ > `--select` to the right wrapper per call.
19
+
20
+ ## 2. Browse & read
21
+
22
+ ```bash
23
+ # the page tree (whole thing; no pagination). Tree linkage: ParentIid (0=root), ordering: Order (a float).
24
+ coding call DescribeWikiList --param ProjectName=demo \
25
+ --select 'Data[].Iid,Data[].Title,Data[].ParentIid,Data[].Order,Data[].Children[].Iid,Data[].Children[].Title' --json
26
+
27
+ # read one page — ⚠️ VersionId is REQUIRED with no "latest" sentinel, yet DescribeWikiList does NOT
28
+ # expose a version. In practice you need a known version number — from the wiki UI, or a prior
29
+ # DescribeWiki response's CurrentVersion/LastVersion. Content = raw source; Html = rendered.
30
+ coding call DescribeWiki --param ProjectName=demo --param Iid=12 --param VersionId=5 \
31
+ --select 'Data.Title,Data.Content,Data.CurrentVersion,Data.LastVersion' --json
32
+ ```
33
+
34
+ ## 3. Create & edit
35
+
36
+ ```bash
37
+ # create — Title, Content, AND ParentIid are all required (use ParentIid:0 for a root page). Msg = commit note.
38
+ coding call CreateWiki --data '{"ProjectName":"demo","Title":"Onboarding","Content":"# Onboarding\n…","ParentIid":0,"Msg":"initial"}' --json
39
+ # → Data.Iid / Data.Id of the new page
40
+
41
+ # full edit — ⚠️ ModifyWiki requires BOTH Title and Content (you can't update body alone). Identified by Iid.
42
+ coding call ModifyWiki --data '{"ProjectName":"demo","Iid":12,"Title":"Onboarding","Content":"# Onboarding (v2)\n…","Msg":"revise"}' --json
43
+
44
+ # rename only — use this instead of ModifyWiki when you don't want to resupply Content
45
+ coding call ModifyWikiTitle --data '{"ProjectName":"demo","Iid":12,"Title":"Onboarding Guide"}' --json
46
+ ```
47
+
48
+ Content is passed inline as the `Content` string (markdown-style source rendered server-side; the read
49
+ response gives you both `Content` and rendered `Html`). There is no markdown-vs-HTML selector param.
50
+
51
+ ## 4. Move & reorder
52
+
53
+ ```bash
54
+ # "Wiki 父级修改" — reparent and/or reorder a page. Iid = the page to move; ParentIid = its new parent
55
+ # (0 = root). Position among the new siblings via Before/After (sibling anchors, both optional ints) —
56
+ # ⚠️ there is NO numeric Order input here despite the action name. Forced (bool,
57
+ # default false) = 是否检查权限 (whether to enforce the permission check).
58
+ coding call ModifyWikiOrder --data '{"ProjectName":"demo","Iid":12,"ParentIid":3,"After":7}' --json
59
+ ```
60
+
61
+ ## 5. ZIP import / update (async, multi-step)
62
+
63
+ A 4-step pipeline. The values that thread through it: `CreateUploadToken` yields **`AuthToken` + `Time`**
64
+ (carried into the ByZip call) and COS upload credentials; the upload yields a **`Key`** (the stored
65
+ object name); the ByZip call yields a **`JobId`** you then poll.
66
+
67
+ ```bash
68
+ # 1. get an upload token + COS credentials for the file
69
+ coding call CreateUploadToken --param ProjectName=demo --param FileName=wiki-export.zip \
70
+ --select 'Token.UploadLink,Token.UpToken,Token.AuthToken,Token.Time,Token.SecretId,Token.SecretKey,Token.Provider' --json
71
+
72
+ # 2. upload the zip to Token.UploadLink (Tencent COS) using UpToken/SecretId/SecretKey — OUT OF BAND
73
+ # (the CLI does not upload bytes). The upload produces the stored object name `Key` (a uuid .zip).
74
+
75
+ # 3a. import as NEW pages under ParentIid — carries FileName, Key, AuthToken, Time from above
76
+ coding call CreateWikiByZip --data '{"ProjectName":"demo","FileName":"wiki-export.zip","Key":"<uuid>.zip",
77
+ "AuthToken":"<AuthToken>","Time":<Time>,"ParentIid":0}' --json # → top-level JobId
78
+
79
+ # 3b. OR update an EXISTING page from the zip — same body but `Iid` (the page) instead of `ParentIid`
80
+ coding call ModifyWikiByZip --data '{"ProjectName":"demo","FileName":"wiki-export.zip","Key":"<uuid>.zip",
81
+ "AuthToken":"<AuthToken>","Time":<Time>,"Iid":12}' --json # → top-level JobId
82
+
83
+ # 4. poll the matching status action (NOT interchangeable): Import↔Create, Update↔Modify.
84
+ coding call DescribeImportJobStatus --param ProjectName=demo --param JobId=<JobId> --select 'Data.JobId,Data.Status' --json
85
+ coding call DescribeUpdateJobStatus --param ProjectName=demo --param JobId=<JobId> --select 'Data.JobId,Data.Status' --json
86
+ ```
87
+
88
+ - **`UpToken` vs `AuthToken` are different tokens** — `UpToken` (with `SecretId`/`SecretKey`) authorizes
89
+ the COS upload in step 2; only **`AuthToken` + `Time`** feed the CODING ByZip call in step 3.
90
+ - **Create→`ParentIid`, Modify→`Iid`** is the one param difference between the two ByZip calls, and they
91
+ have **separate** status pollers (`DescribeImportJobStatus` for create, `DescribeUpdateJobStatus` for
92
+ modify). `Status` is a string enum (e.g. `WAIT_PROCESS` while pending) — poll until it leaves the
93
+ pending state.
94
+
95
+ ## 6. Destructive actions (exit-10 gated)
96
+
97
+ One action needs `--yes`:
98
+
99
+ - **`DeleteWiki`** (`ProjectName`, `Iid`) — **moves the page to the recycle bin (reversible)**, not a
100
+ hard delete. Still gated: preview with `--dry-run`, get explicit approval, re-run the **same argv with
101
+ `--yes`** (see SKILL.md's exit-10 protocol).
102
+
103
+ ## 7. Gotchas
104
+
105
+ - **`ProjectName` (slug), never `ProjectId`** for this whole category.
106
+ - **`Iid` is the input handle; `Id` is a different (global) id in responses** — never round-trip `Id`
107
+ as `Iid`. `ParentIid:0` = root, and `ParentIid` is **required** on `CreateWiki` and `CreateWikiByZip`.
108
+ - **`DescribeWiki` requires `VersionId`** with no "latest" sentinel — and `DescribeWikiList` does **not**
109
+ return a version, so you can't bootstrap one from the tree. You need a version number from elsewhere
110
+ (the UI, or a prior read's `CurrentVersion`/`LastVersion`); surface this to the user if you can't
111
+ determine one.
112
+ - **`ModifyWiki` requires both `Title` and `Content`** — for a rename use `ModifyWikiTitle`.
113
+ - **`ModifyWikiOrder` positions via `Before`/`After` sibling anchors, not a numeric order**; `ParentIid`
114
+ is required; `Forced` (default false) toggles the 是否检查权限 permission check.
115
+ - **Response wrapping is uneven**: list → `Data[]` (array), single read / job status → `Data` (object),
116
+ ByZip → top-level `JobId`. Field types also drift (`Order` is a float in the list but an int in the
117
+ single read; timestamps are epoch-ms in the list but datetime strings in the single read) — don't
118
+ assume one shape across calls.