refactor(api): expose project identity DTOs
This commit is contained in:
@@ -41,6 +41,146 @@ func TestCreateProjectHandlerPersistsMetadata(t *testing.T) {
|
||||
require.Equal(t, "compass", project.Icon)
|
||||
require.Equal(t, "#165DFF", project.Background)
|
||||
require.Equal(t, "RSS exploration workspace", project.Description)
|
||||
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, project.Identity, payload["id"])
|
||||
require.ElementsMatch(t, []string{"id", "name", "identifier", "icon", "background", "description"}, mapKeys(payload))
|
||||
}
|
||||
|
||||
func TestCreateProjectReturnsChineseInvalidRequestEnvelope(t *testing.T) {
|
||||
router, _, _ := newProjectsHandlerTestRouter(t)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/projects", bytes.NewReader([]byte(`{"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.StatusBadRequest, rec.Code)
|
||||
var payload httpx.ErrorEnvelope
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, "invalid_request", payload.Error.Code)
|
||||
require.Equal(t, "请求参数无效", payload.Error.Message)
|
||||
}
|
||||
|
||||
func TestCreateProjectReturnsConflictForDuplicateIdentifier(t *testing.T) {
|
||||
router, project, _ := newProjectsHandlerTestRouter(t)
|
||||
body, err := json.Marshal(gin.H{"name": "Another", "identifier": project.Identifier})
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/projects", bytes.NewReader(body))
|
||||
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.StatusConflict, rec.Code)
|
||||
var payload httpx.ErrorEnvelope
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, "conflict", payload.Error.Code)
|
||||
require.Equal(t, "项目标识已存在", payload.Error.Message)
|
||||
}
|
||||
|
||||
func TestGetProjectUsesOwnedIdentityDTO(t *testing.T) {
|
||||
router, project, _ := newProjectsHandlerTestRouter(t)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/projects/"+project.Identity, nil)
|
||||
req.Header.Set("Authorization", "Bearer test-token")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, project.Identity, payload["id"])
|
||||
require.ElementsMatch(t, []string{"id", "name", "identifier", "icon", "background", "description"}, mapKeys(payload))
|
||||
}
|
||||
|
||||
func TestListProjectsUsesIdentityDTO(t *testing.T) {
|
||||
router, project, _ := newProjectsHandlerTestRouter(t)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/projects", nil)
|
||||
req.Header.Set("Authorization", "Bearer test-token")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
var payload []map[string]any
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Len(t, payload, 1)
|
||||
require.Equal(t, project.Identity, payload[0]["id"])
|
||||
require.ElementsMatch(t, []string{"id", "name", "identifier", "icon", "background", "description"}, mapKeys(payload[0]))
|
||||
}
|
||||
|
||||
func TestUpdateProject(t *testing.T) {
|
||||
t.Run("updates the allowed project fields by identity", func(t *testing.T) {
|
||||
router, project, _ := newProjectsHandlerTestRouter(t)
|
||||
body, err := json.Marshal(gin.H{
|
||||
"name": "Alpha Next",
|
||||
"identifier": "ALPHA-NEXT",
|
||||
"icon": "tree",
|
||||
"background": "#0FC6C2",
|
||||
"description": "更新后的项目说明",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodPatch, "/api/v1/projects/"+project.Identity, bytes.NewReader(body))
|
||||
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.StatusNoContent, rec.Code)
|
||||
require.Empty(t, rec.Body.String())
|
||||
var updated models.SenlinAgentProject
|
||||
require.NoError(t, models.DBService.First(&updated, project.ID).Error)
|
||||
require.Equal(t, "Alpha Next", updated.Name)
|
||||
require.Equal(t, "ALPHA-NEXT", updated.Identifier)
|
||||
require.Equal(t, "tree", updated.Icon)
|
||||
require.Equal(t, "#0FC6C2", updated.Background)
|
||||
require.Equal(t, "更新后的项目说明", updated.Description)
|
||||
})
|
||||
|
||||
t.Run("returns not found when the identity is not owned by the current user", func(t *testing.T) {
|
||||
router, _, _ := newProjectsHandlerTestRouter(t)
|
||||
other, err := NewService().CreateProject(2, "Other", "")
|
||||
require.NoError(t, err)
|
||||
body, err := json.Marshal(gin.H{"name": "不可见项目"})
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodPatch, "/api/v1/projects/"+other.Identity, bytes.NewReader(body))
|
||||
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)
|
||||
var payload httpx.ErrorEnvelope
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, "not_found", payload.Error.Code)
|
||||
require.Equal(t, "项目不存在", payload.Error.Message)
|
||||
})
|
||||
|
||||
t.Run("returns conflict for another owned project identifier", func(t *testing.T) {
|
||||
router, project, _ := newProjectsHandlerTestRouter(t)
|
||||
_, err := NewService().CreateProjectWithInput(1, CreateProjectRequest{Name: "Beta", Identifier: "BETA"})
|
||||
require.NoError(t, err)
|
||||
body, err := json.Marshal(gin.H{"identifier": "BETA"})
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodPatch, "/api/v1/projects/"+project.Identity, bytes.NewReader(body))
|
||||
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.StatusConflict, rec.Code)
|
||||
var payload httpx.ErrorEnvelope
|
||||
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
|
||||
require.Equal(t, "conflict", payload.Error.Code)
|
||||
require.Equal(t, "项目标识已存在", payload.Error.Message)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateTaskHandlerPersistsTask(t *testing.T) {
|
||||
@@ -175,3 +315,11 @@ func newProjectsHandlerTestRouter(t *testing.T) (*gin.Engine, *models.SenlinAgen
|
||||
)
|
||||
return router, project, storageDir
|
||||
}
|
||||
|
||||
func mapKeys(value map[string]any) []string {
|
||||
keys := make([]string, 0, len(value))
|
||||
for key := range value {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user