Files
tutti/tools/scripts/desktop-release-summary.test.mjs
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

300 lines
9.4 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import {
buildFallbackReleaseSummary,
classifyCommit,
normalizeSectionTitle,
normalizeVersion,
resolveChannel
} from "../../apps/desktop/scripts/generate-release-summary.mjs";
import {
GITHUB_RELEASE_BODY_MAX_LENGTH,
RELEASE_NOTES_TRUNCATION_NOTICE
} from "../../apps/desktop/scripts/lib/githubReleaseBody.mjs";
import { resolvePreviousReleaseTag } from "../../apps/desktop/scripts/lib/previousReleaseTag.mjs";
import {
REVIEW_END,
REVIEW_START,
SECTION_END,
SECTION_START,
SUGGESTION_START,
buildApprovedReleaseSummary,
buildUpdatedReleaseBody
} from "../../apps/desktop/scripts/upsert-release-summary.mjs";
import { validateReleaseSummary } from "../../apps/desktop/scripts/validate-release-summary.mjs";
function validReleaseSummary(overrides = {}) {
return {
schemaVersion: "tutti.desktop.release.summary.v1",
tag: "v1.2.4-rc.1",
version: "1.2.4-rc.1",
channel: "rc",
prerelease: true,
targetCommit: "0123456789abcdef",
compare: { from: "v1.2.4-rc.0", range: "v1.2.4-rc.0..HEAD", to: "HEAD" },
generatedAt: "2026-07-21T00:00:00.000Z",
summarySource: "fallback",
zh: {
headline: "本次版本优化发布流程。",
sections: [{ title: "发布与更新", items: ["验证候选版本。"] }],
qaFocus: ["验证更新。"]
},
en: {
headline: "This release improves the release flow.",
sections: [
{ title: "Release and Updates", items: ["Validate the candidate."] }
],
qaFocus: ["Verify updates."]
},
...overrides
};
}
test("desktop release summary classifies commit messages for human sections", () => {
assert.equal(
classifyCommit("abc1234 feat(update): add channel picker"),
"功能变更"
);
assert.equal(
classifyCommit("abc1234 fix(release): avoid rc latest"),
"问题修复"
);
assert.equal(
classifyCommit("abc1234 chore(release): update desktop workflow"),
"发布与更新"
);
});
test("desktop release summary maps technical headings to user-facing sections", () => {
assert.equal(normalizeSectionTitle("核心功能与架构", "zh"), "功能变更");
assert.equal(normalizeSectionTitle("后端与服务端改进", "zh"), "体验优化");
assert.equal(
normalizeSectionTitle("Core Features & Architecture", "en"),
"Feature Updates"
);
assert.equal(
normalizeSectionTitle("Backend & Service Enhancements", "en"),
"Experience Improvements"
);
});
test("desktop release summary resolves channel from version shape", () => {
assert.equal(normalizeVersion("v1.2.4"), "1.2.4");
assert.equal(normalizeVersion("tutti-desktop-v1.2.4-rc.1"), "1.2.4-rc.1");
assert.equal(normalizeVersion("tutti-desktop-v1.2.4-beta.1"), "1.2.4-beta.1");
assert.equal(resolveChannel({ version: "1.2.4" }), "stable");
assert.equal(resolveChannel({ version: "1.2.4-rc.1" }), "rc");
assert.equal(resolveChannel({ version: "1.2.4-beta.1" }), "beta");
});
test("GitHub release notes compare prereleases against the latest same-channel tag", () => {
const previousTag = resolvePreviousReleaseTag({
channel: "rc",
tag: "v0.2.2-rc.9",
tags: ["v0.2.2-rc.9", "v0.2.2-rc.8", "v0.2.2-beta.5", "v0.2.0"],
version: "0.2.2-rc.9"
});
assert.equal(previousTag, "v0.2.2-rc.8");
});
test("GitHub release notes fall back to the latest stable tag", () => {
const previousTag = resolvePreviousReleaseTag({
channel: "beta",
tag: "v0.3.0-beta.0",
tags: ["v0.3.0-beta.0", "v0.2.2-rc.8", "v0.2.1", "v0.2.0"],
version: "0.3.0-beta.0"
});
assert.equal(previousTag, "v0.2.1");
});
test("desktop release summary fallback emits zh and en sections", () => {
const summary = buildFallbackReleaseSummary({
commits: [
"abc1234 feat(update): add release channel picker",
"def5678 fix(release): keep rc out of latest metadata"
]
});
assert.equal(summary.source, "fallback");
assert.match(summary.zh.headline, /桌面端/);
assert.ok(summary.zh.sections.length >= 2);
assert.ok(summary.en.sections.length >= 2);
});
test("desktop release summary creates editable review and replaceable suggestion sections", () => {
const nextBody = buildUpdatedReleaseBody({
existingBody: [
"## What's Changed",
"- Raw GitHub note",
"",
SECTION_START,
"old summary",
SECTION_END
].join("\n"),
summary: {
zh: {
headline: "本次版本聚焦发布链路稳定性。",
sections: [
{ title: "发布与更新", items: ["稳定包入口只指向正式版。"] }
],
qaFocus: ["验证下载入口。"]
},
en: {
headline: "This release focuses on release stability.",
sections: [
{
title: "Release and Updates",
items: ["Stable downloads only point to official releases."]
}
],
qaFocus: ["Verify the download entry."]
}
}
});
assert.equal(nextBody.match(new RegExp(REVIEW_START, "g"))?.length, 1);
assert.equal(nextBody.match(new RegExp(SUGGESTION_START, "g"))?.length, 1);
assert.match(nextBody, /本次版本聚焦发布链路稳定性/);
assert.match(nextBody, /Stable downloads only point to official releases/);
assert.match(nextBody, /Release Notes/);
assert.doesNotMatch(nextBody, /QA Focus/);
assert.doesNotMatch(nextBody, /Verify the download entry/);
assert.match(nextBody, /Raw GitHub note/);
assert.doesNotMatch(nextBody, /old summary/);
});
test("desktop release summary preserves human review when a candidate is rebuilt", () => {
const humanReview = [
REVIEW_START,
"## Release Notes",
"### 中文",
"人工确认标题",
"#### 问题修复",
"- 人工确认内容",
"### English",
"Reviewed headline",
"#### Bug Fixes",
"- Reviewed item",
REVIEW_END
].join("\n");
const nextBody = buildUpdatedReleaseBody({
existingBody: `${humanReview}\n\n## What's Changed\n- keep me`,
summary: validReleaseSummary({
zh: {
headline: "新的机器建议",
sections: [{ title: "功能变更", items: ["机器内容"] }],
qaFocus: []
}
})
});
assert.match(nextBody, /人工确认内容/);
assert.match(nextBody, /新的机器建议/);
assert.match(nextBody, /keep me/);
});
test("approved summary is parsed from the human review instead of generated suggestions", () => {
const body = buildUpdatedReleaseBody({
existingBody: "",
summary: validReleaseSummary()
}).replace("验证候选版本。", "人工筛选后的更新内容");
const approved = buildApprovedReleaseSummary({
body,
generatedSummary: validReleaseSummary(),
reviewedAt: "2026-08-17T01:00:00.000Z"
});
assert.equal(approved.summarySource, "human-reviewed");
assert.deepEqual(approved.zh.sections[0].items, ["人工筛选后的更新内容"]);
assert.equal(approved.generatedAt, "2026-08-17T01:00:00.000Z");
});
test("approved summary accepts reviewed items written as plain Markdown lines", () => {
const body = [
REVIEW_START,
"## Release Notes",
"### 中文",
"人工确认标题",
"#### 功能变更",
"默认开启快捷提示词管理",
"支持会话分叉",
"### English",
"Reviewed headline",
"#### Feature Changes",
"Quick prompt management is enabled by default",
"Conversation forking is supported",
REVIEW_END
].join("\n");
const approved = buildApprovedReleaseSummary({
body,
generatedSummary: validReleaseSummary()
});
assert.deepEqual(approved.zh.sections[0].items, [
"默认开启快捷提示词管理",
"支持会话分叉"
]);
assert.deepEqual(approved.en.sections[0].items, [
"Quick prompt management is enabled by default",
"Conversation forking is supported"
]);
});
test("desktop release summary trims only generated notes at GitHub's body limit", () => {
const oversizedNotes = [
"## What's Changed",
...Array.from(
{ length: 2_000 },
(_, index) => `- generated note ${index}: ${"x".repeat(80)}`
)
].join("\n");
const nextBody = buildUpdatedReleaseBody({
existingBody: oversizedNotes,
summary: validReleaseSummary({
en: {
headline: "A concise release summary.",
sections: [{ title: "Bug Fixes", items: ["Kept releases reliable."] }],
qaFocus: []
}
})
});
assert.ok(nextBody.length <= GITHUB_RELEASE_BODY_MAX_LENGTH);
assert.match(nextBody, /A concise release summary/);
assert.match(nextBody, /generated note 0:/);
assert.doesNotMatch(nextBody, /generated note 1999:/);
assert.ok(nextBody.includes(RELEASE_NOTES_TRUNCATION_NOTICE));
});
test("desktop release summary validator accepts a complete staged summary", () => {
const summary = validReleaseSummary();
assert.equal(
validateReleaseSummary(summary, {
tag: summary.tag,
channel: summary.channel,
targetCommit: summary.targetCommit
}),
summary
);
});
test("desktop release summary validator accepts a human-reviewed summary", () => {
assert.doesNotThrow(() =>
validateReleaseSummary(
validReleaseSummary({ summarySource: "human-reviewed" })
)
);
});
test("desktop release summary validator rejects inconsistent or incomplete summaries", () => {
assert.throws(
() => validateReleaseSummary(validReleaseSummary({ prerelease: false })),
/prerelease flag/
);
assert.throws(
() => validateReleaseSummary(validReleaseSummary({ en: { headline: "" } })),
/en.headline/
);
});