package server import ( "net/http" "net/http/httptest" "strings" "testing" ) // doPush 发起一次推送请求。 func doPush(srv *Server, target, user, pass string) *httptest.ResponseRecorder { r := httptest.NewRequest(http.MethodPost, target, strings.NewReader("0000")) if user != "" { r.SetBasicAuth(user, pass) } w := httptest.NewRecorder() srv.Handler().ServeHTTP(w, r) return w } // TestPushRequiresRepoOwnership 是 P0-1 的回归测试:任意已登录用户都不得 // 向他人仓库推送。修复前 bob 能往 alice 的仓库任意推、删分支、删标签。 func TestPushRequiresRepoOwnership(t *testing.T) { srv, st, alice, bob := newTestServer(t) makeRepo(t, srv, st, "victim", alice) for _, target := range []string{ "/victim.git/git-receive-pack", "/victim.git/info/refs?service=git-receive-pack", } { if w := doPush(srv, target, bob.Username, testPass); w.Code != http.StatusForbidden { t.Errorf("bob 推送 alice 的仓库(%s)返回 %d,期望 403", target, w.Code) } } // owner 本人必须放行,否则说明权限判断写死了 if w := doPush(srv, "/victim.git/git-receive-pack", alice.Username, testPass); w.Code == http.StatusForbidden { t.Error("仓库 owner 自己推送被误拒") } } func TestPushRequiresAuth(t *testing.T) { srv, st, _, _ := newTestServer(t) makeRepo(t, srv, st, "open", nil) w := doPush(srv, "/open.git/git-receive-pack", "", "") if w.Code != http.StatusUnauthorized { t.Fatalf("匿名推送返回 %d,期望 401", w.Code) } if w.Header().Get("WWW-Authenticate") == "" { t.Error("缺少 WWW-Authenticate 挑战头") } } func TestArchivedRepoBlocksPush(t *testing.T) { srv, st, alice, _ := newTestServer(t) makeRepo(t, srv, st, "old", alice) repo, _ := st.RepoByName("old") repo.IsArchived = true if err := st.UpdateRepo(repo); err != nil { t.Fatalf("归档失败: %v", err) } if w := doPush(srv, "/old.git/git-receive-pack", alice.Username, testPass); w.Code != http.StatusForbidden { t.Fatalf("归档仓库推送返回 %d,期望 403", w.Code) } } // TestCloneStaysPublic 确认修复没有把只读克隆也一起拦住。 func TestCloneStaysPublic(t *testing.T) { srv, st, _, _ := newTestServer(t) makeRepo(t, srv, st, "public", nil) r := httptest.NewRequest(http.MethodGet, "/public.git/info/refs?service=git-upload-pack", nil) w := httptest.NewRecorder() srv.Handler().ServeHTTP(w, r) if w.Code != http.StatusOK { t.Fatalf("匿名 clone 返回 %d,期望 200(只读克隆必须保持公开)", w.Code) } } // TestDisabledUserCannotPush 停用账号不得推送。 func TestDisabledUserCannotPush(t *testing.T) { srv, st, _, _ := newTestServer(t) makeRepo(t, srv, st, "repo1", nil) bob, _ := st.UserByUsername("bob") if err := st.SetUserFlags(bob.ID, false, true); err != nil { t.Fatalf("停用 bob 失败: %v", err) } if w := doPush(srv, "/repo1.git/git-receive-pack", "bob", testPass); w.Code != http.StatusUnauthorized { t.Fatalf("停用账号推送返回 %d,期望 401", w.Code) } }