Scert/backend/internal/httpapi/project_bindings_many_test.go
xuans e2200534d3 完善域名服务器多对多关联与共享路径预检,补齐回归源码及进度文档
支持同项目内域名与服务器双向复用,局部修改和清空不影响其他域名;在绑定保存、服务器编辑和部署前拒绝已知共享路径冲突。

新增仓储、接口、工作流与页面回归源码,同步任务状态和第三版接口交接。按要求先开发后统一测试,本批未运行测试、构建或部署,保留在途任务与主机别名等待办。
2026-09-28 21:51:05 +08:00

91 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package httpapi
import (
"encoding/json"
"net/http"
"slices"
"strings"
"testing"
"git.pcux.cn/xuans/Scert/backend/internal/deployment"
"git.pcux.cn/xuans/Scert/backend/internal/domain"
"git.pcux.cn/xuans/Scert/backend/internal/repository"
)
func TestProjectBindingAPIManyToManyAndPathConflict(t *testing.T) {
f, _ := newProjectResourceAPIFixture(t)
server, err := f.server.servers.GetServerByID(t.Context(), "server-a")
if err != nil {
t.Fatal(err)
}
server.CertificatePath, server.CertificateChainPath, server.PrivateKeyPath = "", "", ""
if err := f.server.servers.UpdateServer(t.Context(), server); err != nil {
t.Fatal(err)
}
server.ID, server.Name = "server-c", "第二台共享服务器"
if err := f.server.servers.CreateServer(t.Context(), server); err != nil {
t.Fatal(err)
}
item, err := domain.NewDomain("second.example.test", domain.CertificateSingle, nil)
if err != nil {
t.Fatal(err)
}
item.ID, item.ProjectID = "domain-c", "project-a"
if err := repository.NewSQLDomainRepository(f.db).Create(t.Context(), item); err != nil {
t.Fatal(err)
}
put := func(id, body string, expected int) {
t.Helper()
response := serveUserRequest(f.server, f.cookies["manager"], http.MethodPut, "/api/v1/domains/"+id+"/servers", body)
if response.Code != expected {
t.Fatalf("多对多保存响应错误:%s,%d,%s", id, response.Code, response.Body.String())
}
if expected == http.StatusConflict && (!strings.Contains(response.Body.String(), "路径冲突") || strings.Contains(response.Body.String(), "second.example.test")) {
t.Fatalf("冲突提示不明确或泄露其他域名:%s", response.Body.String())
}
}
assertBindings := func(id string, expected []string) {
t.Helper()
response := serveUserRequest(f.server, f.cookies["manager"], http.MethodGet, "/api/v1/domains/"+id+"/servers", "")
var result struct {
Items []deployment.Server `json:"items"`
}
if response.Code != http.StatusOK || json.Unmarshal(response.Body.Bytes(), &result) != nil || result.Items == nil {
t.Fatalf("多对多读取响应错误:%d,%s", response.Code, response.Body.String())
}
ids := make([]string, 0, len(result.Items))
for _, item := range result.Items {
ids = append(ids, item.ID)
}
slices.Sort(ids)
if !slices.Equal(ids, expected) {
t.Fatalf("改变一个域名后关联不正确:%s,%v,期望 %v", id, ids, expected)
}
}
both := []string{"server-a", "server-c"}
put("domain-a", `{"server_ids":["server-c","server-a","server-a"]}`, http.StatusOK)
put("domain-c", `{"server_ids":["server-a","server-c"]}`, http.StatusOK)
assertBindings("domain-a", both)
assertBindings("domain-c", both)
put("domain-a", `{"server_ids":["server-c"]}`, http.StatusOK)
assertBindings("domain-a", both[1:])
assertBindings("domain-c", both)
put("domain-a", `{"server_ids":[]}`, http.StatusOK)
assertBindings("domain-a", []string{})
assertBindings("domain-c", both)
put("domain-a", `{"server_ids":["server-a","server-c"]}`, http.StatusOK)
put("domain-a", `{"server_ids":["server-b"]}`, http.StatusForbidden)
assertBindings("domain-a", both)
assertBindings("domain-c", both)
if _, err := f.db.ExecContext(t.Context(), `UPDATE servers SET certificate_path='/ssl/fixed/cert.pem' WHERE id='server-a'`); err != nil {
t.Fatal(err)
}
put("domain-a", `{"server_ids":["server-a","server-c"]}`, http.StatusConflict)
assertBindings("domain-a", both)
assertBindings("domain-c", both)
entry := f.lastAudit(t)
if entry.Action != "domain.servers.replace" || entry.ProjectID != "project-a" || entry.Status != "failed" {
t.Fatalf("路径冲突缺少项目失败审计:%+v", entry)
}
}