feat(testutil): add MockSlurm, MockMinIO, TestEnv and 37 integration tests
- mockminio: in-memory ObjectStorage with all 11 methods, thread-safe, SHA256 ETag, Range support - mockslurm: httptest server with 11 Slurm REST API endpoints, job eviction from active to history queue - testenv: one-line test environment factory (SQLite + MockSlurm + MockMinIO + all stores/services/handlers + httptest server) - integration tests: 37 tests covering Jobs(5), Cluster(5), App(6), Upload(5), File(4), Folder(4), Task(4), E2E(1) - no external dependencies, no existing files modified
This commit is contained in:
193
cmd/server/integration_folder_test.go
Normal file
193
cmd/server/integration_folder_test.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"gcy_hpc_server/internal/testutil/testenv"
|
||||
)
|
||||
|
||||
// folderData mirrors the FolderResponse DTO returned by the API.
|
||||
type folderData struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ParentID *int64 `json:"parent_id,omitempty"`
|
||||
Path string `json:"path"`
|
||||
FileCount int64 `json:"file_count"`
|
||||
SubFolderCount int64 `json:"subfolder_count"`
|
||||
}
|
||||
|
||||
// folderMessageData mirrors the delete endpoint response data structure.
|
||||
type folderMessageData struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// folderDoRequest marshals body and calls env.DoRequest.
|
||||
func folderDoRequest(env *testenv.TestEnv, method, path string, body interface{}) *http.Response {
|
||||
var r io.Reader
|
||||
if body != nil {
|
||||
b, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("folderDoRequest marshal: %v", err))
|
||||
}
|
||||
r = bytes.NewReader(b)
|
||||
}
|
||||
return env.DoRequest(method, path, r)
|
||||
}
|
||||
|
||||
// folderDecodeAll decodes the response and returns status, success, and raw data.
|
||||
func folderDecodeAll(env *testenv.TestEnv, resp *http.Response) (statusCode int, success bool, data json.RawMessage, err error) {
|
||||
statusCode = resp.StatusCode
|
||||
success, data, err = env.DecodeResponse(resp)
|
||||
return
|
||||
}
|
||||
|
||||
// folderSeed creates a folder via HTTP and returns its ID.
|
||||
func folderSeed(env *testenv.TestEnv, name string) int64 {
|
||||
body := map[string]interface{}{"name": name}
|
||||
resp := folderDoRequest(env, http.MethodPost, "/api/v1/files/folders", body)
|
||||
status, success, data, err := folderDecodeAll(env, resp)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("folderSeed decode: %v", err))
|
||||
}
|
||||
if status != http.StatusCreated {
|
||||
panic(fmt.Sprintf("folderSeed: expected 201, got %d", status))
|
||||
}
|
||||
if !success {
|
||||
panic("folderSeed: expected success=true")
|
||||
}
|
||||
var f folderData
|
||||
if err := json.Unmarshal(data, &f); err != nil {
|
||||
panic(fmt.Sprintf("folderSeed unmarshal: %v", err))
|
||||
}
|
||||
return f.ID
|
||||
}
|
||||
|
||||
// TestIntegration_Folder_Create verifies POST /api/v1/files/folders creates a folder.
|
||||
func TestIntegration_Folder_Create(t *testing.T) {
|
||||
env := testenv.NewTestEnv(t)
|
||||
|
||||
body := map[string]interface{}{"name": "test-folder-create"}
|
||||
resp := folderDoRequest(env, http.MethodPost, "/api/v1/files/folders", body)
|
||||
status, success, data, err := folderDecodeAll(env, resp)
|
||||
if err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if status != http.StatusCreated {
|
||||
t.Fatalf("expected status 201, got %d", status)
|
||||
}
|
||||
if !success {
|
||||
t.Fatal("expected success=true")
|
||||
}
|
||||
|
||||
var created folderData
|
||||
if err := json.Unmarshal(data, &created); err != nil {
|
||||
t.Fatalf("unmarshal created data: %v", err)
|
||||
}
|
||||
if created.ID <= 0 {
|
||||
t.Fatalf("expected positive id, got %d", created.ID)
|
||||
}
|
||||
if created.Name != "test-folder-create" {
|
||||
t.Fatalf("expected name=test-folder-create, got %s", created.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestIntegration_Folder_List verifies GET /api/v1/files/folders returns a list.
|
||||
func TestIntegration_Folder_List(t *testing.T) {
|
||||
env := testenv.NewTestEnv(t)
|
||||
|
||||
// Seed two folders.
|
||||
folderSeed(env, "list-folder-1")
|
||||
folderSeed(env, "list-folder-2")
|
||||
|
||||
resp := env.DoRequest(http.MethodGet, "/api/v1/files/folders", nil)
|
||||
status, success, data, err := folderDecodeAll(env, resp)
|
||||
if err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if status != http.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", status)
|
||||
}
|
||||
if !success {
|
||||
t.Fatal("expected success=true")
|
||||
}
|
||||
|
||||
var folders []folderData
|
||||
if err := json.Unmarshal(data, &folders); err != nil {
|
||||
t.Fatalf("unmarshal list data: %v", err)
|
||||
}
|
||||
if len(folders) < 2 {
|
||||
t.Fatalf("expected at least 2 folders, got %d", len(folders))
|
||||
}
|
||||
}
|
||||
|
||||
// TestIntegration_Folder_Get verifies GET /api/v1/files/folders/:id returns folder details.
|
||||
func TestIntegration_Folder_Get(t *testing.T) {
|
||||
env := testenv.NewTestEnv(t)
|
||||
id := folderSeed(env, "test-folder-get")
|
||||
|
||||
path := fmt.Sprintf("/api/v1/files/folders/%d", id)
|
||||
resp := env.DoRequest(http.MethodGet, path, nil)
|
||||
status, success, data, err := folderDecodeAll(env, resp)
|
||||
if err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if status != http.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", status)
|
||||
}
|
||||
if !success {
|
||||
t.Fatal("expected success=true")
|
||||
}
|
||||
|
||||
var f folderData
|
||||
if err := json.Unmarshal(data, &f); err != nil {
|
||||
t.Fatalf("unmarshal folder data: %v", err)
|
||||
}
|
||||
if f.ID != id {
|
||||
t.Fatalf("expected id=%d, got %d", id, f.ID)
|
||||
}
|
||||
if f.Name != "test-folder-get" {
|
||||
t.Fatalf("expected name=test-folder-get, got %s", f.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestIntegration_Folder_Delete verifies DELETE /api/v1/files/folders/:id removes a folder.
|
||||
func TestIntegration_Folder_Delete(t *testing.T) {
|
||||
env := testenv.NewTestEnv(t)
|
||||
id := folderSeed(env, "test-folder-delete")
|
||||
|
||||
path := fmt.Sprintf("/api/v1/files/folders/%d", id)
|
||||
resp := env.DoRequest(http.MethodDelete, path, nil)
|
||||
status, success, data, err := folderDecodeAll(env, resp)
|
||||
if err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if status != http.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", status)
|
||||
}
|
||||
if !success {
|
||||
t.Fatal("expected success=true")
|
||||
}
|
||||
|
||||
var msg folderMessageData
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
t.Fatalf("unmarshal message data: %v", err)
|
||||
}
|
||||
if msg.Message != "folder deleted" {
|
||||
t.Fatalf("expected message 'folder deleted', got %q", msg.Message)
|
||||
}
|
||||
|
||||
// Verify it's gone via GET → 404.
|
||||
getResp := env.DoRequest(http.MethodGet, path, nil)
|
||||
getStatus, getSuccess, _, _ := folderDecodeAll(env, getResp)
|
||||
if getStatus != http.StatusNotFound {
|
||||
t.Fatalf("expected status 404 after delete, got %d", getStatus)
|
||||
}
|
||||
if getSuccess {
|
||||
t.Fatal("expected success=false after delete")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user