package server import ( "fmt" "os" "os/exec" "path/filepath" "strings" "testing" "gitcat/internal/gitx" "gitcat/internal/store" ) // testHash 是测试账号的明文密码;哈希由服务自己的 hashPassword 生成, // 必须用真 bcrypt 串——basicAuthUser 会走 CompareHashAndAuthentication。 const testPass = "secret-pw" func testHash(t *testing.T) string { t.Helper() h, err := hashPassword(testPass) if err != nil { t.Fatalf("生成密码哈希失败: %v", err) } return h } // newTestServer 起一个已完成安装的测试服务。alice 是管理员兼仓库 owner, // bob 是无关的普通用户——权限测试只需要这个区分。 func newTestServer(t *testing.T) (*Server, *store.Store, *store.User, *store.User) { t.Helper() dir := t.TempDir() st, err := store.Open(filepath.Join(dir, "gitcat.db")) if err != nil { t.Fatalf("打开数据库失败: %v", err) } t.Cleanup(func() { st.Close() }) for _, kv := range [][2]string{{"installed", "1"}, {"site_name", "测试站"}} { if err := st.SetSetting(kv[0], kv[1]); err != nil { t.Fatalf("写入设置失败: %v", err) } } alice, err := st.CreateUser("alice", "Alice", "", testHash(t), true) if err != nil { t.Fatalf("建 alice 失败: %v", err) } bob, err := st.CreateUser("bob", "Bob", "", testHash(t), false) if err != nil { t.Fatalf("建 bob 失败: %v", err) } srv, err := New(Config{Addr: ":0", DataDir: dir, Version: "1.0.0-test"}, st) if err != nil { t.Fatalf("构造服务失败: %v", err) } return srv, st, alice, bob } // makeRepo 造一个仓库记录 + 磁盘裸仓库,返回目录路径。 // owner 为 nil 时挂到 alice 名下——repos.owner_id 有外键,传 0 会失败。 func makeRepo(t *testing.T, srv *Server, st *store.Store, name string, owner *store.User) string { t.Helper() if owner == nil { owner, _ = st.UserByUsername("alice") } repo, err := st.CreateRepo(name, "", owner.ID) if err != nil { t.Fatalf("建仓库记录失败: %v", err) } dir := srv.repoPath(repo.Name) if err := gitx.InitBare(dir, "main"); err != nil { t.Fatalf("初始化裸仓库失败: %v", err) } return dir } // gitOut 在裸仓库里跑一条 git 命令并返回输出。 func gitOut(t *testing.T, dir, stdin string, args ...string) string { t.Helper() cmd := exec.Command("git", args...) cmd.Dir = dir cmd.Env = testGitEnv() if stdin != "" { cmd.Stdin = strings.NewReader(stdin) } out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("git %s 失败: %v\n%s", strings.Join(args, " "), err, out) } return strings.TrimSpace(string(out)) } // commitFiles 把一组文件写进裸仓库并提交到 main。 func commitFiles(t *testing.T, dir, message string, files map[string]string) { t.Helper() var lines []string for name, content := range files { sha := gitOut(t, dir, content, "hash-object", "-w", "--stdin") lines = append(lines, fmt.Sprintf("100644 blob %s\t%s", sha, name)) } tree := gitOut(t, dir, strings.Join(lines, "\n")+"\n", "mktree") commit := gitOut(t, dir, "", "commit-tree", tree, "-m", message) gitOut(t, dir, "", "update-ref", "refs/heads/main", commit) } // testGitEnv 补上 git 作者身份,测试机未必配了 user.name/email。 func testGitEnv() []string { return append(os.Environ(), "GIT_AUTHOR_NAME=gitcat", "GIT_AUTHOR_EMAIL=test@gitcat.local", "GIT_COMMITTER_NAME=gitcat", "GIT_COMMITTER_EMAIL=test@gitcat.local") }