refactor(backend): split project write responsibilities
This commit is contained in:
98
backend/internal/logic/projects/write_handlers_test.go
Normal file
98
backend/internal/logic/projects/write_handlers_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package projects
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/require"
|
||||
"senlinai-agent/backend/internal/config"
|
||||
"senlinai-agent/backend/internal/httpx"
|
||||
"senlinai-agent/backend/internal/models"
|
||||
)
|
||||
|
||||
func TestTagRegistrarUsesIdentityAndListsOnlyProjectTags(t *testing.T) {
|
||||
database := newTestDB(t)
|
||||
require.NoError(t, database.Create(&models.SenlinAgentUser{Email: "owner@example.com", DisplayName: "Owner", PasswordHash: "hash"}).Error)
|
||||
first := models.SenlinAgentProject{OwnerID: 1, Name: "Alpha", Identifier: "ALPHA"}
|
||||
second := models.SenlinAgentProject{OwnerID: 1, Name: "Beta", Identifier: "BETA"}
|
||||
require.NoError(t, database.Create(&first).Error)
|
||||
require.NoError(t, database.Create(&second).Error)
|
||||
require.NoError(t, database.Create(&models.SenlinAgentTag{ProjectID: second.ID, Name: "其他项目"}).Error)
|
||||
router := newProjectWriteHandlerRouter(t, 1, NewTagHandler(NewService()))
|
||||
createReq := httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+first.Identity+"/tags", bytes.NewBufferString(`{"name":"设计"}`))
|
||||
createReq.Header.Set("Content-Type", "application/json")
|
||||
createReq.Header.Set("Authorization", "Bearer test-token")
|
||||
createRec := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(createRec, createReq)
|
||||
|
||||
require.Equal(t, http.StatusCreated, createRec.Code, createRec.Body.String())
|
||||
var created map[string]any
|
||||
require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
|
||||
require.NotEmpty(t, created["id"])
|
||||
require.Equal(t, "设计", created["name"])
|
||||
require.ElementsMatch(t, []string{"id", "name"}, mapKeys(created))
|
||||
|
||||
listReq := httptest.NewRequest(http.MethodGet, "/api/v1/projects/"+first.Identity+"/tags", nil)
|
||||
listReq.Header.Set("Authorization", "Bearer test-token")
|
||||
listRec := httptest.NewRecorder()
|
||||
router.ServeHTTP(listRec, listReq)
|
||||
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
|
||||
var listed []WorkspaceTagDTO
|
||||
require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listed))
|
||||
require.Len(t, listed, 1)
|
||||
require.Equal(t, "设计", listed[0].Name)
|
||||
}
|
||||
|
||||
func TestTagRegistrarRejectsProjectOwnedByAnotherUser(t *testing.T) {
|
||||
database := newTestDB(t)
|
||||
require.NoError(t, database.Create(&models.SenlinAgentUser{Email: "owner@example.com", DisplayName: "Owner", PasswordHash: "hash"}).Error)
|
||||
other := models.SenlinAgentProject{OwnerID: 2, Name: "Private", Identifier: "PRIVATE"}
|
||||
require.NoError(t, database.Create(&other).Error)
|
||||
router := newProjectWriteHandlerRouter(t, 1, NewTagHandler(NewService()))
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+other.Identity+"/tags", bytes.NewBufferString(`{"name":"越权"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer test-token")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
require.Equal(t, http.StatusNotFound, rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
func TestCronRegistrarCreatesCamelCaseIdentityDTO(t *testing.T) {
|
||||
database := newTestDB(t)
|
||||
require.NoError(t, database.Create(&models.SenlinAgentUser{Email: "owner@example.com", DisplayName: "Owner", PasswordHash: "hash"}).Error)
|
||||
project := models.SenlinAgentProject{OwnerID: 1, Name: "Alpha", Identifier: "ALPHA"}
|
||||
require.NoError(t, database.Create(&project).Error)
|
||||
router := newProjectWriteHandlerRouter(t, 1, NewCronHandler(NewService()))
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+project.Identity+"/cron-plans", bytes.NewBufferString(`{"title":"每日整理","schedule":"0 9 * * *","enabled":true,"nextRunAt":"2026-07-22T08:00:00Z"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer test-token")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
|
||||
var plan models.SenlinAgentCronPlan
|
||||
require.NoError(t, database.Where("project_id = ?", project.ID).First(&plan).Error)
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, plan.Identity, payload["id"])
|
||||
require.Equal(t, project.Identity, payload["projectId"])
|
||||
require.Equal(t, "2026-07-22T08:00:00Z", payload["nextRunAt"])
|
||||
require.NotContains(t, payload, "ProjectID")
|
||||
}
|
||||
|
||||
func newProjectWriteHandlerRouter(t *testing.T, currentUserID uint, registrar httpx.RouteRegistrar) *gin.Engine {
|
||||
t.Helper()
|
||||
return httpx.NewProtectedRouter(
|
||||
config.Config{Env: "test"},
|
||||
func(string) (uint, error) { return currentUserID, nil },
|
||||
registrar,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user