Scert/backend/internal/httpapi/resource_scope_test.go
xuans 7284958819 完善域名服务器项目归属与数据库关联约束
保存资源项目、所有者和操作人,接口从会话记录归属,普通更新保留创建信息。
新增三库第十一版迁移、同项目绑定约束和 SQLite 触发器保护,补充迁移回滚重试、仓储、接口及备份恢复测试。
本次整理验证:后端构建、静态检查、关联改造针对性测试和 CLI 包测试通过。
范围说明:项目成员权限及其他资源归属尚未完成;本次未重新执行外部数据库实库、独立程序及生产部署验收。
2026-09-28 10:45:00 +08:00

79 lines
3.2 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"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.pcux.cn/xuans/Scert/backend/internal/database"
"git.pcux.cn/xuans/Scert/backend/internal/domain"
"git.pcux.cn/xuans/Scert/backend/internal/repository"
"github.com/gin-gonic/gin"
)
func TestDomainResourceScopeAPI(t *testing.T) {
cfg := readyTestConfig(t.TempDir())
db, err := database.Open(t.Context(), cfg.Database)
if err != nil {
t.Fatal(err)
}
defer db.Close()
if err := database.Migrate(t.Context(), db.DB, "sqlite"); err != nil {
t.Fatal(err)
}
repo := repository.NewSQLDomainRepository(db.DB)
server := New(Dependencies{Config: cfg, Repository: repo})
request := func(method, path, body, user string) *httptest.ResponseRecorder {
req := httptest.NewRequest(method, path, strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.AddCookie(httpUserSessionCookie(server, user, repository.UserRoleAdmin))
response := httptest.NewRecorder()
server.Handler().ServeHTTP(response, req)
return response
}
for _, field := range []string{"project_id", "owner_id", "created_by", "updated_by"} {
body := fmt.Sprintf(`{"name":"forged.example.test","certificate_type":"single","%s":"forged"}`, field)
response := request(http.MethodPost, "/api/v1/domains", body, "creator")
if response.Code != http.StatusBadRequest {
t.Fatalf("客户端不应提供 %s:%d", field, response.Code)
}
}
response := request(http.MethodPost, "/api/v1/domains", `{"name":"scope.example.test","certificate_type":"single"}`, "creator")
if response.Code != http.StatusCreated {
t.Fatalf("创建失败:%d,%s", response.Code, response.Body.String())
}
var created domain.Domain
if err := json.Unmarshal(response.Body.Bytes(), &created); err != nil {
t.Fatal(err)
}
want := domain.ResourceScope{ProjectID: "default", OwnerID: "creator", CreatedBy: "creator", UpdatedBy: "creator"}
if created.ResourceScope != want {
t.Fatalf("创建响应归属错误:%+v", created.ResourceScope)
}
response = request(http.MethodPut, "/api/v1/domains/"+created.ID, `{"name":"edited.example.test","certificate_type":"single"}`, "editor")
if response.Code != http.StatusOK {
t.Fatalf("更新失败:%d,%s", response.Code, response.Body.String())
}
loaded, err := repo.GetByID(t.Context(), created.ID)
want.UpdatedBy = "editor"
if err != nil || loaded.ResourceScope != want {
t.Fatalf("更新后归属错误:%+v,%v", loaded.ResourceScope, err)
}
}
func TestResourceScopePreservesProjectAndCreator(t *testing.T) {
server := New(Dependencies{Config: readyTestConfig(t.TempDir())})
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPut, "/", nil)
ctx.Request.AddCookie(httpUserSessionCookie(server, "editor", repository.UserRoleAdmin))
existing := domain.ResourceScope{ProjectID: "project-a", OwnerID: "owner", CreatedBy: "creator", UpdatedBy: "previous"}
updated := server.updatedResourceScope(ctx, existing)
if updated.ProjectID != existing.ProjectID || updated.OwnerID != existing.OwnerID || updated.CreatedBy != existing.CreatedBy || updated.UpdatedBy != "editor" {
t.Fatalf("更新操作者时丢失原资源归属:%+v", updated)
}
}