返回 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 { loadCatalog, releaseForVersion, releaseRefs, renderGitHubRelease, validateCatalog } from "./release-notes.mjs";
4 import { validateReleaseEvent } from "./release-event.mjs";
5
6 test("the committed release catalog is valid and newest first", async () => {
7 const catalog = await loadCatalog();
8 assert.equal(catalog.schemaVersion, 1);
9 assert.equal(new Set(catalog.releases.map((release) => release.version)).size, catalog.releases.length);
10 });
11
12 test("tag namespaces resolve to the same product release", async () => {
13 const catalog = await loadCatalog();
14 assert.equal(releaseForVersion(catalog, "v1.17.13").version, "1.17.13");
15 assert.equal(releaseForVersion(catalog, "desktop-v1.17.13").version, "1.17.13");
16 assert.equal(releaseForVersion(catalog, "npm-v1.17.13").version, "1.17.13");
17 });
18
19 test("GitHub rendering keeps product sections and source PR links", async () => {
20 const catalog = await loadCatalog();
21 const markdown = renderGitHubRelease(releaseForVersion(catalog, "1.17.13"), "zh");
22 assert.match(markdown, /## 使用攻略/);
23 assert.match(markdown, /## 重点内容/);
24 assert.match(markdown, /## 升级提醒/);
25 assert.match(markdown, /## 风险提示/);
26 assert.match(markdown, /\/pull\/6460/);
27 assert.match(markdown, /reasonix\.io\/changelog\/v1\.17\.13/);
28 });
29
30 test("rendering credits authors and fixers and lists them once, in order", async () => {
31 const catalog = await loadCatalog();
32 const release = structuredClone(releaseForVersion(catalog, "1.39.1"));
33 release.contributors = ["late-comer"];
34 const refs = releaseRefs(release);
35 const special = {
36 10853: { kind: "pull", login: "github-actions[bot]", bot: true },
37 10858: { kind: "issue", fixes: [{ number: 20000, login: "fixer", bot: false }] },
38 10495: { kind: "issue", fixes: [] },
39 };
40 const credits = new Map(
41 refs.map((ref) => [ref, special[ref] || { kind: "pull", login: ref % 2 ? "KHG420" : "esengine", bot: false }]),
42 );
43 const zh = renderGitHubRelease(release, "zh", credits);
44 assert.match(zh, /\[#10763\]\([^)]+\/pull\/10763\) by @KHG420/);
45 assert.match(zh, /\[#10858\]\([^)]+\/pull\/10858\) fixed in #20000 by @fixer, /);
46 assert.match(zh, /\[#10495\]\([^)]+\/pull\/10495\), /);
47 assert.doesNotMatch(zh, /by @github-actions/);
48 assert.match(zh, /## 贡献者\n\n感谢本版本的贡献者:@KHG420、@fixer、@esengine\n$/);
49 const en = renderGitHubRelease(release, "en", credits);
50 assert.match(en, /## Contributors\n\nThanks to the contributors in this release: @KHG420, @fixer, @esengine\n$/);
51 assert.doesNotMatch(en, /late-comer/);
52 });
53
54 test("an unresolved ref renders as the plain link it always was", async () => {
55 const catalog = await loadCatalog();
56 const release = releaseForVersion(catalog, "1.39.0");
57 const markdown = renderGitHubRelease(release, "zh", new Map());
58 assert.doesNotMatch(markdown, / by @/);
59 assert.match(markdown, /\(\[#10731\]\(https:\/\/github\.com\/esengine\/DeepSeek-Reasonix\/pull\/10731\)\)/);
60 assert.doesNotMatch(markdown, /## 贡献者/);
61 });
62
63 test("targeted releases render Desktop and CLI sections from one shared item", async () => {
64 const catalog = await loadCatalog();
65 const markdown = renderGitHubRelease(releaseForVersion(catalog, "1.31.4"), "zh");
66 assert.match(markdown, /## 桌面端更新/);
67 assert.match(markdown, /## CLI 端更新/);
68 assert.match(markdown, /## 其他项目更新/);
69 assert.match(markdown, /\*\*\[Service\]\*\* \*\*降低数据库读取成本\*\*/);
70 assert.equal(markdown.match(/更好的数学渲染/g)?.length, 2);
71 });
72
73 test("targeted release validation requires canonical item targets and matching surfaces", () => {
74 const release = {
75 version: "2.0.0",
76 targetingVersion: 1,
77 date: "2026-01-01",
78 channel: "stable",
79 title: { en: "Targeted", zh: "分类版本" },
80 summary: { en: "Summary", zh: "摘要" },
81 surfaces: ["desktop", "cli"],
82 guides: [],
83 highlights: [{
84 kind: "fixed",
85 targets: ["desktop", "cli"],
86 title: { en: "Shared fix", zh: "共享修复" },
87 body: { en: "A shared fix.", zh: "一项共享修复。" },
88 refs: [1],
89 }],
90 changes: { new: [], improved: [], fixed: [] },
91 upgrade: [],
92 risks: [],
93 contributors: [],
94 links: {
95 github: "https://example.com/release",
96 compare: "https://example.com/compare",
97 download: "https://example.com/download",
98 },
99 };
100 assert.doesNotThrow(() => validateCatalog({ schemaVersion: 1, releases: [release] }));
101 assert.throws(
102 () => validateCatalog({
103 schemaVersion: 1,
104 releases: [{ ...release, highlights: [{ ...release.highlights[0], targets: undefined }] }],
105 }),
106 /targets must be a non-empty array/,
107 );
108 assert.throws(
109 () => validateCatalog({ schemaVersion: 1, releases: [{ ...release, surfaces: ["desktop"] }] }),
110 /surfaces must equal the canonical union/,
111 );
112 assert.throws(
113 () => validateCatalog({
114 schemaVersion: 1,
115 releases: [{ ...release, highlights: [{ ...release.highlights[0], targets: ["cli", "desktop"] }] }],
116 }),
117 /targets must use canonical order/,
118 );
119 });
120
121 test("targeting adoption boundary preserves historical releases and rejects future legacy records", () => {
122 const release = {
123 version: "1.31.3",
124 date: "2026-01-01",
125 channel: "stable",
126 title: { en: "Legacy", zh: "旧版" },
127 summary: { en: "Summary", zh: "摘要" },
128 surfaces: ["desktop"],
129 guides: [],
130 highlights: [{
131 kind: "fixed",
132 title: { en: "Legacy fix", zh: "旧版修复" },
133 body: { en: "A historical fix.", zh: "一项历史修复。" },
134 refs: [1],
135 }],
136 changes: { new: [], improved: [], fixed: [] },
137 upgrade: [],
138 risks: [],
139 contributors: [],
140 links: {
141 github: "https://example.com/release",
142 compare: "https://example.com/compare",
143 download: "https://example.com/download",
144 },
145 };
146
147 assert.doesNotThrow(() => validateCatalog({ schemaVersion: 1, releases: [release] }));
148 for (const version of ["1.31.4", "1.32.0", "2.0.0-preview.1"]) {
149 assert.throws(
150 () => validateCatalog({ schemaVersion: 1, releases: [{ ...release, version }] }),
151 /targetingVersion must be 1 for v1\.31\.4 and newer/,
152 );
153 }
154 });
155
156 test("validation rejects bilingual drift", () => {
157 assert.throws(
158 () =>
159 validateCatalog({
160 schemaVersion: 1,
161 releases: [
162 {
163 version: "1.0.0",
164 date: "2026-01-01",
165 channel: "stable",
166 title: { en: "Title", zh: "" },
167 summary: { en: "Summary", zh: "摘要" },
168 },
169 ],
170 }),
171 /title\.zh/,
172 );
173 });
174
175 test("managed Preview records bind every surface to one exact ordinal", () => {
176 const release = {
177 version: "1.19.0-preview.3",
178 releaseId: "1.19.0-preview.3",
179 baseVersion: "1.19.0",
180 date: "2026-07-31",
181 channel: "prerelease",
182 status: "reviewed",
183 previousRelease: "1.19.0-preview.2",
184 builds: {
185 cli: "v1.19.0-preview.3",
186 desktop: "v1.19.0-preview.3",
187 npm: "1.19.0-canary.3",
188 },
189 title: { en: "Preview", zh: "预览版" },
190 summary: { en: "Preview summary", zh: "预览版摘要" },
191 surfaces: ["cli"],
192 guides: [],
193 highlights: [{
194 kind: "fixed",
195 title: { en: "Fix", zh: "修复" },
196 body: { en: "A fix.", zh: "一项修复。" },
197 refs: [1],
198 }],
199 changes: { new: [], improved: [], fixed: [] },
200 upgrade: [],
201 risks: [],
202 contributors: [],
203 links: {
204 github: "https://github.com/esengine/DeepSeek-Reasonix/releases/tag/v1.19.0-preview.3",
205 compare: "https://github.com/esengine/DeepSeek-Reasonix/compare/v1.19.0-preview.2...v1.19.0-preview.3",
206 download: "https://reasonix.io/?download=desktop&channel=preview#start",
207 },
208 };
209
210 assert.doesNotThrow(() => validateCatalog({ schemaVersion: 1, releases: [release] }));
211 assert.throws(
212 () => validateCatalog({
213 schemaVersion: 1,
214 releases: [{ ...release, builds: { ...release.builds, npm: "1.19.0-canary.4" } }],
215 }),
216 /builds\.npm/,
217 );
218 });
219
220 test("publication marker is bound to reviewed version, channel, SHA, and builds", () => {
221 const release = {
222 version: "1.19.0-preview.3",
223 channel: "prerelease",
224 candidateSha: "a".repeat(40),
225 builds: {
226 cli: "v1.19.0-preview.3",
227 desktop: "v1.19.0-preview.3",
228 npm: "1.19.0-canary.3",
229 },
230 };
231 const event = {
232 schemaVersion: 1,
233 releaseId: release.version,
234 channel: "preview",
235 candidateSha: release.candidateSha,
236 publishedAt: "2026-07-31T00:00:00.000Z",
237 releaseNotesUrl: "https://reasonix.io/changelog/v1.19.0-preview.3/",
238 builds: release.builds,
239 };
240
241 assert.doesNotThrow(() => validateReleaseEvent(event, release));
242 assert.throws(
243 () => validateReleaseEvent({ ...event, candidateSha: "b".repeat(40) }, release),
244 /candidateSha/,
245 );
246 });
247
247 lines Plain Text