test: 补充 alias/browse/status 单元测试
- 新增 cmd/alias/alias_test.go: 8 个测试(加载/保存/覆盖/删除/无效YAML/子命令结构) - 新增 cmd/browse/browse_test.go: 5 个测试(命令结构/参数校验/示例内容) - 新增 cmd/status/status_test.go: 5 个测试(命令结构/token来源判断) 关联 Issue: #14
This commit is contained in:
parent
b887e5053d
commit
9474c1f432
|
|
@ -0,0 +1,193 @@
|
|||
package alias
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestLoadAliasesEmpty(t *testing.T) {
|
||||
// 设置临时配置目录
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
aliases, err := loadAliases()
|
||||
if err != nil {
|
||||
t.Fatalf("loadAliases failed: %v", err)
|
||||
}
|
||||
if len(aliases) != 0 {
|
||||
t.Fatalf("expected empty aliases, got %d", len(aliases))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveAndLoadAliases(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
// 保存
|
||||
original := map[string]string{
|
||||
"rl": "repo +list",
|
||||
"ri": "repo +info",
|
||||
}
|
||||
if err := saveAliases(original); err != nil {
|
||||
t.Fatalf("saveAliases failed: %v", err)
|
||||
}
|
||||
|
||||
// 加载
|
||||
loaded, err := loadAliases()
|
||||
if err != nil {
|
||||
t.Fatalf("loadAliases failed: %v", err)
|
||||
}
|
||||
if len(loaded) != 2 {
|
||||
t.Fatalf("expected 2 aliases, got %d", len(loaded))
|
||||
}
|
||||
if loaded["rl"] != "repo +list" {
|
||||
t.Errorf("expected rl -> repo +list, got %s", loaded["rl"])
|
||||
}
|
||||
if loaded["ri"] != "repo +info" {
|
||||
t.Errorf("expected ri -> repo +info, got %s", loaded["ri"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveAliasesOverwrite(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
// 第一次保存
|
||||
saveAliases(map[string]string{"rl": "repo +list"})
|
||||
|
||||
// 覆盖保存
|
||||
saveAliases(map[string]string{"rl": "repo +list --owner Gitlink"})
|
||||
|
||||
loaded, _ := loadAliases()
|
||||
if loaded["rl"] != "repo +list --owner Gitlink" {
|
||||
t.Errorf("alias should be overwritten, got %s", loaded["rl"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAliasesInvalidYAML(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
// 写入无效 YAML
|
||||
os.WriteFile(tmpDir+"/aliases.yaml", []byte("{{invalid yaml}}"), 0600)
|
||||
|
||||
aliases, err := loadAliases()
|
||||
if err != nil {
|
||||
t.Fatalf("should not error on invalid YAML, got: %v", err)
|
||||
}
|
||||
if len(aliases) != 0 {
|
||||
t.Fatalf("should return empty map on invalid YAML, got %d", len(aliases))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAliasCmd(t *testing.T) {
|
||||
cmd := NewAliasCmd()
|
||||
if cmd.Use != "alias" {
|
||||
t.Errorf("expected Use 'alias', got %s", cmd.Use)
|
||||
}
|
||||
if !cmd.HasSubCommands() {
|
||||
t.Error("alias command should have subcommands")
|
||||
}
|
||||
subcmds := cmd.Commands()
|
||||
if len(subcmds) != 3 {
|
||||
t.Fatalf("expected 3 subcommands, got %d", len(subcmds))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliasListSubcommand(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
cmd := NewAliasCmd()
|
||||
// 找到 +list 子命令
|
||||
var listCmd *cobra.Command
|
||||
for _, sub := range cmd.Commands() {
|
||||
if sub.Use == "+list" {
|
||||
listCmd = sub
|
||||
break
|
||||
}
|
||||
}
|
||||
if listCmd == nil {
|
||||
t.Fatal("+list subcommand not found")
|
||||
}
|
||||
|
||||
// 无别名时运行
|
||||
buf := new(bytes.Buffer)
|
||||
listCmd.SetOut(buf)
|
||||
listCmd.SetArgs([]string{})
|
||||
if err := listCmd.Execute(); err != nil {
|
||||
t.Fatalf("list failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(buf.String(), "未定义任何别名") {
|
||||
t.Errorf("expected hint for no aliases, got: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliasSetAndDeleteSubcommands(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
cmd := NewAliasCmd()
|
||||
|
||||
// 找到 +set 子命令
|
||||
var setCmd, deleteCmd *cobra.Command
|
||||
for _, sub := range cmd.Commands() {
|
||||
if strings.HasPrefix(sub.Use, "+set") {
|
||||
setCmd = sub
|
||||
}
|
||||
if strings.HasPrefix(sub.Use, "+delete") {
|
||||
deleteCmd = sub
|
||||
}
|
||||
}
|
||||
|
||||
// +set
|
||||
setCmd.SetArgs([]string{"rl", "repo +list"})
|
||||
if err := setCmd.Execute(); err != nil {
|
||||
t.Fatalf("set failed: %v", err)
|
||||
}
|
||||
|
||||
// 验证文件写入
|
||||
aliases, _ := loadAliases()
|
||||
if aliases["rl"] != "repo +list" {
|
||||
t.Fatalf("alias not saved correctly: %v", aliases)
|
||||
}
|
||||
|
||||
// +delete
|
||||
deleteCmd.SetArgs([]string{"rl"})
|
||||
if err := deleteCmd.Execute(); err != nil {
|
||||
t.Fatalf("delete failed: %v", err)
|
||||
}
|
||||
|
||||
// 验证已删除
|
||||
aliases, _ = loadAliases()
|
||||
if _, ok := aliases["rl"]; ok {
|
||||
t.Fatal("alias should have been deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliasDeleteNonExistent(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
t.Setenv("GITLINK_CONFIG_DIR", tmpDir)
|
||||
|
||||
cmd := NewAliasCmd()
|
||||
var deleteCmd *cobra.Command
|
||||
for _, sub := range cmd.Commands() {
|
||||
if strings.HasPrefix(sub.Use, "+delete") {
|
||||
deleteCmd = sub
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
deleteCmd.SetArgs([]string{"nonexistent"})
|
||||
err := deleteCmd.Execute()
|
||||
if err == nil {
|
||||
t.Fatal("expected error when deleting nonexistent alias")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "不存在") {
|
||||
t.Errorf("error should mention alias does not exist: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package browse
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewBrowseCmd(t *testing.T) {
|
||||
cmd := NewBrowseCmd()
|
||||
if cmd.Use != "browse [resource]" {
|
||||
t.Errorf("expected Use 'browse [resource]', got %s", cmd.Use)
|
||||
}
|
||||
if cmd.Short == "" {
|
||||
t.Error("Short description should not be empty")
|
||||
}
|
||||
if cmd.Long == "" {
|
||||
t.Error("Long description should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowseCmdHasCorrectArgs(t *testing.T) {
|
||||
cmd := NewBrowseCmd()
|
||||
// MaximumNArgs(1) should allow 0 or 1 args
|
||||
if err := cmd.Args(cmd, []string{}); err != nil {
|
||||
t.Errorf("should accept 0 args: %v", err)
|
||||
}
|
||||
if err := cmd.Args(cmd, []string{"issues/42"}); err != nil {
|
||||
t.Errorf("should accept 1 arg: %v", err)
|
||||
}
|
||||
if err := cmd.Args(cmd, []string{"a", "b"}); err == nil {
|
||||
t.Error("should reject more than 1 arg")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowseCmdSubcommandStructure(t *testing.T) {
|
||||
cmd := NewBrowseCmd()
|
||||
// browse 不应该有子命令
|
||||
if cmd.HasSubCommands() {
|
||||
t.Error("browse should not have subcommands")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowseCmdExample(t *testing.T) {
|
||||
cmd := NewBrowseCmd()
|
||||
if cmd.Example == "" {
|
||||
t.Error("Example should not be empty")
|
||||
}
|
||||
if !strings.Contains(cmd.Example, "browse") {
|
||||
t.Error("Example should contain 'browse'")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenBrowserReturnsNoError(t *testing.T) {
|
||||
// openBrowser 在所有平台都应该返回 nil 或一个 error
|
||||
// 在无头环境下可能会失败,但不应该 panic
|
||||
_ = openBrowser("https://gitlink.org.cn")
|
||||
// 只要不 panic 就行
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package status
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewStatusCmd(t *testing.T) {
|
||||
cmd := NewStatusCmd()
|
||||
if cmd.Use != "status" {
|
||||
t.Errorf("expected Use 'status', got %s", cmd.Use)
|
||||
}
|
||||
if cmd.Short == "" {
|
||||
t.Error("Short description should not be empty")
|
||||
}
|
||||
if cmd.Long == "" {
|
||||
t.Error("Long description should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStatusCmdExample(t *testing.T) {
|
||||
cmd := NewStatusCmd()
|
||||
if !strings.Contains(cmd.Example, "status") {
|
||||
t.Errorf("Example should contain 'status', got: %s", cmd.Example)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewStatusCmdHasNoSubcommands(t *testing.T) {
|
||||
cmd := NewStatusCmd()
|
||||
if cmd.HasSubCommands() {
|
||||
t.Error("status should not have subcommands")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenSourceFromEnv(t *testing.T) {
|
||||
t.Setenv("GITLINK_TOKEN", "test-token-123")
|
||||
result := tokenSource("test-token-123")
|
||||
if result != "环境变量 GITLINK_TOKEN" {
|
||||
t.Errorf("expected env source, got: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenSourceFromKeyring(t *testing.T) {
|
||||
// 不设置环境变量,或用不同的值
|
||||
t.Setenv("GITLINK_TOKEN", "")
|
||||
result := tokenSource("some-stored-token")
|
||||
if result != "keyring / 配置文件" {
|
||||
t.Errorf("expected keyring source, got: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenSourceMismatch(t *testing.T) {
|
||||
t.Setenv("GITLINK_TOKEN", "env-token")
|
||||
result := tokenSource("different-token")
|
||||
if result != "keyring / 配置文件" {
|
||||
t.Errorf("should fallback to keyring when token differs from env, got: %s", result)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue