feat(bookmark): 添加文件夹树结构导入导出接口 新增 `/bookmarks/v1/folder/serial` 接口,支持文件夹树结构的压缩导出与解压导入。 同时完善了相关响应结构体定义,如 ImportResponse 等。 refactor(bookmark): 重命名配置文件并调整字段命名 将 `config/api.yaml` 重命名为 `config/bookmark.yaml`,并统一将 parent_path_id 字段 更名为 parent_id。此外,更新 API 鉴权头名称为 X-BookMark-Token。 feat(bookmark): 实现文件夹挂载管理功能 新增以下三个接口用于管理文件夹挂载: - GET `/bookmarks/v1/folder/{id}/mount` 获取挂载信息 - POST `/bookmarks/v1/folder/{id}/mount` 挂载文件夹 - DELETE `/bookmarks/v1/folder/{id}/mount` 取消挂载 新增相关结构体定义:MountResponse、MountInfo。 feat(vfs): 初始化虚拟文件系统 API 配置 新增 `config/vfs.yaml` 和 `config/vfs_cfg.yaml` 配置文件,定义 VFS 相关接口和代码生成规则。 接口包括文件/目录的创建、读取、更新和删除操作,并引入新的安全头 X-VFS-Token。 chore(config): 忽略 data 目录并更新生成路径 .gitignore 中新增忽略 data/ 目录。同时更新 bookmark 和 vfs 的代码生成输出路径分别为 `./gen/bookmarks/gen.go` 和 `./gen/vfs/gen.go`。 chore(deps): 引入 casbin、gopsutil 等依赖库 go.mod 中新增 casbin 权限控制、gopsutil 系统监控等相关依赖。 ```
311 lines
9.7 KiB
Go
311 lines
9.7 KiB
Go
// internal/handlers/bookmark_test.go
|
|
package handlers_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/bookmarks"
|
|
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/handlers"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type BookmarkTestSuite struct {
|
|
suite.Suite
|
|
server *gin.Engine
|
|
bookmarks *handlers.BookMarksImpl
|
|
testDBPath string
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) SetupSuite() {
|
|
// 使用内存数据库进行测试
|
|
suite.testDBPath = ":memory:"
|
|
|
|
// 设置Gin为测试模式
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
var err error
|
|
suite.bookmarks, err = handlers.NewBookMarks(suite.testDBPath)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
suite.server = gin.New()
|
|
api.RegisterHandlers(suite.server, suite.bookmarks)
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestCreateBookmark() {
|
|
// 准备测试数据
|
|
detail := "Test detail"
|
|
description := "Test description"
|
|
link := "https://example.com"
|
|
request := api.BookmarkRequest{
|
|
Name: "Test Bookmark",
|
|
Detail: &detail,
|
|
Description: &description,
|
|
Link: &link,
|
|
}
|
|
|
|
// 将请求转换为JSON
|
|
jsonData, _ := json.Marshal(request)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/data", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// 发送请求
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
// 验证响应
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
|
|
var response api.BookmarkResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), request.Name, response.Name)
|
|
assert.Equal(suite.T(), *request.Detail, *response.Detail)
|
|
assert.Equal(suite.T(), *request.Description, *response.Description)
|
|
assert.Equal(suite.T(), int64(1), response.ParentId) // 默认根目录
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestGetBookmark() {
|
|
// 先创建一个书签
|
|
detail := "Test detail for get"
|
|
description := "Test description for get"
|
|
link := "https://example.com/get"
|
|
request := api.BookmarkRequest{
|
|
Name: "Test Get Bookmark",
|
|
Detail: &detail,
|
|
Description: &description,
|
|
Link: &link,
|
|
}
|
|
|
|
jsonData, _ := json.Marshal(request)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/data", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
var createdBookmark api.BookmarkResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &createdBookmark)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
// 获取创建的书签
|
|
req, _ = http.NewRequest("GET", fmt.Sprintf("/bookmarks/v1/data/%d", createdBookmark.Id), nil)
|
|
resp = httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusOK, resp.Code)
|
|
|
|
var response api.BookmarkResponse
|
|
err = json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), createdBookmark.Id, response.Id)
|
|
assert.Equal(suite.T(), request.Name, response.Name)
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestCreateFolder() {
|
|
// 准备测试数据
|
|
request := api.FolderRequest{
|
|
Name: "Test Folder",
|
|
}
|
|
|
|
// 将请求转换为JSON
|
|
jsonData, _ := json.Marshal(request)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/folder", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// 发送请求
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
// 验证响应
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
|
|
var response api.FolderResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), request.Name, response.Name)
|
|
assert.Equal(suite.T(), int64(1), response.ParentId) // 默认根目录
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestGetFolderInfo() {
|
|
// 先创建一个文件夹
|
|
request := api.FolderRequest{
|
|
Name: "Test Get Folder",
|
|
}
|
|
|
|
jsonData, _ := json.Marshal(request)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/folder", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
var createdFolder api.FolderResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &createdFolder)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
// 获取创建的文件夹信息
|
|
req, _ = http.NewRequest("GET", fmt.Sprintf("/bookmarks/v1/folder/%d", createdFolder.Id), nil)
|
|
resp = httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusOK, resp.Code)
|
|
|
|
var response api.FolderResponse
|
|
err = json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), createdFolder.Id, response.Id)
|
|
assert.Equal(suite.T(), request.Name, response.Name)
|
|
assert.Equal(suite.T(), 0, response.BookmarkCount)
|
|
assert.Equal(suite.T(), 0, response.SubFolderCount)
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestGetFolderContent() {
|
|
// 创建一个文件夹
|
|
folderRequest := api.FolderRequest{
|
|
Name: "Content Test Folder",
|
|
}
|
|
|
|
folderJson, _ := json.Marshal(folderRequest)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/folder", bytes.NewBuffer(folderJson))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
var createdFolder api.FolderResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &createdFolder)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
// 在该文件夹中创建一个书签
|
|
detail := "Bookmark in folder"
|
|
description := "Test bookmark in folder"
|
|
link := "https://example.com/folder"
|
|
bookmarkRequest := api.BookmarkRequest{
|
|
Name: "Folder Bookmark",
|
|
Detail: &detail,
|
|
Description: &description,
|
|
Link: &link,
|
|
ParentId: &createdFolder.Id,
|
|
}
|
|
|
|
bookmarkJson, _ := json.Marshal(bookmarkRequest)
|
|
req, _ = http.NewRequest("POST", "/bookmarks/v1/data", bytes.NewBuffer(bookmarkJson))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp = httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
|
|
// 获取文件夹内容
|
|
req, _ = http.NewRequest("GET", fmt.Sprintf("/bookmarks/v1/folder/%d/content", createdFolder.Id), nil)
|
|
resp = httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusOK, resp.Code)
|
|
|
|
var response api.FolderContentResponse
|
|
err = json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Len(suite.T(), response.Bookmarks, 1)
|
|
assert.Equal(suite.T(), "Folder Bookmark", response.Bookmarks[0].Name)
|
|
assert.Len(suite.T(), response.SubFolders, 0)
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestUpdateBookmark() {
|
|
// 先创建一个书签
|
|
detail := "Original detail"
|
|
description := "Original description"
|
|
link := "https://example.com/original"
|
|
request := api.BookmarkRequest{
|
|
Name: "Original Bookmark",
|
|
Detail: &detail,
|
|
Description: &description,
|
|
Link: &link,
|
|
}
|
|
|
|
jsonData, _ := json.Marshal(request)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/data", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
var createdBookmark api.BookmarkResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &createdBookmark)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
// 更新书签
|
|
newName := "Updated Bookmark"
|
|
newDetail := "Updated detail"
|
|
updateRequest := api.BookmarkRequest{
|
|
Name: newName,
|
|
Detail: &newDetail,
|
|
}
|
|
|
|
updateJson, _ := json.Marshal(updateRequest)
|
|
req, _ = http.NewRequest("PUT", fmt.Sprintf("/bookmarks/v1/data/%d", createdBookmark.Id), bytes.NewBuffer(updateJson))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp = httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusOK, resp.Code)
|
|
|
|
var response api.BookmarkResponse
|
|
err = json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), newName, response.Name)
|
|
assert.Equal(suite.T(), newDetail, *response.Detail)
|
|
// FIXME 确保更新时间发生了变化 时钟粒度不足
|
|
// assert.True(suite.T(), response.UpdatedAt.After(response.CreatedAt) || response.UpdatedAt.Equal(response.CreatedAt))
|
|
}
|
|
|
|
func (suite *BookmarkTestSuite) TestUpdateFolder() {
|
|
// 先创建一个文件夹
|
|
request := api.FolderRequest{
|
|
Name: "Original Folder",
|
|
}
|
|
|
|
jsonData, _ := json.Marshal(request)
|
|
req, _ := http.NewRequest("POST", "/bookmarks/v1/folder", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp := httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusCreated, resp.Code)
|
|
var createdFolder api.FolderResponse
|
|
err := json.Unmarshal(resp.Body.Bytes(), &createdFolder)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
// 更新文件夹
|
|
newName := "Updated Folder"
|
|
updateRequest := api.FolderRequest{
|
|
Name: newName,
|
|
}
|
|
|
|
updateJson, _ := json.Marshal(updateRequest)
|
|
req, _ = http.NewRequest("PUT", fmt.Sprintf("/bookmarks/v1/folder/%d", createdFolder.Id), bytes.NewBuffer(updateJson))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp = httptest.NewRecorder()
|
|
suite.server.ServeHTTP(resp, req)
|
|
|
|
assert.Equal(suite.T(), http.StatusOK, resp.Code)
|
|
|
|
var response api.FolderResponse
|
|
err = json.Unmarshal(resp.Body.Bytes(), &response)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), newName, response.Name)
|
|
// 确保更新时间发生了变化
|
|
assert.True(suite.T(), response.UpdatedAt.After(response.CreatedAt) || response.UpdatedAt.Equal(response.CreatedAt))
|
|
}
|
|
|
|
func TestBookmarkTestSuite(t *testing.T) {
|
|
suite.Run(t, new(BookmarkTestSuite))
|
|
}
|