GetJobs now accepts page/page_size query parameters and returns JobListResponse instead of raw array. Uses in-memory pagination matching GetJobHistory pattern. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
909 lines
25 KiB
Go
909 lines
25 KiB
Go
package handler
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"gcy_hpc_server/internal/service"
|
|
"gcy_hpc_server/internal/slurm"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
"go.uber.org/zap/zaptest/observer"
|
|
)
|
|
|
|
func setupJobRouter(h *JobHandler) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
v1 := r.Group("/api/v1")
|
|
jobs := v1.Group("/jobs")
|
|
{
|
|
jobs.POST("/submit", h.SubmitJob)
|
|
jobs.GET("", h.GetJobs)
|
|
jobs.GET("/history", h.GetJobHistory)
|
|
jobs.GET("/:id", h.GetJob)
|
|
jobs.DELETE("/:id", h.CancelJob)
|
|
}
|
|
return r
|
|
}
|
|
|
|
func setupJobHandler(mux *http.ServeMux) (*httptest.Server, *JobHandler) {
|
|
srv := httptest.NewServer(mux)
|
|
client, _ := slurm.NewClient(srv.URL, srv.Client())
|
|
jobSvc := service.NewJobService(client, zap.NewNop())
|
|
return srv, NewJobHandler(jobSvc, zap.NewNop())
|
|
}
|
|
|
|
func setupJobHandlerWithObserver(mux *http.ServeMux) (*httptest.Server, *JobHandler, *observer.ObservedLogs) {
|
|
core, recorded := observer.New(zapcore.DebugLevel)
|
|
l := zap.New(core)
|
|
srv := httptest.NewServer(mux)
|
|
client, _ := slurm.NewClient(srv.URL, srv.Client())
|
|
jobSvc := service.NewJobService(client, l)
|
|
return srv, NewJobHandler(jobSvc, l), recorded
|
|
}
|
|
|
|
func handlerLogs(logs *observer.ObservedLogs) []observer.LoggedEntry {
|
|
var handler []observer.LoggedEntry
|
|
for _, e := range logs.All() {
|
|
for _, f := range e.Context {
|
|
if f.Key == "method" {
|
|
handler = append(handler, e)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return handler
|
|
}
|
|
|
|
func TestSubmitJob_Success(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/submit", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobSubmitResponse{
|
|
Result: &slurm.JobSubmitResponseMsg{JobID: slurm.Ptr(int32(123))},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
body := `{"script":"#!/bin/bash\necho hello"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusCreated {
|
|
t.Fatalf("expected 201, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
if !resp["success"].(bool) {
|
|
t.Fatal("expected success=true")
|
|
}
|
|
data := resp["data"].(map[string]interface{})
|
|
if int(data["job_id"].(float64)) != 123 {
|
|
t.Errorf("expected job_id=123, got %v", data["job_id"])
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_MissingScript(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
body := `{"partition":"normal"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
if resp["success"].(bool) {
|
|
t.Fatal("expected success=false")
|
|
}
|
|
if resp["error"] != "invalid request body" && resp["error"] != "script is required" {
|
|
t.Errorf("expected validation error, got %v", resp["error"])
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_EmptyScript(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
body := `{"script":""}`
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
if resp["success"].(bool) {
|
|
t.Fatal("expected success=false")
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_SlurmError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/submit", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprint(w, `{"errors":[{"error":"internal error"}]}`)
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
body := `{"script":"#!/bin/bash\necho hello"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadGateway {
|
|
t.Fatalf("expected 502, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestGetJobs_Success(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{
|
|
Jobs: []slurm.JobInfo{
|
|
{JobID: slurm.Ptr(int32(1)), Name: slurm.Ptr("job1")},
|
|
{JobID: slurm.Ptr(int32(2)), Name: slurm.Ptr("job2")},
|
|
},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs?page=1&page_size=10", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
if !resp["success"].(bool) {
|
|
t.Fatal("expected success=true")
|
|
}
|
|
data := resp["data"].(map[string]interface{})
|
|
jobs := data["jobs"].([]interface{})
|
|
if len(jobs) != 2 {
|
|
t.Fatalf("expected 2 jobs, got %d", len(jobs))
|
|
}
|
|
if int(data["total"].(float64)) != 2 {
|
|
t.Errorf("expected total=2, got %v", data["total"])
|
|
}
|
|
if int(data["page"].(float64)) != 1 {
|
|
t.Errorf("expected page=1, got %v", data["page"])
|
|
}
|
|
if int(data["page_size"].(float64)) != 10 {
|
|
t.Errorf("expected page_size=10, got %v", data["page_size"])
|
|
}
|
|
}
|
|
|
|
func TestGetJobs_Pagination(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{
|
|
Jobs: []slurm.JobInfo{
|
|
{JobID: slurm.Ptr(int32(1)), Name: slurm.Ptr("job1")},
|
|
{JobID: slurm.Ptr(int32(2)), Name: slurm.Ptr("job2")},
|
|
{JobID: slurm.Ptr(int32(3)), Name: slurm.Ptr("job3")},
|
|
},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs?page=2&page_size=1", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
data := resp["data"].(map[string]interface{})
|
|
jobs := data["jobs"].([]interface{})
|
|
if len(jobs) != 1 {
|
|
t.Fatalf("expected 1 job on page 2, got %d", len(jobs))
|
|
}
|
|
if int(data["total"].(float64)) != 3 {
|
|
t.Errorf("expected total=3, got %v", data["total"])
|
|
}
|
|
jobData := jobs[0].(map[string]interface{})
|
|
if int(jobData["job_id"].(float64)) != 2 {
|
|
t.Errorf("expected job_id=2 on page 2, got %v", jobData["job_id"])
|
|
}
|
|
}
|
|
|
|
func TestGetJobs_DefaultPagination(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{Jobs: []slurm.JobInfo{}})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
data := resp["data"].(map[string]interface{})
|
|
if int(data["page"].(float64)) != 1 {
|
|
t.Errorf("expected default page=1, got %v", data["page"])
|
|
}
|
|
if int(data["page_size"].(float64)) != 20 {
|
|
t.Errorf("expected default page_size=20, got %v", data["page_size"])
|
|
}
|
|
}
|
|
|
|
func TestGetJob_Success(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{
|
|
Jobs: []slurm.JobInfo{
|
|
{JobID: slurm.Ptr(int32(42)), Name: slurm.Ptr("test-job")},
|
|
},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/42", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
data := resp["data"].(map[string]interface{})
|
|
if int(data["job_id"].(float64)) != 42 {
|
|
t.Errorf("expected job_id=42, got %v", data["job_id"])
|
|
}
|
|
}
|
|
|
|
func TestGetJob_NotFound(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/999", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{
|
|
Jobs: []slurm.JobInfo{},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/999", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("expected 404, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
if resp["error"].(string) != "job not found" {
|
|
t.Errorf("expected 'job not found' error, got %v", resp["error"])
|
|
}
|
|
}
|
|
|
|
func TestCancelJob_Success(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiResp{})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/api/v1/jobs/42", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
if !resp["success"].(bool) {
|
|
t.Fatal("expected success=true")
|
|
}
|
|
data := resp["data"].(map[string]interface{})
|
|
if data["message"].(string) != "job cancelled" {
|
|
t.Errorf("expected 'job cancelled', got %v", data["message"])
|
|
}
|
|
}
|
|
|
|
func TestGetJobHistory_Success(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurmdb/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiSlurmdbdJobsResp{
|
|
Jobs: []slurm.Job{
|
|
{JobID: slurm.Ptr(int32(1)), Name: slurm.Ptr("hist1")},
|
|
{JobID: slurm.Ptr(int32(2)), Name: slurm.Ptr("hist2")},
|
|
{JobID: slurm.Ptr(int32(3)), Name: slurm.Ptr("hist3")},
|
|
},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/history?page=1&page_size=2", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
data := resp["data"].(map[string]interface{})
|
|
jobs := data["jobs"].([]interface{})
|
|
if len(jobs) != 2 {
|
|
t.Fatalf("expected 2 jobs on page 1, got %d", len(jobs))
|
|
}
|
|
if int(data["total"].(float64)) != 3 {
|
|
t.Errorf("expected total=3, got %v", data["total"])
|
|
}
|
|
if int(data["page"].(float64)) != 1 {
|
|
t.Errorf("expected page=1, got %v", data["page"])
|
|
}
|
|
if int(data["page_size"].(float64)) != 2 {
|
|
t.Errorf("expected page_size=2, got %v", data["page_size"])
|
|
}
|
|
}
|
|
|
|
func TestGetJobHistory_DefaultPagination(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurmdb/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiSlurmdbdJobsResp{
|
|
Jobs: []slurm.Job{
|
|
{JobID: slurm.Ptr(int32(1)), Name: slurm.Ptr("h1")},
|
|
},
|
|
})
|
|
})
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/history", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
data := resp["data"].(map[string]interface{})
|
|
if int(data["page"].(float64)) != 1 {
|
|
t.Errorf("expected default page=1, got %v", data["page"])
|
|
}
|
|
if int(data["page_size"].(float64)) != 20 {
|
|
t.Errorf("expected default page_size=20, got %v", data["page_size"])
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_InvalidBody(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
srv, handler := setupJobHandler(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(`not json`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
// --- Logging verification tests ---
|
|
|
|
func TestSubmitJob_InvalidBody_LogsWarn(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(`not json`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.WarnLevel {
|
|
t.Errorf("expected Warn level, got %v", entry.Level)
|
|
}
|
|
if entry.Context[0].Key != "method" || entry.Context[0].String != "SubmitJob" {
|
|
t.Errorf("expected method=SubmitJob, got %v", entry.Context[0])
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_EmptyScript_LogsWarn(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(`{"script":""}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.WarnLevel {
|
|
t.Errorf("expected Warn level, got %v", entry.Level)
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_SlurmError_LogsError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/submit", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprint(w, `{"errors":[{"error":"internal error"}]}`)
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(`{"script":"#!/bin/bash\necho hello"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadGateway {
|
|
t.Fatalf("expected 502, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.ErrorLevel {
|
|
t.Errorf("expected Error level, got %v", entry.Level)
|
|
}
|
|
foundMethod := false
|
|
foundStatus := false
|
|
for _, f := range entry.Context {
|
|
if f.Key == "method" && f.String == "SubmitJob" {
|
|
foundMethod = true
|
|
}
|
|
if f.Key == "status" && f.Integer == http.StatusBadGateway {
|
|
foundStatus = true
|
|
}
|
|
}
|
|
if !foundMethod {
|
|
t.Error("expected method=SubmitJob in log fields")
|
|
}
|
|
if !foundStatus {
|
|
t.Error("expected status=502 in log fields")
|
|
}
|
|
}
|
|
|
|
func TestSubmitJob_Success_NoHandlerLogs(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/submit", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobSubmitResponse{
|
|
Result: &slurm.JobSubmitResponseMsg{JobID: slurm.Ptr(int32(123))},
|
|
})
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs/submit", bytes.NewBufferString(`{"script":"#!/bin/bash\necho hello"}`))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusCreated {
|
|
t.Fatalf("expected 201, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 0 {
|
|
t.Errorf("expected no handler log entries on success, got %d", len(hLogs))
|
|
}
|
|
}
|
|
|
|
func TestGetJobs_Error_LogsError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Fatalf("expected 500, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.ErrorLevel {
|
|
t.Errorf("expected Error level, got %v", entry.Level)
|
|
}
|
|
foundMethod := false
|
|
for _, f := range entry.Context {
|
|
if f.Key == "method" && f.String == "GetJobs" {
|
|
foundMethod = true
|
|
}
|
|
}
|
|
if !foundMethod {
|
|
t.Error("expected method=GetJobs in log fields")
|
|
}
|
|
}
|
|
|
|
func TestGetJob_NotFound_LogsWarn(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/999", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{Jobs: []slurm.JobInfo{}})
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/999", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("expected 404, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.WarnLevel {
|
|
t.Errorf("expected Warn level, got %v", entry.Level)
|
|
}
|
|
foundMethod := false
|
|
for _, f := range entry.Context {
|
|
if f.Key == "method" && f.String == "GetJob" {
|
|
foundMethod = true
|
|
}
|
|
}
|
|
if !foundMethod {
|
|
t.Error("expected method=GetJob in log fields")
|
|
}
|
|
}
|
|
|
|
func TestGetJob_Error_LogsError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/42", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Fatalf("expected 500, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
if hLogs[0].Level != zapcore.ErrorLevel {
|
|
t.Errorf("expected Error level, got %v", hLogs[0].Level)
|
|
}
|
|
}
|
|
|
|
func TestCancelJob_SlurmError_LogsError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/api/v1/jobs/42", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadGateway {
|
|
t.Fatalf("expected 502, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.ErrorLevel {
|
|
t.Errorf("expected Error level, got %v", entry.Level)
|
|
}
|
|
foundMethod := false
|
|
for _, f := range entry.Context {
|
|
if f.Key == "method" && f.String == "CancelJob" {
|
|
foundMethod = true
|
|
}
|
|
}
|
|
if !foundMethod {
|
|
t.Error("expected method=CancelJob in log fields")
|
|
}
|
|
}
|
|
|
|
func TestGetJobHistory_InvalidQuery_LogsWarn(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurmdb/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiSlurmdbdJobsResp{})
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/history?page=abc", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
entry := hLogs[0]
|
|
if entry.Level != zapcore.WarnLevel {
|
|
t.Errorf("expected Warn level, got %v", entry.Level)
|
|
}
|
|
foundMethod := false
|
|
for _, f := range entry.Context {
|
|
if f.Key == "method" && f.String == "GetJobHistory" {
|
|
foundMethod = true
|
|
}
|
|
}
|
|
if !foundMethod {
|
|
t.Error("expected method=GetJobHistory in log fields")
|
|
}
|
|
}
|
|
|
|
func TestGetJobHistory_Error_LogsError(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurmdb/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/history?page=1&page_size=10", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Fatalf("expected 500, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 1 {
|
|
t.Fatalf("expected 1 handler log entry, got %d", len(hLogs))
|
|
}
|
|
if hLogs[0].Level != zapcore.ErrorLevel {
|
|
t.Errorf("expected Error level, got %v", hLogs[0].Level)
|
|
}
|
|
}
|
|
|
|
func TestGetJobHistory_Success_NoHandlerLogs(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurmdb/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiSlurmdbdJobsResp{
|
|
Jobs: []slurm.Job{
|
|
{JobID: slurm.Ptr(int32(1)), Name: slurm.Ptr("h1")},
|
|
},
|
|
})
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs/history?page=1&page_size=10", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 0 {
|
|
t.Errorf("expected no handler log entries on success, got %d", len(hLogs))
|
|
}
|
|
}
|
|
|
|
func TestGetJobs_Success_NoHandlerLogs(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiJobInfoResp{Jobs: []slurm.JobInfo{}})
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/jobs", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 0 {
|
|
t.Errorf("expected no handler log entries on success, got %d", len(hLogs))
|
|
}
|
|
}
|
|
|
|
func TestCancelJob_Success_NoHandlerLogs(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(slurm.OpenapiResp{})
|
|
})
|
|
srv, handler, logs := setupJobHandlerWithObserver(mux)
|
|
defer srv.Close()
|
|
|
|
router := setupJobRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/api/v1/jobs/42", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
hLogs := handlerLogs(logs)
|
|
if len(hLogs) != 0 {
|
|
t.Errorf("expected no handler log entries on success, got %d", len(hLogs))
|
|
}
|
|
}
|