package model import ( "encoding/json" "reflect" "testing" ) func TestSubmitJobRequest_SchedulingFields(t *testing.T) { payload := `{ "script": "#!/bin/bash\necho hello", "work_dir": "/tmp/work", "partition": "gpu", "qos": "high", "cpus": 16, "memory": "4GB", "time_limit": "60", "job_name": "test-job", "environment": {"PATH": "/usr/bin"}, "memory_per_node": 32768, "memory_per_cpu": 4096, "nodes": "2", "tasks": 4, "cpus_per_task": 8, "constraints": "gpu&a100", "reservation": "res-001", "account": "project-x", "nice": 100, "mail_type": "END,FAIL", "mail_user": "user@example.com", "standard_output": "/tmp/out_%j.log", "standard_error": "/tmp/err_%j.log", "standard_input": "/tmp/input.txt", "required_nodes": "node[01-03]", "excluded_nodes": "node04", "begin_time": 1700000000, "deadline": 1700086400, "array": "1-100%10", "dependency": "afterok:12345", "requeue": true, "kill_on_node_fail": false }` var req SubmitJobRequest if err := json.Unmarshal([]byte(payload), &req); err != nil { t.Fatalf("unmarshal SubmitJobRequest: %v", err) } // Existing fields if req.Script != "#!/bin/bash\necho hello" { t.Errorf("Script = %q, want script content", req.Script) } if req.WorkDir != "/tmp/work" { t.Errorf("WorkDir = %q, want /tmp/work", req.WorkDir) } if req.Partition != "gpu" { t.Errorf("Partition = %q, want gpu", req.Partition) } if req.QOS != "high" { t.Errorf("QOS = %q, want high", req.QOS) } if req.CPUs != 16 { t.Errorf("CPUs = %d, want 16", req.CPUs) } if req.Memory != "4GB" { t.Errorf("Memory = %q, want 4GB", req.Memory) } if req.TimeLimit != "60" { t.Errorf("TimeLimit = %q, want 60", req.TimeLimit) } if req.JobName != "test-job" { t.Errorf("JobName = %q, want test-job", req.JobName) } if v, ok := req.Environment["PATH"]; !ok || v != "/usr/bin" { t.Errorf("Environment[PATH] = %q, want /usr/bin", v) } // New scheduling fields if req.MemoryPerNode == nil || *req.MemoryPerNode != 32768 { t.Errorf("MemoryPerNode = %v, want 32768", req.MemoryPerNode) } if req.MemoryPerCpu == nil || *req.MemoryPerCpu != 4096 { t.Errorf("MemoryPerCpu = %v, want 4096", req.MemoryPerCpu) } if req.Nodes == nil || *req.Nodes != "2" { t.Errorf("Nodes = %v, want 2", req.Nodes) } if req.Tasks == nil || *req.Tasks != 4 { t.Errorf("Tasks = %v, want 4", req.Tasks) } if req.CpusPerTask == nil || *req.CpusPerTask != 8 { t.Errorf("CpusPerTask = %v, want 8", req.CpusPerTask) } if req.Constraints == nil || *req.Constraints != "gpu&a100" { t.Errorf("Constraints = %v, want gpu&a100", req.Constraints) } if req.Reservation == nil || *req.Reservation != "res-001" { t.Errorf("Reservation = %v, want res-001", req.Reservation) } if req.Account == nil || *req.Account != "project-x" { t.Errorf("Account = %v, want project-x", req.Account) } if req.Nice == nil || *req.Nice != 100 { t.Errorf("Nice = %v, want 100", req.Nice) } if req.MailType == nil || *req.MailType != "END,FAIL" { t.Errorf("MailType = %v, want END,FAIL", req.MailType) } if req.MailUser == nil || *req.MailUser != "user@example.com" { t.Errorf("MailUser = %v, want user@example.com", req.MailUser) } if req.StandardOutput == nil || *req.StandardOutput != "/tmp/out_%j.log" { t.Errorf("StandardOutput = %v, want /tmp/out_%%j.log", req.StandardOutput) } if req.StandardError == nil || *req.StandardError != "/tmp/err_%j.log" { t.Errorf("StandardError = %v, want /tmp/err_%%j.log", req.StandardError) } if req.StandardInput == nil || *req.StandardInput != "/tmp/input.txt" { t.Errorf("StandardInput = %v, want /tmp/input.txt", req.StandardInput) } if req.RequiredNodes == nil || *req.RequiredNodes != "node[01-03]" { t.Errorf("RequiredNodes = %v, want node[01-03]", req.RequiredNodes) } if req.ExcludedNodes == nil || *req.ExcludedNodes != "node04" { t.Errorf("ExcludedNodes = %v, want node04", req.ExcludedNodes) } if req.BeginTime == nil || *req.BeginTime != 1700000000 { t.Errorf("BeginTime = %v, want 1700000000", req.BeginTime) } if req.Deadline == nil || *req.Deadline != 1700086400 { t.Errorf("Deadline = %v, want 1700086400", req.Deadline) } if req.Array == nil || *req.Array != "1-100%10" { t.Errorf("Array = %v, want 1-100%%10", req.Array) } if req.Dependency == nil || *req.Dependency != "afterok:12345" { t.Errorf("Dependency = %v, want afterok:12345", req.Dependency) } if req.Requeue == nil || *req.Requeue != true { t.Errorf("Requeue = %v, want true", req.Requeue) } if req.KillOnNodeFail == nil || *req.KillOnNodeFail != false { t.Errorf("KillOnNodeFail = %v, want false", req.KillOnNodeFail) } } func TestSubmitJobRequest_BackwardCompat(t *testing.T) { // Minimal JSON — only required fields payload := `{"script": "#!/bin/bash\necho hello", "work_dir": "/tmp"}` var req SubmitJobRequest if err := json.Unmarshal([]byte(payload), &req); err != nil { t.Fatalf("unmarshal minimal SubmitJobRequest: %v", err) } // Required fields present if req.Script != "#!/bin/bash\necho hello" { t.Errorf("Script = %q, want script content", req.Script) } if req.WorkDir != "/tmp" { t.Errorf("WorkDir = %q, want /tmp", req.WorkDir) } // Old fields exist with zero values if req.Memory != "" { t.Errorf("Memory = %q, want empty", req.Memory) } if req.Environment != nil { t.Errorf("Environment = %v, want nil", req.Environment) } // All new scheduling fields are nil assertNil := func(name string, val any) { if !reflect.ValueOf(val).IsNil() { t.Errorf("%s = %v, want nil", name, val) } } assertNil("MemoryPerNode", req.MemoryPerNode) assertNil("MemoryPerCpu", req.MemoryPerCpu) assertNil("Nodes", req.Nodes) assertNil("Tasks", req.Tasks) assertNil("CpusPerTask", req.CpusPerTask) assertNil("Constraints", req.Constraints) assertNil("Reservation", req.Reservation) assertNil("Account", req.Account) assertNil("Nice", req.Nice) assertNil("MailType", req.MailType) assertNil("MailUser", req.MailUser) assertNil("StandardOutput", req.StandardOutput) assertNil("StandardError", req.StandardError) assertNil("StandardInput", req.StandardInput) assertNil("RequiredNodes", req.RequiredNodes) assertNil("ExcludedNodes", req.ExcludedNodes) assertNil("BeginTime", req.BeginTime) assertNil("Deadline", req.Deadline) assertNil("Array", req.Array) assertNil("Dependency", req.Dependency) assertNil("Requeue", req.Requeue) assertNil("KillOnNodeFail", req.KillOnNodeFail) }