zephyr-enterprise-tools 1.3.0 → 1.3.1
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.
- package/README.md +32 -1
- package/cli.js +36 -0
- package/mcp-server.js +21 -0
- package/package.json +1 -1
- package/zephyr-enterprise-tools.d.ts +30 -0
- package/zephyr-enterprise-tools.js +117 -0
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Comprehensive tools for Zephyr Enterprise — Release Readiness, Project Health,
|
|
|
11
11
|
| Tool | Description | Thresholds |
|
|
12
12
|
|------|-------------|------------|
|
|
13
13
|
| `release-readiness` | Run all 4 quality gates | Combined assessment |
|
|
14
|
+
| `compare-releases` | Compare readiness across two releases | MCP/programmatic API |
|
|
14
15
|
| `requirement-coverage` | Are requirements covered by tests? | ≥70% = GO |
|
|
15
16
|
| `test-plan` | Are tests planned and assigned? | <80% = NO GO, 80–90% = CONDITIONAL, ≥90% = GO |
|
|
16
17
|
| `test-execution` | Have tests been executed? | <90% = NO GO, 90–97% = CONDITIONAL, ≥97% = GO |
|
|
@@ -28,7 +29,10 @@ Comprehensive tools for Zephyr Enterprise — Release Readiness, Project Health,
|
|
|
28
29
|
| `search-tests` | Search test cases by keyword query |
|
|
29
30
|
| `user-activity` | User activity and productivity metrics |
|
|
30
31
|
| `user-trend` | Full audit log history for a user — every action across the system, filterable by date range, entity type, and operation |
|
|
32
|
+
| `execution-burnup` | Day-by-day execution burnup (cumulative executed vs ideal and total scope), supports optional date range filtering |
|
|
31
33
|
| `execution-burndown` | Day-by-day execution burndown (remaining vs ideal), supports optional date range filtering |
|
|
34
|
+
| `list-cycles` | List all test cycles for a release, including phases and execution status counts |
|
|
35
|
+
| `get-cycle` | Get full details for a single test cycle |
|
|
32
36
|
|
|
33
37
|
---
|
|
34
38
|
|
|
@@ -66,6 +70,7 @@ Use `zephyr-enterprise-tools` as an MCP (Model Context Protocol) server with you
|
|
|
66
70
|
| `list_projects` | _(none)_ | List all Zephyr projects |
|
|
67
71
|
| `list_releases` | `projectId` | List releases for a project |
|
|
68
72
|
| `release_readiness` | `projectId`, `releaseId` | Run all 4 quality gates |
|
|
73
|
+
| `compare_releases` | `projectId`, `releaseId1`, `releaseId2`, `query?` _(ZQL)_ | Compare readiness between two releases in the same project |
|
|
69
74
|
| `requirement_coverage` | `projectId`, `releaseId` | Check requirement coverage |
|
|
70
75
|
| `test_plan_analysis` | `projectId`, `releaseId`, `query?` _(ZQL)_ | Analyze test planning status — supports ZQL filter e.g. `priority = "P1"` |
|
|
71
76
|
| `test_execution` | `projectId`, `releaseId` | Check test execution progress |
|
|
@@ -77,13 +82,18 @@ Use `zephyr-enterprise-tools` as an MCP (Model Context Protocol) server with you
|
|
|
77
82
|
| `search_test_cases` | `projectId`, `releaseId`, `query?`, `limit?` | Search test cases by keyword |
|
|
78
83
|
| `user_activity` | `projectId`, `releaseId`, `days?` | Get user activity metrics |
|
|
79
84
|
| `user_trend` | `userName`*, `fromDate?`, `toDate?`, `entity?`, `operation?`, `pageSize?`, `offset?` | Full audit log history for a user |
|
|
85
|
+
| `execution_burnup` | `projectId`, `releaseId`, `startDate?`, `endDate?` | Day-by-day burnup chart data with cumulative executed, ideal, and scope counts |
|
|
80
86
|
| `execution_burndown` | `projectId`, `releaseId`, `startDate?`, `endDate?` | Day-by-day burndown chart data |
|
|
87
|
+
| `list_cycles` | `releaseId` | List all test cycles for a release, including phases and execution status counts |
|
|
88
|
+
| `get_cycle` | `cycleId` | Get full details for a single test cycle, including phases |
|
|
81
89
|
|
|
82
90
|
> **\* `user_trend` — `userName` must be the user's full email address** (e.g. `jane.doe@yourcompany.com`). Short names or display names will return 0 results. `pageSize` supports up to 1000 records per request.
|
|
83
91
|
|
|
84
92
|
> **`test_plan_analysis` ZQL filter** — Use the `query` parameter to scope results to a specific priority, e.g. `priority = "P1"`. This is the recommended way to filter test plan metrics by priority. Note: `search_test_cases` accepts keyword queries but does not support ZQL priority filtering.
|
|
85
93
|
|
|
86
|
-
>
|
|
94
|
+
> **Execution chart date range** — Use `startDate` and `endDate` (format: `YYYY-MM-DD`) to scope `execution_burnup` and `execution_burndown` to a specific period within the release window.
|
|
95
|
+
|
|
96
|
+
> **Execution status IDs** — Test execution, failed tests, trends, user activity, burnup, burndown, and cycle status counts resolve status IDs from Zephyr v4 system preferences using `testresult.testresultStatus.LOV` from `/admin/preference/all/system`. Built-in IDs are used only as a fallback when the preference cannot be loaded or parsed.
|
|
87
97
|
|
|
88
98
|
---
|
|
89
99
|
|
|
@@ -179,6 +189,15 @@ zephyr-enterprise-tools -p 364 -r 4312 -t test-plan -q 'priority = "P1"'
|
|
|
179
189
|
# Get trends for last 14 days
|
|
180
190
|
zephyr-enterprise-tools -p 364 -r 4312 -t test-trends -d 14
|
|
181
191
|
|
|
192
|
+
# Get execution burnup chart data
|
|
193
|
+
zephyr-enterprise-tools -p 364 -r 4312 -t execution-burnup --start-date 2026-07-22 --end-date 2026-08-27
|
|
194
|
+
|
|
195
|
+
# List all cycles and phase details for a release
|
|
196
|
+
zephyr-enterprise-tools -p 364 -r 4312 -t list-cycles
|
|
197
|
+
|
|
198
|
+
# Get one cycle by ID
|
|
199
|
+
zephyr-enterprise-tools -p 364 -r 4312 -t get-cycle -c 98765
|
|
200
|
+
|
|
182
201
|
# Get user audit log (full email required)
|
|
183
202
|
zephyr-enterprise-tools -t user-trend --user jane.doe@yourcompany.com --page-size 1000
|
|
184
203
|
|
|
@@ -199,6 +218,7 @@ zephyr-enterprise-tools --help
|
|
|
199
218
|
| `-q, --query <text>` | Keyword query (for `search-tests`) or ZQL expression (for `test-plan`) |
|
|
200
219
|
| `-d, --days <n>` | Days for trends/activity (default: 30) |
|
|
201
220
|
| `-l, --limit <n>` | Max results (default: 50) |
|
|
221
|
+
| `-c, --cycle <id>` | Cycle ID for `get-cycle` |
|
|
202
222
|
| `--user <email>` | Full email address for `user-trend` |
|
|
203
223
|
| `--page-size <n>` | Records per page for `user-trend` (max: 1000) |
|
|
204
224
|
| `--start-date <YYYY-MM-DD>` | Start date for `execution-burndown` |
|
|
@@ -230,6 +250,9 @@ const tools = new ZephyrEnterpriseTools({
|
|
|
230
250
|
const report = await tools.runAllGates(364, 4312);
|
|
231
251
|
console.log(report.overallStatus); // "GO" | "CONDITIONAL GO" | "NO GO"
|
|
232
252
|
|
|
253
|
+
const comparison = await tools.compareReleases(364, 4312, 4313, { query: 'priority = "P1"' });
|
|
254
|
+
console.log(comparison.overallStatusChanged); // true when release readiness status changed
|
|
255
|
+
|
|
233
256
|
// Individual gates
|
|
234
257
|
const coverage = await tools.requirementCoverageGate(364, 4312);
|
|
235
258
|
const planning = await tools.testPlanAnalysisGate(364, 4312);
|
|
@@ -247,6 +270,14 @@ const reqCoverage = await tools.getRequirementCoverage(364, 4312);
|
|
|
247
270
|
const trends = await tools.getTestCaseTrends(364, 4312, { days: 14 });
|
|
248
271
|
const results = await tools.searchTestCases(364, 4312, { query: 'login' });
|
|
249
272
|
const activity = await tools.getUserActivity(364, 4312, { days: 30 });
|
|
273
|
+
const cycles = await tools.listCycles(4312);
|
|
274
|
+
const cycle = await tools.getCycle(98765);
|
|
275
|
+
|
|
276
|
+
// Burnup with optional date range
|
|
277
|
+
const burnup = await tools.getExecutionBurnup(364, 4312, {
|
|
278
|
+
startDate: '2026-07-22',
|
|
279
|
+
endDate: '2026-08-27',
|
|
280
|
+
});
|
|
250
281
|
|
|
251
282
|
// User audit log — full email address required; pageSize up to 1000
|
|
252
283
|
const auditLog = await tools.getUserTrend({
|
package/cli.js
CHANGED
|
@@ -43,6 +43,8 @@ function parseArgs() {
|
|
|
43
43
|
days: 30,
|
|
44
44
|
limit: 50,
|
|
45
45
|
cycleId: null,
|
|
46
|
+
startDate: null,
|
|
47
|
+
endDate: null,
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -81,6 +83,12 @@ function parseArgs() {
|
|
|
81
83
|
case '--cycle':
|
|
82
84
|
options.cycleId = Number(args[++i]);
|
|
83
85
|
break;
|
|
86
|
+
case '--start-date':
|
|
87
|
+
options.startDate = args[++i] || null;
|
|
88
|
+
break;
|
|
89
|
+
case '--end-date':
|
|
90
|
+
options.endDate = args[++i] || null;
|
|
91
|
+
break;
|
|
84
92
|
case '--json':
|
|
85
93
|
options.format = 'json';
|
|
86
94
|
break;
|
|
@@ -146,6 +154,8 @@ AVAILABLE TOOLS:
|
|
|
146
154
|
│ test-trends Test execution trends over time │
|
|
147
155
|
│ search-tests Search test cases by query │
|
|
148
156
|
│ user-activity User activity and productivity metrics │
|
|
157
|
+
│ execution-burnup Day-by-day execution burnup chart │
|
|
158
|
+
│ execution-burndown Day-by-day execution burndown chart │
|
|
149
159
|
│ list-cycles List all cycles for a release with phase details │
|
|
150
160
|
│ get-cycle Get full details for one cycle (-c <cycleId>) │
|
|
151
161
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
@@ -173,6 +183,9 @@ EXAMPLES:
|
|
|
173
183
|
# Get user activity report
|
|
174
184
|
zephyr-tools -p 364 -r 4312 -t user-activity
|
|
175
185
|
|
|
186
|
+
# Get execution burnup chart data
|
|
187
|
+
zephyr-tools -p 364 -r 4312 -t execution-burnup --start-date 2026-07-22 --end-date 2026-08-27
|
|
188
|
+
|
|
176
189
|
# List all cycles and phase details for a release
|
|
177
190
|
zephyr-tools -p 364 -r 4312 -t list-cycles
|
|
178
191
|
|
|
@@ -427,6 +440,17 @@ function formatGenericResult(result) {
|
|
|
427
440
|
console.log(' └────────────┴───────┴────────┴────────┴─────────┘');
|
|
428
441
|
}
|
|
429
442
|
|
|
443
|
+
if (result.dailyBurnup && result.dailyBurnup.length > 0) {
|
|
444
|
+
console.log('\n📈 DAILY BURNUP (last 10 days):');
|
|
445
|
+
console.log(' ┌────────────┬────────┬────────────┬───────┬───────┐');
|
|
446
|
+
console.log(' │ Date │ Today │ Cumulative │ Ideal │ Scope │');
|
|
447
|
+
console.log(' ├────────────┼────────┼────────────┼───────┼───────┤');
|
|
448
|
+
for (const day of result.dailyBurnup.slice(-10)) {
|
|
449
|
+
console.log(` │ ${day.date} │ ${String(day.executedToday).padEnd(6)} │ ${String(day.cumulativeExecuted).padEnd(10)} │ ${String(day.ideal).padEnd(5)} │ ${String(day.scope).padEnd(5)} │`);
|
|
450
|
+
}
|
|
451
|
+
console.log(' └────────────┴────────┴────────────┴───────┴───────┘');
|
|
452
|
+
}
|
|
453
|
+
|
|
430
454
|
// Cycles list/detail
|
|
431
455
|
if (result.cycles && result.cycles.length > 0) {
|
|
432
456
|
console.log(`\n🔁 CYCLES (${result.total || result.cycles.length}):`);
|
|
@@ -568,6 +592,18 @@ async function main() {
|
|
|
568
592
|
case 'user-activity':
|
|
569
593
|
result = await tools.getUserActivity(projectId, releaseId, { days });
|
|
570
594
|
break;
|
|
595
|
+
case 'execution-burnup':
|
|
596
|
+
result = await tools.getExecutionBurnup(projectId, releaseId, {
|
|
597
|
+
startDate: options.startDate,
|
|
598
|
+
endDate: options.endDate,
|
|
599
|
+
});
|
|
600
|
+
break;
|
|
601
|
+
case 'execution-burndown':
|
|
602
|
+
result = await tools.getExecutionBurndown(projectId, releaseId, {
|
|
603
|
+
startDate: options.startDate,
|
|
604
|
+
endDate: options.endDate,
|
|
605
|
+
});
|
|
606
|
+
break;
|
|
571
607
|
case 'list-cycles':
|
|
572
608
|
result = await tools.listCycles(releaseId);
|
|
573
609
|
break;
|
package/mcp-server.js
CHANGED
|
@@ -197,6 +197,20 @@ const TOOLS = [
|
|
|
197
197
|
required: ['projectId', 'releaseId'],
|
|
198
198
|
},
|
|
199
199
|
},
|
|
200
|
+
{
|
|
201
|
+
name: 'execution_burnup',
|
|
202
|
+
description: 'Generate a day-by-day execution burnup chart for a release. Returns cumulative executed vs ideal and total scope counts for charting progress over time.',
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {
|
|
206
|
+
projectId: { type: 'number', description: 'Zephyr project ID' },
|
|
207
|
+
releaseId: { type: 'number', description: 'Zephyr release ID' },
|
|
208
|
+
startDate: { type: 'string', description: 'Burnup start date YYYY-MM-DD (default: earliest execution date)' },
|
|
209
|
+
endDate: { type: 'string', description: 'Burnup end date YYYY-MM-DD (default: today)' },
|
|
210
|
+
},
|
|
211
|
+
required: ['projectId', 'releaseId'],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
200
214
|
{
|
|
201
215
|
name: 'user_trend',
|
|
202
216
|
description: 'Get full audit log activity for a user — every action they performed across the system. Filter by date range and optionally by entity type (project or release).',
|
|
@@ -374,6 +388,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
374
388
|
});
|
|
375
389
|
break;
|
|
376
390
|
|
|
391
|
+
case 'execution_burnup':
|
|
392
|
+
result = await tools.getExecutionBurnup(projectId, releaseId, {
|
|
393
|
+
startDate: args.startDate || null,
|
|
394
|
+
endDate: args.endDate || null,
|
|
395
|
+
});
|
|
396
|
+
break;
|
|
397
|
+
|
|
377
398
|
case 'user_trend':
|
|
378
399
|
result = await tools.getUserTrend({
|
|
379
400
|
userName: args.userName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zephyr-enterprise-tools",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Comprehensive Zephyr Enterprise Tools - Release Readiness, Project Health, Test Analytics & More",
|
|
5
5
|
"main": "zephyr-enterprise-tools.js",
|
|
6
6
|
"types": "zephyr-enterprise-tools.d.ts",
|
|
@@ -221,6 +221,34 @@ export interface ListUsersOptions {
|
|
|
221
221
|
pageSize?: number;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
export interface ExecutionChartOptions {
|
|
225
|
+
startDate?: string | null;
|
|
226
|
+
endDate?: string | null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface ExecutionBurnupDay {
|
|
230
|
+
date: string;
|
|
231
|
+
executedToday: number;
|
|
232
|
+
cumulativeExecuted: number;
|
|
233
|
+
remaining: number;
|
|
234
|
+
completionPct: number;
|
|
235
|
+
ideal: number;
|
|
236
|
+
scope: number;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface ExecutionBurnupResult {
|
|
240
|
+
tool: string;
|
|
241
|
+
projectId: number;
|
|
242
|
+
releaseId: number;
|
|
243
|
+
timestamp: string;
|
|
244
|
+
dateRange?: { from: string; to: string };
|
|
245
|
+
total: number;
|
|
246
|
+
totalPlanned: number;
|
|
247
|
+
summary?: Record<string, unknown>;
|
|
248
|
+
message?: string;
|
|
249
|
+
dailyBurnup: ExecutionBurnupDay[];
|
|
250
|
+
}
|
|
251
|
+
|
|
224
252
|
export interface ZephyrUser {
|
|
225
253
|
id: number;
|
|
226
254
|
fullName: string;
|
|
@@ -323,6 +351,8 @@ export declare class QualityGates {
|
|
|
323
351
|
getTestCaseTrends(projectId: number, releaseId: number, options?: TrendsOptions): Promise<TestTrendsResult>;
|
|
324
352
|
searchTestCases(projectId: number, releaseId: number, options?: SearchTestCasesOptions): Promise<SearchTestCasesResult>;
|
|
325
353
|
getUserActivity(projectId: number, releaseId: number, options?: UserActivityOptions): Promise<UserActivityResult>;
|
|
354
|
+
getExecutionBurndown(projectId: number, releaseId: number, options?: ExecutionChartOptions): Promise<Record<string, unknown>>;
|
|
355
|
+
getExecutionBurnup(projectId: number, releaseId: number, options?: ExecutionChartOptions): Promise<ExecutionBurnupResult>;
|
|
326
356
|
listUsers(projectId: number, options?: ListUsersOptions): Promise<ListUsersResult>;
|
|
327
357
|
listCycles(releaseId: number): Promise<ListCyclesResult>;
|
|
328
358
|
getCycle(cycleId: number): Promise<GetCycleResult>;
|
|
@@ -1803,6 +1803,123 @@ export class QualityGates {
|
|
|
1803
1803
|
};
|
|
1804
1804
|
}
|
|
1805
1805
|
|
|
1806
|
+
async getExecutionBurnup(projectId, releaseId, options = {}) {
|
|
1807
|
+
const { startDate = null, endDate = null } = options;
|
|
1808
|
+
const statusMap = await this.getTestResultStatusMap();
|
|
1809
|
+
|
|
1810
|
+
const executionsById = new Map();
|
|
1811
|
+
let currentOffset = 0;
|
|
1812
|
+
const pageSize = 10000;
|
|
1813
|
+
|
|
1814
|
+
while (true) {
|
|
1815
|
+
const executionData = await this.GET('/execution', {
|
|
1816
|
+
releaseid: releaseId,
|
|
1817
|
+
offset: currentOffset,
|
|
1818
|
+
pagesize: pageSize,
|
|
1819
|
+
includeanyoneuser: true,
|
|
1820
|
+
});
|
|
1821
|
+
const page = executionData.results || executionData || [];
|
|
1822
|
+
|
|
1823
|
+
if (!Array.isArray(page) || page.length === 0) break;
|
|
1824
|
+
|
|
1825
|
+
const sizeBefore = executionsById.size;
|
|
1826
|
+
for (const exec of page) executionsById.set(exec.id, exec);
|
|
1827
|
+
currentOffset += page.length;
|
|
1828
|
+
|
|
1829
|
+
if (executionsById.size === sizeBefore) break;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
const executions = [...executionsById.values()];
|
|
1833
|
+
const total = executions.length;
|
|
1834
|
+
const totalPlanned = total;
|
|
1835
|
+
|
|
1836
|
+
if (total === 0) {
|
|
1837
|
+
return {
|
|
1838
|
+
tool: 'Execution Burnup',
|
|
1839
|
+
projectId,
|
|
1840
|
+
releaseId,
|
|
1841
|
+
timestamp: new Date().toISOString(),
|
|
1842
|
+
total: 0,
|
|
1843
|
+
totalPlanned: 0,
|
|
1844
|
+
message: 'No executions found for this release.',
|
|
1845
|
+
dailyBurnup: [],
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
const allDates = executions
|
|
1850
|
+
.map(e => e.lastModifiedOn || e.createDatetime)
|
|
1851
|
+
.filter(Boolean)
|
|
1852
|
+
.map(d => new Date(d).toISOString().split('T')[0]);
|
|
1853
|
+
|
|
1854
|
+
const rangeStart = startDate || allDates.reduce((a, b) => (a < b ? a : b));
|
|
1855
|
+
const rangeEnd = endDate || new Date().toISOString().split('T')[0];
|
|
1856
|
+
|
|
1857
|
+
const executedByDate = {};
|
|
1858
|
+
for (const exec of executions) {
|
|
1859
|
+
const rawDate = exec.lastModifiedOn || exec.createDatetime;
|
|
1860
|
+
if (!rawDate) continue;
|
|
1861
|
+
const dateKey = new Date(rawDate).toISOString().split('T')[0];
|
|
1862
|
+
if (dateKey < rangeStart || dateKey > rangeEnd) continue;
|
|
1863
|
+
if (!this.isExecutionStatus(exec, statusMap, "unexecuted")) {
|
|
1864
|
+
executedByDate[dateKey] = (executedByDate[dateKey] || 0) + 1;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
const days = [];
|
|
1869
|
+
let current = new Date(rangeStart);
|
|
1870
|
+
const end = new Date(rangeEnd);
|
|
1871
|
+
let cumulativeExecuted = 0;
|
|
1872
|
+
|
|
1873
|
+
while (current <= end) {
|
|
1874
|
+
const dateKey = current.toISOString().split('T')[0];
|
|
1875
|
+
const executedToday = executedByDate[dateKey] || 0;
|
|
1876
|
+
cumulativeExecuted += executedToday;
|
|
1877
|
+
|
|
1878
|
+
days.push({
|
|
1879
|
+
date: dateKey,
|
|
1880
|
+
executedToday,
|
|
1881
|
+
cumulativeExecuted,
|
|
1882
|
+
remaining: Math.max(0, total - cumulativeExecuted),
|
|
1883
|
+
completionPct: Math.round((cumulativeExecuted / total) * 100),
|
|
1884
|
+
});
|
|
1885
|
+
|
|
1886
|
+
current.setDate(current.getDate() + 1);
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
const totalDays = days.length;
|
|
1890
|
+
const idealDailyBurnup = totalDays > 1 ? total / (totalDays - 1) : total;
|
|
1891
|
+
const dailyBurnup = days.map((day, i) => ({
|
|
1892
|
+
...day,
|
|
1893
|
+
ideal: Math.min(total, Math.round(idealDailyBurnup * i)),
|
|
1894
|
+
scope: total,
|
|
1895
|
+
}));
|
|
1896
|
+
|
|
1897
|
+
const lastDay = dailyBurnup[dailyBurnup.length - 1];
|
|
1898
|
+
const behindBy = lastDay.ideal - lastDay.cumulativeExecuted;
|
|
1899
|
+
|
|
1900
|
+
return {
|
|
1901
|
+
tool: 'Execution Burnup',
|
|
1902
|
+
projectId,
|
|
1903
|
+
releaseId,
|
|
1904
|
+
timestamp: new Date().toISOString(),
|
|
1905
|
+
dateRange: { from: rangeStart, to: rangeEnd },
|
|
1906
|
+
total,
|
|
1907
|
+
totalPlanned,
|
|
1908
|
+
summary: {
|
|
1909
|
+
totalExecutions: total,
|
|
1910
|
+
executed: lastDay.cumulativeExecuted,
|
|
1911
|
+
remaining: lastDay.remaining,
|
|
1912
|
+
completionPct: lastDay.completionPct,
|
|
1913
|
+
status: behindBy > 0
|
|
1914
|
+
? `⚠️ Behind ideal by ${behindBy} tests`
|
|
1915
|
+
: behindBy < 0
|
|
1916
|
+
? `✅ Ahead of schedule by ${Math.abs(behindBy)} tests`
|
|
1917
|
+
: '✅ On track',
|
|
1918
|
+
},
|
|
1919
|
+
dailyBurnup,
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1806
1923
|
// ─── User Trend (Audit Logs) ─────────────────────────────────────────────────
|
|
1807
1924
|
|
|
1808
1925
|
async getUserTrend(options = {}) {
|