From 3628a7898b5f137cc149668b6eaf287a7faea94f Mon Sep 17 00:00:00 2001 From: Ruslan Popov Date: Wed, 24 Dec 2025 10:38:58 +0300 Subject: [PATCH] Add tests for bumpversion logic --- src/main_test.go | 222 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/main_test.go diff --git a/src/main_test.go b/src/main_test.go new file mode 100644 index 0000000..9dfe66f --- /dev/null +++ b/src/main_test.go @@ -0,0 +1,222 @@ +package main + +import ( + "os" + "path/filepath" + "reflect" + "strings" + "testing" +) + +func TestGetBumpConfig(t *testing.T) { + tmpDir := t.TempDir() + cfgPath := filepath.Join(tmpDir, ".bumpversion.cfg") + cfg := ` +[bumpversion] +current_version = 1.2.3 +commit = true +tag = true +tag_name = v{new_version} +parse = ^(?P\d+)\.(?P\d+)\.(?P\d+)$ +serialize = {major}.{minor}.{patch} +message = Release {new_version} + +[bumpversion:file:VERSION] +[bumpversion:file:README.md] +` + if err := os.WriteFile(cfgPath, []byte(cfg), 0o644); err != nil { + t.Fatalf("write config: %v", err) + } + + got, err := getBumpConfig(cfgPath) + if err != nil { + t.Fatalf("getBumpConfig returned error: %v", err) + } + + if got.CurrentVersion != "1.2.3" { + t.Fatalf("CurrentVersion = %q, want 1.2.3", got.CurrentVersion) + } + if !got.Commit || !got.Tag { + t.Fatalf("expected commit and tag to be true, got commit=%v tag=%v", got.Commit, got.Tag) + } + if got.TagName != "v{new_version}" { + t.Fatalf("TagName = %q, want v{new_version}", got.TagName) + } + if strings.TrimSpace(got.Serialize) != "{major}.{minor}.{patch}" { + t.Fatalf("Serialize = %q, want {major}.{minor}.{patch}", got.Serialize) + } + if got.Parse != `^(?P\d+)\.(?P\d+)\.(?P\d+)$` { + t.Fatalf("Parse = %q, want regex string", got.Parse) + } + + wantPaths := map[string]bool{ + "VERSION": true, + "README.md": true, + } + for _, p := range got.FilePaths { + delete(wantPaths, p) + } + if len(wantPaths) != 0 { + t.Fatalf("FilePaths missing entries: %v", reflect.ValueOf(wantPaths).MapKeys()) + } +} + +func TestUpdateConfigFile(t *testing.T) { + tmpDir := t.TempDir() + cfgPath := filepath.Join(tmpDir, ".bumpversion.cfg") + cfg := ` +[bumpversion] +current_version = 0.0.1 +commit = false +` + if err := os.WriteFile(cfgPath, []byte(cfg), 0o644); err != nil { + t.Fatalf("write config: %v", err) + } + + if err := updateConfigFile(cfgPath, "0.0.2"); err != nil { + t.Fatalf("updateConfigFile returned error: %v", err) + } + + updated, err := getBumpConfig(cfgPath) + if err != nil { + t.Fatalf("getBumpConfig returned error: %v", err) + } + if updated.CurrentVersion != "0.0.2" { + t.Fatalf("CurrentVersion = %q, want 0.0.2", updated.CurrentVersion) + } +} + +func TestBumpVersion(t *testing.T) { + bc := &BumpConfig{ + CurrentVersion: "1.2.3", + Parse: `^v?(?P\d+)\.(?P\d+)\.(?P\d+)$`, + Serialize: "{major}.{minor}.{patch}", + } + + tests := []struct { + name string + part string + want string + wantErr bool + }{ + {name: "major", part: "major", want: "2.0.0"}, + {name: "minor", part: "minor", want: "1.3.0"}, + {name: "patch", part: "patch", want: "1.2.4"}, + {name: "unknown", part: "build", wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := bumpVersion(bc, tt.part) + if (err != nil) != tt.wantErr { + t.Fatalf("bumpVersion error = %v, wantErr %v", err, tt.wantErr) + } + if err == nil && got != tt.want { + t.Fatalf("bumpVersion = %q, want %q", got, tt.want) + } + }) + } +} + +func TestBumpVersionInvalidCurrent(t *testing.T) { + bc := &BumpConfig{ + CurrentVersion: "1.2", + Parse: `^v?(?P\d+)\.(?P\d+)\.(?P\d+)$`, + Serialize: "{major}.{minor}.{patch}", + } + if _, err := bumpVersion(bc, "patch"); err == nil { + t.Fatalf("expected error for invalid current_version") + } +} + +func TestUpdateFiles(t *testing.T) { + tmpDir := t.TempDir() + oldV := "1.2.3" + newV := "1.2.4" + + filePaths := []string{ + filepath.Join(tmpDir, "VERSION"), + filepath.Join(tmpDir, "README.md"), + } + + for _, p := range filePaths { + contents := "project version " + oldV + "\n" + if err := os.WriteFile(p, []byte(contents), 0o644); err != nil { + t.Fatalf("write %s: %v", p, err) + } + } + + updateFiles(filePaths, oldV, newV) + + for _, p := range filePaths { + data, err := os.ReadFile(p) + if err != nil { + t.Fatalf("read %s: %v", p, err) + } + if !strings.Contains(string(data), newV) || strings.Contains(string(data), oldV) { + t.Fatalf("%s not updated correctly: %s", p, string(data)) + } + } +} + +func TestResolveFlag(t *testing.T) { + boolPtr := func(b bool) *bool { return &b } + + tests := []struct { + name string + positive *bool + negative *bool + defaultValue bool + want bool + wantPanic bool + }{ + { + name: "positive wins", + positive: boolPtr(true), + negative: boolPtr(false), + defaultValue: false, + want: true, + }, + { + name: "negative wins", + positive: boolPtr(false), + negative: boolPtr(true), + defaultValue: true, + want: false, + }, + { + name: "default used", + positive: boolPtr(false), + negative: boolPtr(false), + defaultValue: true, + want: true, + }, + { + name: "panic on conflict", + positive: boolPtr(true), + negative: boolPtr(true), + defaultValue: false, + wantPanic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); tt.wantPanic && r == nil { + t.Fatalf("expected panic but function returned") + } else if !tt.wantPanic && r != nil { + t.Fatalf("unexpected panic: %v", r) + } + }() + + got := resolveFlag(tt.positive, tt.negative, tt.defaultValue) + if tt.wantPanic { + return + } + if got != tt.want { + t.Fatalf("resolveFlag = %v, want %v", got, tt.want) + } + }) + } +}