package slurm import ( "fmt" "net/http" "net/http/httptest" "strings" "testing" ) func TestCheckResponse(t *testing.T) { tests := []struct { name string statusCode int body string wantErr bool wantStatusCode int wantErrors int wantWarnings int wantMessageContains string }{ { name: "2xx response returns nil", statusCode: http.StatusOK, body: `{"meta":{}}`, wantErr: false, }, { name: "404 with valid JSON error body", statusCode: http.StatusNotFound, body: `{"errors":[{"description":"Unable to query JobId=198","error_number":2017,"error":"Invalid job id specified","source":"_handle_job_get"}],"warnings":[]}`, wantErr: true, wantStatusCode: 404, wantErrors: 1, wantWarnings: 0, wantMessageContains: "Invalid job id specified", }, { name: "500 with non-JSON body", statusCode: http.StatusInternalServerError, body: "internal server error", wantErr: true, wantStatusCode: 500, wantErrors: 0, wantWarnings: 0, wantMessageContains: "internal server error", }, { name: "503 with empty body returns http.Status text", statusCode: http.StatusServiceUnavailable, body: "", wantErr: true, wantStatusCode: 503, wantErrors: 0, wantWarnings: 0, wantMessageContains: "503 Service Unavailable", }, { name: "400 with valid JSON but empty errors array", statusCode: http.StatusBadRequest, body: `{"errors":[],"warnings":[]}`, wantErr: true, wantStatusCode: 400, wantErrors: 0, wantWarnings: 0, wantMessageContains: `{"errors":[],"warnings":[]}`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rec := httptest.NewRecorder() rec.WriteHeader(tt.statusCode) if tt.body != "" { rec.Body.WriteString(tt.body) } err := CheckResponse(rec.Result()) if (err != nil) != tt.wantErr { t.Fatalf("CheckResponse() error = %v, wantErr %v", err, tt.wantErr) } if !tt.wantErr { return } apiErr, ok := err.(*SlurmAPIError) if !ok { t.Fatalf("expected *SlurmAPIError, got %T", err) } if apiErr.StatusCode != tt.wantStatusCode { t.Errorf("StatusCode = %d, want %d", apiErr.StatusCode, tt.wantStatusCode) } if len(apiErr.Errors) != tt.wantErrors { t.Errorf("len(Errors) = %d, want %d", len(apiErr.Errors), tt.wantErrors) } if len(apiErr.Warnings) != tt.wantWarnings { t.Errorf("len(Warnings) = %d, want %d", len(apiErr.Warnings), tt.wantWarnings) } if !strings.Contains(apiErr.Message, tt.wantMessageContains) { t.Errorf("Message = %q, want to contain %q", apiErr.Message, tt.wantMessageContains) } }) } } func TestIsNotFound(t *testing.T) { tests := []struct { name string err error want bool }{ { name: "404 SlurmAPIError returns true", err: &SlurmAPIError{StatusCode: http.StatusNotFound}, want: true, }, { name: "500 SlurmAPIError returns false", err: &SlurmAPIError{StatusCode: http.StatusInternalServerError}, want: false, }, { name: "200 SlurmAPIError returns false", err: &SlurmAPIError{StatusCode: http.StatusOK}, want: false, }, { name: "plain error returns false", err: fmt.Errorf("some error"), want: false, }, { name: "nil returns false", err: nil, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := IsNotFound(tt.err); got != tt.want { t.Errorf("IsNotFound() = %v, want %v", got, tt.want) } }) } } func TestSlurmAPIError_Error(t *testing.T) { fakeReq := httptest.NewRequest("GET", "http://localhost/slurm/v0.0.40/job/123", nil) tests := []struct { name string err *SlurmAPIError wantContains []string }{ { name: "with Error field set", err: &SlurmAPIError{ Response: &http.Response{Request: fakeReq}, StatusCode: http.StatusNotFound, Errors: OpenapiErrors{{Error: Ptr("Job not found")}}, Message: "raw body", }, wantContains: []string{"404", "Job not found"}, }, { name: "with Description field set when Error is nil", err: &SlurmAPIError{ Response: &http.Response{Request: fakeReq}, StatusCode: http.StatusBadRequest, Errors: OpenapiErrors{{Description: Ptr("Unable to query")}}, Message: "raw body", }, wantContains: []string{"400", "Unable to query"}, }, { name: "with both Error and Description nil falls through to Message", err: &SlurmAPIError{ Response: &http.Response{Request: fakeReq}, StatusCode: http.StatusInternalServerError, Errors: OpenapiErrors{{}}, Message: "something went wrong", }, wantContains: []string{"500", "something went wrong"}, }, { name: "with empty Errors slice falls through to Message", err: &SlurmAPIError{ Response: &http.Response{Request: fakeReq}, StatusCode: http.StatusServiceUnavailable, Errors: OpenapiErrors{}, Message: "service unavailable fallback", }, wantContains: []string{"503", "service unavailable fallback"}, }, { name: "with non-empty Errors but empty detail string falls through to Message", err: &SlurmAPIError{ Response: &http.Response{Request: fakeReq}, StatusCode: http.StatusBadGateway, Errors: OpenapiErrors{{ErrorNumber: Ptr(int32(42))}}, Message: "gateway error detail", }, wantContains: []string{"502", "gateway error detail"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.err.Error() for _, substr := range tt.wantContains { if !strings.Contains(got, substr) { t.Errorf("Error() = %q, want to contain %q", got, substr) } } }) } }