feat(service): add GetJob fallback to SlurmDBD history and expand query params
GetJob now falls back to SlurmDBD history when active queue returns 404 or empty jobs. Expand JobHistoryQuery from 7 to 16 filter params (add SubmitTime, Cluster, Qos, Constraints, ExitCode, Node, Reservation, Groups, Wckey). Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gcy_hpc_server/internal/model"
|
||||
@@ -701,3 +702,157 @@ func TestJobService_GetJobHistory_ErrorLog(t *testing.T) {
|
||||
t.Error("expected error field in log entry")
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fallback to SlurmDBD history tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestGetJob_FallbackToHistory_Found(t *testing.T) {
|
||||
jobID := int32(198)
|
||||
name := "hist-job"
|
||||
ts := int64(1700000000)
|
||||
|
||||
client, cleanup := mockJobServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/slurm/v0.0.40/job/198":
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"errors": []map[string]interface{}{
|
||||
{
|
||||
"description": "Unable to query JobId=198",
|
||||
"error_number": float64(2017),
|
||||
"error": "Invalid job id specified",
|
||||
"source": "_handle_job_get",
|
||||
},
|
||||
},
|
||||
"jobs": []interface{}{},
|
||||
})
|
||||
case "/slurmdb/v0.0.40/job/198":
|
||||
resp := slurm.OpenapiSlurmdbdJobsResp{
|
||||
Jobs: slurm.JobList{
|
||||
{
|
||||
JobID: &jobID,
|
||||
Name: &name,
|
||||
State: &slurm.JobState{Current: []string{"COMPLETED"}},
|
||||
Time: &slurm.JobTime{Submission: &ts, Start: &ts, End: &ts},
|
||||
},
|
||||
},
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer cleanup()
|
||||
|
||||
svc := NewJobService(client, zap.NewNop())
|
||||
job, err := svc.GetJob(context.Background(), "198")
|
||||
if err != nil {
|
||||
t.Fatalf("GetJob: %v", err)
|
||||
}
|
||||
if job == nil {
|
||||
t.Fatal("expected job, got nil")
|
||||
}
|
||||
if job.JobID != 198 {
|
||||
t.Errorf("expected JobID 198, got %d", job.JobID)
|
||||
}
|
||||
if job.Name != "hist-job" {
|
||||
t.Errorf("expected Name hist-job, got %s", job.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJob_FallbackToHistory_NotFound(t *testing.T) {
|
||||
client, cleanup := mockJobServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
defer cleanup()
|
||||
|
||||
svc := NewJobService(client, zap.NewNop())
|
||||
job, err := svc.GetJob(context.Background(), "999")
|
||||
if err != nil {
|
||||
t.Fatalf("GetJob: %v", err)
|
||||
}
|
||||
if job != nil {
|
||||
t.Errorf("expected nil, got %+v", job)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJob_FallbackToHistory_HistoryError(t *testing.T) {
|
||||
client, cleanup := mockJobServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/slurm/v0.0.40/job/500":
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"errors": []map[string]interface{}{
|
||||
{
|
||||
"description": "Unable to query JobId=500",
|
||||
"error_number": float64(2017),
|
||||
"error": "Invalid job id specified",
|
||||
"source": "_handle_job_get",
|
||||
},
|
||||
},
|
||||
"jobs": []interface{}{},
|
||||
})
|
||||
case "/slurmdb/v0.0.40/job/500":
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"errors":[{"error":"db error"}]}`))
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer cleanup()
|
||||
|
||||
svc := NewJobService(client, zap.NewNop())
|
||||
job, err := svc.GetJob(context.Background(), "500")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if job != nil {
|
||||
t.Errorf("expected nil job, got %+v", job)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "get job history") {
|
||||
t.Errorf("expected error to contain 'get job history', got %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJob_FallbackToHistory_EmptyHistory(t *testing.T) {
|
||||
client, cleanup := mockJobServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/slurm/v0.0.40/job/777":
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"errors": []map[string]interface{}{
|
||||
{
|
||||
"description": "Unable to query JobId=777",
|
||||
"error_number": float64(2017),
|
||||
"error": "Invalid job id specified",
|
||||
"source": "_handle_job_get",
|
||||
},
|
||||
},
|
||||
"jobs": []interface{}{},
|
||||
})
|
||||
case "/slurmdb/v0.0.40/job/777":
|
||||
resp := slurm.OpenapiSlurmdbdJobsResp{Jobs: slurm.JobList{}}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer cleanup()
|
||||
|
||||
svc := NewJobService(client, zap.NewNop())
|
||||
job, err := svc.GetJob(context.Background(), "777")
|
||||
if err != nil {
|
||||
t.Fatalf("GetJob: %v", err)
|
||||
}
|
||||
if job != nil {
|
||||
t.Errorf("expected nil, got %+v", job)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user