2a2e98bd8e
build / build (push) Successful in 1m41s
识别“## 开发快照 <version>(说明)”并保留旧 snapshot/v 格式。 版本号精确匹配、不跨二级标题、空正文不返回,archive 精确 SHA 绑定不变。
134 lines
4.9 KiB
Go
134 lines
4.9 KiB
Go
package versionhistory
|
||
|
||
import (
|
||
"os"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"knowledge-governance/internal/governance"
|
||
)
|
||
|
||
const plainNotesFixture = `# Changelog
|
||
|
||
## 开发快照 0.1.351(索引预检使用目标映射 ID)
|
||
|
||
### Fixed
|
||
|
||
- 索引预检使用目标映射 ID
|
||
|
||
## 开发快照 0.1.35(前缀相似版本)
|
||
|
||
### Fixed
|
||
|
||
- 不应被 0.1.351 命中
|
||
|
||
## 开发快照 0.1.350(首次复用取消本库历史来源门槛)
|
||
|
||
### Changed
|
||
|
||
- 首次复用说明
|
||
`
|
||
|
||
func TestPlainDevelopmentHeadingExactVersion(t *testing.T) {
|
||
got := governance.ExtractDevelopmentNotes(plainNotesFixture, "0.1.351")
|
||
if got == "" || !strings.Contains(got, "索引预检使用目标映射 ID") || strings.Contains(got, "首次复用说明") {
|
||
t.Fatalf("plain 0.1.351 notes wrong: %q", got)
|
||
}
|
||
if other := governance.ExtractDevelopmentNotes(plainNotesFixture, "0.1.35"); strings.Contains(other, "索引预检") {
|
||
t.Fatalf("0.1.35 must not match 0.1.351 body: %q", other)
|
||
}
|
||
if other := governance.ExtractDevelopmentNotes(plainNotesFixture, "0.1.3"); other != "" {
|
||
t.Fatalf("0.1.3 must not prefix-match 0.1.35/0.1.351: %q", other)
|
||
}
|
||
if next := governance.ExtractDevelopmentNotes(plainNotesFixture, "0.1.350"); !strings.Contains(next, "首次复用说明") || strings.Contains(next, "索引预检") {
|
||
t.Fatalf("adjacent version bleed: %q", next)
|
||
}
|
||
}
|
||
|
||
func TestPlainDevelopmentHeadingEmptyBodyAndLegacyFormat(t *testing.T) {
|
||
if got := governance.ExtractDevelopmentNotes("## 开发快照 0.1.351(空)\n\n## 开发快照 0.1.350(下一版)\n\n- x\n", "0.1.351"); got != "" {
|
||
t.Fatalf("empty body must stay empty: %q", got)
|
||
}
|
||
legacy := "## 开发快照 snapshot/v0.1.1 - 2026-10-18\n\n### Fixed\n\n- legacy body\n"
|
||
if got := governance.ExtractDevelopmentNotes(legacy, "0.1.1"); !strings.Contains(got, "legacy body") {
|
||
t.Fatalf("legacy snapshot format regressed: %q", got)
|
||
}
|
||
}
|
||
|
||
func TestRealChangelogDevelopmentNotes(t *testing.T) {
|
||
body, err := os.ReadFile("../../CHANGELOG.md")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
changelog := string(body)
|
||
for _, tc := range []struct{ version, want string }{
|
||
{"0.1.351", "索引预检"},
|
||
{"0.1.350", "首次同步可复用"},
|
||
} {
|
||
notes := governance.ExtractDevelopmentNotes(changelog, tc.version)
|
||
if notes == "" || !strings.Contains(notes, tc.want) {
|
||
t.Fatalf("real CHANGELOG %s notes missing: %q", tc.version, notes)
|
||
}
|
||
if governance.ExtractDevelopmentNotes(changelog, "0.1.35") != "" {
|
||
t.Fatal("prefix version matched a longer version")
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestDeriveVersionsPlainHeadingReleaseNotes(t *testing.T) {
|
||
text := "# Changelog\n\n## 开发快照 0.1.1(索引预检使用目标映射 ID)\n\n### Fixed\n\n- plain delivered notes\n\n## 开发快照 snapshot/v0.1.1 - 2026-10-18\n\n### Fixed\n\n- legacy duplicate heading\n\n## [0.1.0] - 2026-10-13\n\n### Added\n\n- baseline\n"
|
||
result := DeriveVersions(DeriveInput{
|
||
Commits: []RawCommit{{SHA: "a", CommittedAt: time.Now()}, {SHA: "b", Parents: []string{"a"}, CommittedAt: time.Now()}},
|
||
Tags: []RawTag{{Name: "v0.1.0", TargetSHA: "a", Annotated: true}},
|
||
Changelog: func(string) (string, error) {
|
||
return text, nil
|
||
},
|
||
})
|
||
for _, record := range result.Records {
|
||
if record.CommitSHA == "b" {
|
||
if record.RecordKind != governance.RecordKindDevelopment || !strings.Contains(record.ReleaseNotes, "plain delivered notes") ||
|
||
strings.Contains(record.ReleaseNotes, "legacy duplicate") {
|
||
t.Fatalf("DeriveVersions plain heading notes wrong: %q kind=%s label=%s", record.ReleaseNotes, record.RecordKind, record.VersionLabel)
|
||
}
|
||
return
|
||
}
|
||
}
|
||
t.Fatal("development record b missing")
|
||
}
|
||
|
||
func TestPlainDevelopmentArchiveBindsExactCommit(t *testing.T) {
|
||
sha := strings.Repeat("a", 40)
|
||
archive := "## 开发快照 0.1.351(索引预检使用目标映射 ID)\n\n目标提交:" + sha + "\n\n### Fixed\n\n- archived plain notes\n"
|
||
if got := governance.ExtractDevelopmentNotesForCommit(archive, "0.1.351", sha); !strings.Contains(got, "archived plain notes") {
|
||
t.Fatalf("plain archive notes missing for exact commit: %q", got)
|
||
}
|
||
if got := governance.ExtractDevelopmentNotesForCommit(archive, "0.1.351", strings.Repeat("b", 40)); got != "" {
|
||
t.Fatalf("plain archive notes leaked to wrong commit: %q", got)
|
||
}
|
||
}
|
||
|
||
func TestRealChangelogVersionInventory(t *testing.T) {
|
||
body, err := os.ReadFile("../../CHANGELOG.md")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
changelog := string(body)
|
||
for _, version := range []string{"0.1.351", "0.1.350", "0.1.349", "0.1.348"} {
|
||
notes := governance.ExtractDevelopmentNotes(changelog, version)
|
||
heading := ""
|
||
for _, line := range strings.Split(changelog, "\n") {
|
||
if strings.HasPrefix(line, "## 开发快照 "+version) || strings.HasPrefix(line, "## 开发快照 snapshot/v"+version) {
|
||
heading = line
|
||
break
|
||
}
|
||
}
|
||
t.Logf("version=%s heading=%q notes_bytes=%d", version, heading, len(notes))
|
||
if version == "0.1.351" || version == "0.1.350" {
|
||
if notes == "" {
|
||
t.Fatalf("%s has a CHANGELOG section but no extracted notes", version)
|
||
}
|
||
}
|
||
}
|
||
}
|