test(server): add integration tests for task defaults and file_ids
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -36,6 +36,33 @@ type taskListItem struct {
|
|||||||
AppID int64 `json:"app_id"`
|
AppID int64 `json:"app_id"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
SlurmJobID *int32 `json:"slurm_job_id"`
|
SlurmJobID *int32 `json:"slurm_job_id"`
|
||||||
|
Partition string `json:"partition,omitempty"`
|
||||||
|
Cpus *int32 `json:"cpus,omitempty"`
|
||||||
|
MemoryPerNode *int64 `json:"memory_per_node,omitempty"`
|
||||||
|
MemoryPerCpu *int64 `json:"memory_per_cpu,omitempty"`
|
||||||
|
TimeLimit *int32 `json:"time_limit,omitempty"`
|
||||||
|
QOS *string `json:"qos,omitempty"`
|
||||||
|
JobName *string `json:"job_name,omitempty"`
|
||||||
|
Nodes *string `json:"nodes,omitempty"`
|
||||||
|
Tasks *int32 `json:"tasks,omitempty"`
|
||||||
|
CpusPerTask *int32 `json:"cpus_per_task,omitempty"`
|
||||||
|
Constraints *string `json:"constraints,omitempty"`
|
||||||
|
Reservation *string `json:"reservation,omitempty"`
|
||||||
|
Account *string `json:"account,omitempty"`
|
||||||
|
Nice *int32 `json:"nice,omitempty"`
|
||||||
|
MailType *string `json:"mail_type,omitempty"`
|
||||||
|
MailUser *string `json:"mail_user,omitempty"`
|
||||||
|
StandardOutput *string `json:"standard_output,omitempty"`
|
||||||
|
StandardError *string `json:"standard_error,omitempty"`
|
||||||
|
StandardInput *string `json:"standard_input,omitempty"`
|
||||||
|
RequiredNodes *string `json:"required_nodes,omitempty"`
|
||||||
|
ExcludedNodes *string `json:"excluded_nodes,omitempty"`
|
||||||
|
BeginTime *int64 `json:"begin_time,omitempty"`
|
||||||
|
Deadline *int64 `json:"deadline,omitempty"`
|
||||||
|
Array *string `json:"array,omitempty"`
|
||||||
|
Dependency *string `json:"dependency,omitempty"`
|
||||||
|
Requeue *bool `json:"requeue,omitempty"`
|
||||||
|
KillOnNodeFail *bool `json:"kill_on_node_fail,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// taskSendReq sends an HTTP request via the test env and returns the response.
|
// taskSendReq sends an HTTP request via the test env and returns the response.
|
||||||
@@ -259,3 +286,233 @@ func TestIntegration_Task_Validation(t *testing.T) {
|
|||||||
t.Error("expected non-empty error message")
|
t.Error("expected non-empty error message")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIntegration_Task_WithSchedulingParams(t *testing.T) {
|
||||||
|
env := testenv.NewTestEnv(t)
|
||||||
|
|
||||||
|
appID, err := env.CreateApp("sched-param-app", "#!/bin/bash\necho hello", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := fmt.Sprintf(`{
|
||||||
|
"app_id": %d,
|
||||||
|
"task_name": "sched-task",
|
||||||
|
"values": {},
|
||||||
|
"file_ids": [],
|
||||||
|
"partition": "gpu",
|
||||||
|
"cpus": 16,
|
||||||
|
"memory_per_node": 32768,
|
||||||
|
"time_limit": 120
|
||||||
|
}`, appID)
|
||||||
|
|
||||||
|
resp := taskSendReq(t, env, http.MethodPost, "/api/v1/tasks", body)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusCreated {
|
||||||
|
b, _ := io.ReadAll(resp.Body)
|
||||||
|
t.Fatalf("expected 201, got %d: %s", resp.StatusCode, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed := taskParseResp(t, resp)
|
||||||
|
if !parsed.Success {
|
||||||
|
t.Fatalf("expected success=true, got error: %s", parsed.Error)
|
||||||
|
}
|
||||||
|
var data taskCreateData
|
||||||
|
if err := json.Unmarshal(parsed.Data, &data); err != nil {
|
||||||
|
t.Fatalf("unmarshal create data: %v", err)
|
||||||
|
}
|
||||||
|
if data.ID == 0 {
|
||||||
|
t.Fatal("expected non-zero task ID")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntegration_Task_ListWithScheduling(t *testing.T) {
|
||||||
|
env := testenv.NewTestEnv(t)
|
||||||
|
|
||||||
|
appID, err := env.CreateApp("sched-list-app", "#!/bin/bash\necho hello", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := fmt.Sprintf(`{
|
||||||
|
"app_id": %d,
|
||||||
|
"task_name": "sched-list-task",
|
||||||
|
"values": {},
|
||||||
|
"file_ids": [],
|
||||||
|
"partition": "gpu",
|
||||||
|
"cpus": 16,
|
||||||
|
"memory_per_node": 32768,
|
||||||
|
"time_limit": 120
|
||||||
|
}`, appID)
|
||||||
|
|
||||||
|
createResp := taskSendReq(t, env, http.MethodPost, "/api/v1/tasks", body)
|
||||||
|
defer createResp.Body.Close()
|
||||||
|
|
||||||
|
if createResp.StatusCode != http.StatusCreated {
|
||||||
|
b, _ := io.ReadAll(createResp.Body)
|
||||||
|
t.Fatalf("expected 201 creating task, got %d: %s", createResp.StatusCode, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
createParsed := taskParseResp(t, createResp)
|
||||||
|
var createData taskCreateData
|
||||||
|
if err := json.Unmarshal(createParsed.Data, &createData); err != nil {
|
||||||
|
t.Fatalf("unmarshal create data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
listResp := taskSendReq(t, env, http.MethodGet, "/api/v1/tasks", "")
|
||||||
|
defer listResp.Body.Close()
|
||||||
|
|
||||||
|
if listResp.StatusCode != http.StatusOK {
|
||||||
|
b, _ := io.ReadAll(listResp.Body)
|
||||||
|
t.Fatalf("expected 200 listing tasks, got %d: %s", listResp.StatusCode, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
listParsed := taskParseResp(t, listResp)
|
||||||
|
var listData taskListData
|
||||||
|
if err := json.Unmarshal(listParsed.Data, &listData); err != nil {
|
||||||
|
t.Fatalf("unmarshal list data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var found *taskListItem
|
||||||
|
for i := range listData.Items {
|
||||||
|
if listData.Items[i].ID == createData.ID {
|
||||||
|
found = &listData.Items[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found == nil {
|
||||||
|
t.Fatalf("task %d not found in list", createData.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if found.Partition != "gpu" {
|
||||||
|
t.Errorf("expected partition=gpu, got %q", found.Partition)
|
||||||
|
}
|
||||||
|
if found.Cpus == nil || *found.Cpus != 16 {
|
||||||
|
t.Errorf("expected cpus=16, got %v", found.Cpus)
|
||||||
|
}
|
||||||
|
if found.MemoryPerNode == nil || *found.MemoryPerNode != 32768 {
|
||||||
|
t.Errorf("expected memory_per_node=32768, got %v", found.MemoryPerNode)
|
||||||
|
}
|
||||||
|
if found.TimeLimit == nil || *found.TimeLimit != 120 {
|
||||||
|
t.Errorf("expected time_limit=120, got %v", found.TimeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntegration_Task_PartialScheduling(t *testing.T) {
|
||||||
|
env := testenv.NewTestEnv(t)
|
||||||
|
|
||||||
|
appID, err := env.CreateApp("partial-sched-app", "#!/bin/bash\necho hello", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := fmt.Sprintf(`{
|
||||||
|
"app_id": %d,
|
||||||
|
"task_name": "partial-sched-task",
|
||||||
|
"values": {},
|
||||||
|
"file_ids": [],
|
||||||
|
"partition": "gpu",
|
||||||
|
"cpus": 8
|
||||||
|
}`, appID)
|
||||||
|
|
||||||
|
createResp := taskSendReq(t, env, http.MethodPost, "/api/v1/tasks", body)
|
||||||
|
defer createResp.Body.Close()
|
||||||
|
|
||||||
|
if createResp.StatusCode != http.StatusCreated {
|
||||||
|
b, _ := io.ReadAll(createResp.Body)
|
||||||
|
t.Fatalf("expected 201, got %d: %s", createResp.StatusCode, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
createParsed := taskParseResp(t, createResp)
|
||||||
|
var createData taskCreateData
|
||||||
|
if err := json.Unmarshal(createParsed.Data, &createData); err != nil {
|
||||||
|
t.Fatalf("unmarshal create data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
listResp := taskSendReq(t, env, http.MethodGet, "/api/v1/tasks", "")
|
||||||
|
defer listResp.Body.Close()
|
||||||
|
|
||||||
|
listParsed := taskParseResp(t, listResp)
|
||||||
|
var listData taskListData
|
||||||
|
if err := json.Unmarshal(listParsed.Data, &listData); err != nil {
|
||||||
|
t.Fatalf("unmarshal list data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var found *taskListItem
|
||||||
|
for i := range listData.Items {
|
||||||
|
if listData.Items[i].ID == createData.ID {
|
||||||
|
found = &listData.Items[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found == nil {
|
||||||
|
t.Fatalf("task %d not found in list", createData.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if found.Partition != "gpu" {
|
||||||
|
t.Errorf("expected partition=gpu, got %q", found.Partition)
|
||||||
|
}
|
||||||
|
if found.Cpus == nil || *found.Cpus != 8 {
|
||||||
|
t.Errorf("expected cpus=8, got %v", found.Cpus)
|
||||||
|
}
|
||||||
|
if found.MemoryPerNode != nil {
|
||||||
|
t.Errorf("expected memory_per_node=nil, got %v", found.MemoryPerNode)
|
||||||
|
}
|
||||||
|
if found.TimeLimit != nil {
|
||||||
|
t.Errorf("expected time_limit=nil, got %v", found.TimeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntegration_Task_BackwardCompat(t *testing.T) {
|
||||||
|
env := testenv.NewTestEnv(t)
|
||||||
|
|
||||||
|
appID, err := env.CreateApp("compat-app", "#!/bin/bash\necho hello", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
taskID := taskCreateViaAPI(t, env, appID, "compat-task")
|
||||||
|
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
listResp := taskSendReq(t, env, http.MethodGet, "/api/v1/tasks", "")
|
||||||
|
defer listResp.Body.Close()
|
||||||
|
|
||||||
|
listParsed := taskParseResp(t, listResp)
|
||||||
|
var listData taskListData
|
||||||
|
if err := json.Unmarshal(listParsed.Data, &listData); err != nil {
|
||||||
|
t.Fatalf("unmarshal list data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var found *taskListItem
|
||||||
|
for i := range listData.Items {
|
||||||
|
if listData.Items[i].ID == taskID {
|
||||||
|
found = &listData.Items[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found == nil {
|
||||||
|
t.Fatalf("task %d not found in list", taskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if found.Partition != "" {
|
||||||
|
t.Errorf("expected empty partition, got %q", found.Partition)
|
||||||
|
}
|
||||||
|
if found.Cpus != nil {
|
||||||
|
t.Errorf("expected nil cpus, got %v", found.Cpus)
|
||||||
|
}
|
||||||
|
if found.MemoryPerNode != nil {
|
||||||
|
t.Errorf("expected nil memory_per_node, got %v", found.MemoryPerNode)
|
||||||
|
}
|
||||||
|
if found.TimeLimit != nil {
|
||||||
|
t.Errorf("expected nil time_limit, got %v", found.TimeLimit)
|
||||||
|
}
|
||||||
|
if found.QOS != nil {
|
||||||
|
t.Errorf("expected nil qos, got %v", found.QOS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user