返回 DeepSeek-Reasonix
release-notes.test.mjs
根目录 / scripts / release-notes.test.mjs
1 import assert from "node:assert/strict";
2 import { test } from "node:test";
3 import {
4 editorialLimitInstruction,
5 inferPullTargetHints,
6 releaseOutputBudgets,
7 } from "./generate-release-notes.mjs";
8 import { loadCatalog, releaseForVersion, renderGitHubRelease, validateCatalog } from "./release-notes.mjs";
9 import { validateReleaseEvent } from "./release-event.mjs";
10
11 test("the committed release catalog is valid and newest first", async () => {
12 const catalog = await loadCatalog();
13 assert.equal(catalog.schemaVersion, 1);
14 assert.equal(new Set(catalog.releases.map((release) => release.version)).size, catalog.releases.length);
15 });
16
17 test("tag namespaces resolve to the same product release", async () => {
18 const catalog = await loadCatalog();
19 assert.equal(releaseForVersion(catalog, "v1.17.13").version, "1.17.13");
20 assert.equal(releaseForVersion(catalog, "desktop-v1.17.13").version, "1.17.13");
21 assert.equal(releaseForVersion(catalog, "npm-v1.17.13").version, "1.17.13");
22 });
23
24 test("GitHub rendering keeps product sections and source PR links", async () => {
25 const catalog = await loadCatalog();
26 const markdown = renderGitHubRelease(releaseForVersion(catalog, "1.17.13"), "zh");
27 assert.match(markdown, /## 使用攻略/);
28 assert.match(markdown, /## 重点内容/);
29 assert.match(markdown, /## 升级提醒/);
30 assert.match(markdown, /## 风险提示/);
31 assert.match(markdown, /## 致谢/);
32 assert.match(markdown, /\/pull\/6460/);
33 assert.match(markdown, /reasonix\.io\/changelog\/v1\.17\.13/);
34 });
35
36 test("targeted releases render Desktop and CLI sections from one shared item", async () => {
37 const catalog = await loadCatalog();
38 const markdown = renderGitHubRelease(releaseForVersion(catalog, "1.31.4"), "zh");
39 assert.match(markdown, /## 桌面端更新/);
40 assert.match(markdown, /## CLI 端更新/);
41 assert.match(markdown, /## 其他项目更新/);
42 assert.match(markdown, /\*\*\[Service\]\*\* \*\*降低数据库读取成本\*\*/);
43 assert.equal(markdown.match(/更好的数学渲染/g)?.length, 2);
44 });
45
46 test("targeted release validation requires canonical item targets and matching surfaces", () => {
47 const release = {
48 version: "2.0.0",
49 targetingVersion: 1,
50 date: "2026-01-01",
51 channel: "stable",
52 title: { en: "Targeted", zh: "分类版本" },
53 summary: { en: "Summary", zh: "摘要" },
54 surfaces: ["desktop", "cli"],
55 guides: [],
56 highlights: [{
57 kind: "fixed",
58 targets: ["desktop", "cli"],
59 title: { en: "Shared fix", zh: "共享修复" },
60 body: { en: "A shared fix.", zh: "一项共享修复。" },
61 refs: [1],
62 }],
63 changes: { new: [], improved: [], fixed: [] },
64 upgrade: [],
65 risks: [],
66 contributors: [],
67 links: {
68 github: "https://example.com/release",
69 compare: "https://example.com/compare",
70 download: "https://example.com/download",
71 },
72 };
73 assert.doesNotThrow(() => validateCatalog({ schemaVersion: 1, releases: [release] }));
74 assert.throws(
75 () => validateCatalog({
76 schemaVersion: 1,
77 releases: [{ ...release, highlights: [{ ...release.highlights[0], targets: undefined }] }],
78 }),
79 /targets must be a non-empty array/,
80 );
81 assert.throws(
82 () => validateCatalog({ schemaVersion: 1, releases: [{ ...release, surfaces: ["desktop"] }] }),
83 /surfaces must equal the canonical union/,
84 );
85 assert.throws(
86 () => validateCatalog({
87 schemaVersion: 1,
88 releases: [{ ...release, highlights: [{ ...release.highlights[0], targets: ["cli", "desktop"] }] }],
89 }),
90 /targets must use canonical order/,
91 );
92 });
93
94 test("targeting adoption boundary preserves historical releases and rejects future legacy records", () => {
95 const release = {
96 version: "1.31.3",
97 date: "2026-01-01",
98 channel: "stable",
99 title: { en: "Legacy", zh: "旧版" },
100 summary: { en: "Summary", zh: "摘要" },
101 surfaces: ["desktop"],
102 guides: [],
103 highlights: [{
104 kind: "fixed",
105 title: { en: "Legacy fix", zh: "旧版修复" },
106 body: { en: "A historical fix.", zh: "一项历史修复。" },
107 refs: [1],
108 }],
109 changes: { new: [], improved: [], fixed: [] },
110 upgrade: [],
111 risks: [],
112 contributors: [],
113 links: {
114 github: "https://example.com/release",
115 compare: "https://example.com/compare",
116 download: "https://example.com/download",
117 },
118 };
119
120 assert.doesNotThrow(() => validateCatalog({ schemaVersion: 1, releases: [release] }));
121 for (const version of ["1.31.4", "1.32.0", "2.0.0-preview.1"]) {
122 assert.throws(
123 () => validateCatalog({ schemaVersion: 1, releases: [{ ...release, version }] }),
124 /targetingVersion must be 1 for v1\.31\.4 and newer/,
125 );
126 }
127 });
128
129 test("release target hints prefer explicit product labels and identify shared or service paths", () => {
130 assert.deepEqual(inferPullTargetHints(["desktop"], ["internal/sessioncatalog/catalog.go"]), ["desktop"]);
131 assert.deepEqual(inferPullTargetHints(["desktop", "tui"], ["desktop/app.go", "internal/cli/cli.go"]), ["desktop", "cli"]);
132 assert.deepEqual(inferPullTargetHints([], ["internal/agent/agent.go"]), ["desktop", "cli"]);
133 assert.deepEqual(inferPullTargetHints([], ["workers/crash-report/src/index.ts"]), ["service"]);
134 });
135
136 test("release generation uses bounded output and a tighter truncation retry", () => {
137 assert.ok(releaseOutputBudgets.standard.highlights > releaseOutputBudgets.compact.highlights);
138 assert.ok(releaseOutputBudgets.standard.changes > releaseOutputBudgets.compact.changes);
139 assert.ok(releaseOutputBudgets.standard.bodyChars > releaseOutputBudgets.compact.bodyChars);
140 assert.match(editorialLimitInstruction(), /Always return a complete JSON object/);
141 assert.match(editorialLimitInstruction(true), /at most 4 highlights/);
142 assert.match(editorialLimitInstruction(true), /9 total change items/);
143 });
144
145 test("validation rejects bilingual drift", () => {
146 assert.throws(
147 () =>
148 validateCatalog({
149 schemaVersion: 1,
150 releases: [
151 {
152 version: "1.0.0",
153 date: "2026-01-01",
154 channel: "stable",
155 title: { en: "Title", zh: "" },
156 summary: { en: "Summary", zh: "摘要" },
157 },
158 ],
159 }),
160 /title\.zh/,
161 );
162 });
163
164 test("managed Preview records bind every surface to one exact ordinal", () => {
165 const release = {
166 version: "1.19.0-preview.3",
167 releaseId: "1.19.0-preview.3",
168 baseVersion: "1.19.0",
169 date: "2026-07-31",
170 channel: "prerelease",
171 status: "reviewed",
172 previousRelease: "1.19.0-preview.2",
173 builds: {
174 cli: "v1.19.0-preview.3",
175 desktop: "v1.19.0-preview.3",
176 npm: "1.19.0-canary.3",
177 },
178 title: { en: "Preview", zh: "预览版" },
179 summary: { en: "Preview summary", zh: "预览版摘要" },
180 surfaces: ["cli"],
181 guides: [],
182 highlights: [{
183 kind: "fixed",
184 title: { en: "Fix", zh: "修复" },
185 body: { en: "A fix.", zh: "一项修复。" },
186 refs: [1],
187 }],
188 changes: { new: [], improved: [], fixed: [] },
189 upgrade: [],
190 risks: [],
191 contributors: [],
192 links: {
193 github: "https://github.com/esengine/DeepSeek-Reasonix/releases/tag/v1.19.0-preview.3",
194 compare: "https://github.com/esengine/DeepSeek-Reasonix/compare/v1.19.0-preview.2...v1.19.0-preview.3",
195 download: "https://reasonix.io/?download=desktop&channel=preview#start",
196 },
197 };
198
199 assert.doesNotThrow(() => validateCatalog({ schemaVersion: 1, releases: [release] }));
200 assert.throws(
201 () => validateCatalog({
202 schemaVersion: 1,
203 releases: [{ ...release, builds: { ...release.builds, npm: "1.19.0-canary.4" } }],
204 }),
205 /builds\.npm/,
206 );
207 });
208
209 test("publication marker is bound to reviewed version, channel, SHA, and builds", () => {
210 const release = {
211 version: "1.19.0-preview.3",
212 channel: "prerelease",
213 candidateSha: "a".repeat(40),
214 builds: {
215 cli: "v1.19.0-preview.3",
216 desktop: "v1.19.0-preview.3",
217 npm: "1.19.0-canary.3",
218 },
219 };
220 const event = {
221 schemaVersion: 1,
222 releaseId: release.version,
223 channel: "preview",
224 candidateSha: release.candidateSha,
225 publishedAt: "2026-07-31T00:00:00.000Z",
226 releaseNotesUrl: "https://reasonix.io/changelog/v1.19.0-preview.3/",
227 builds: release.builds,
228 };
229
230 assert.doesNotThrow(() => validateReleaseEvent(event, release));
231 assert.throws(
232 () => validateReleaseEvent({ ...event, candidateSha: "b".repeat(40) }, release),
233 /candidateSha/,
234 );
235 });
236
236 lines Plain Text