feat: 添加 Reservation 领域类型和 ReservationsService
包含 ReservationInfo、ReservationCoreSpec 等类型。ReservationsService 提供 GetReservations 和 GetReservation 2 个方法。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
71
internal/slurm/slurm_reservations.go
Normal file
71
internal/slurm/slurm_reservations.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package slurm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetReservationsOptions specifies optional parameters for GetReservations.
|
||||||
|
type GetReservationsOptions struct {
|
||||||
|
UpdateTime *int64 `url:"update_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReservations lists all reservations.
|
||||||
|
func (s *ReservationsService) GetReservations(ctx context.Context, opts *GetReservationsOptions) (*OpenapiReservationResp, *Response, error) {
|
||||||
|
path := "slurm/v0.0.40/reservations"
|
||||||
|
req, err := s.client.NewRequest("GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
u, parseErr := url.Parse(req.URL.String())
|
||||||
|
if parseErr != nil {
|
||||||
|
return nil, nil, parseErr
|
||||||
|
}
|
||||||
|
q := u.Query()
|
||||||
|
if opts.UpdateTime != nil {
|
||||||
|
q.Set("update_time", strconv.FormatInt(*opts.UpdateTime, 10))
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
req.URL = u
|
||||||
|
}
|
||||||
|
|
||||||
|
var result OpenapiReservationResp
|
||||||
|
resp, err := s.client.Do(ctx, req, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return &result, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReservation gets a single reservation by name.
|
||||||
|
func (s *ReservationsService) GetReservation(ctx context.Context, reservationName string, opts *GetReservationsOptions) (*OpenapiReservationResp, *Response, error) {
|
||||||
|
path := fmt.Sprintf("slurm/v0.0.40/reservation/%s", reservationName)
|
||||||
|
req, err := s.client.NewRequest("GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
u, parseErr := url.Parse(req.URL.String())
|
||||||
|
if parseErr != nil {
|
||||||
|
return nil, nil, parseErr
|
||||||
|
}
|
||||||
|
q := u.Query()
|
||||||
|
if opts.UpdateTime != nil {
|
||||||
|
q.Set("update_time", strconv.FormatInt(*opts.UpdateTime, 10))
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
req.URL = u
|
||||||
|
}
|
||||||
|
|
||||||
|
var result OpenapiReservationResp
|
||||||
|
resp, err := s.client.Do(ctx, req, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return &result, resp, nil
|
||||||
|
}
|
||||||
86
internal/slurm/slurm_reservations_test.go
Normal file
86
internal/slurm/slurm_reservations_test.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package slurm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReservationsService_GetReservations(t *testing.T) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/slurm/v0.0.40/reservations", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
testMethod(t, r, "GET")
|
||||||
|
fmt.Fprint(w, `{"reservations": []}`)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client, _ := NewClient(server.URL, nil)
|
||||||
|
resp, _, err := client.Reservations.GetReservations(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("expected non-nil response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationsService_GetReservation(t *testing.T) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/slurm/v0.0.40/reservation/test-reservation", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
testMethod(t, r, "GET")
|
||||||
|
fmt.Fprint(w, `{"reservations": [{"name": "test-reservation"}]}`)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client, _ := NewClient(server.URL, nil)
|
||||||
|
resp, _, err := client.Reservations.GetReservation(context.Background(), "test-reservation", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("expected non-nil response")
|
||||||
|
}
|
||||||
|
if resp.Reservations == nil {
|
||||||
|
t.Fatal("expected non-nil reservations")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationsService_GetReservations_Error(t *testing.T) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/slurm/v0.0.40/reservations", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprint(w, `{"errors": [{"error": "internal error"}]}`)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client, _ := NewClient(server.URL, nil)
|
||||||
|
_, _, err := client.Reservations.GetReservations(context.Background(), nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for 500 response")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "500") {
|
||||||
|
t.Errorf("expected error to contain 500, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationsService_GetReservation_Error(t *testing.T) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/slurm/v0.0.40/reservation/nonexistent", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprint(w, `{"errors": [{"error": "reservation not found"}]}`)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client, _ := NewClient(server.URL, nil)
|
||||||
|
_, _, err := client.Reservations.GetReservation(context.Background(), "nonexistent", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for 404 response")
|
||||||
|
}
|
||||||
|
}
|
||||||
54
internal/slurm/types_reservation.go
Normal file
54
internal/slurm/types_reservation.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package slurm
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Reservation types — v0.0.40 reservation schemas
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ReservationCoreSpec represents a single core specialization entry (v0.0.40_reservation_core_spec).
|
||||||
|
type ReservationCoreSpec struct {
|
||||||
|
Node *string `json:"node,omitempty"`
|
||||||
|
Core *string `json:"core,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReservationInfoCoreSpec is a collection of ReservationCoreSpec objects (v0.0.40_reservation_info_core_spec).
|
||||||
|
type ReservationInfoCoreSpec []ReservationCoreSpec
|
||||||
|
|
||||||
|
// ReservationPurgeCompleted represents purge_completed settings for a reservation.
|
||||||
|
type ReservationPurgeCompleted struct {
|
||||||
|
Time *Uint32NoVal `json:"time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReservationInfo represents a Slurm reservation (v0.0.40_reservation_info).
|
||||||
|
type ReservationInfo struct {
|
||||||
|
Accounts *string `json:"accounts,omitempty"`
|
||||||
|
BurstBuffer *string `json:"burst_buffer,omitempty"`
|
||||||
|
CoreCount *int32 `json:"core_count,omitempty"`
|
||||||
|
CoreSpecializations *ReservationInfoCoreSpec `json:"core_specializations,omitempty"`
|
||||||
|
EndTime *Uint64NoVal `json:"end_time,omitempty"`
|
||||||
|
Features *string `json:"features,omitempty"`
|
||||||
|
Flags []string `json:"flags,omitempty"`
|
||||||
|
Groups *string `json:"groups,omitempty"`
|
||||||
|
Licenses *string `json:"licenses,omitempty"`
|
||||||
|
MaxStartDelay *int32 `json:"max_start_delay,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
NodeCount *int32 `json:"node_count,omitempty"`
|
||||||
|
NodeList *string `json:"node_list,omitempty"`
|
||||||
|
Partition *string `json:"partition,omitempty"`
|
||||||
|
PurgeCompleted *ReservationPurgeCompleted `json:"purge_completed,omitempty"`
|
||||||
|
StartTime *Uint64NoVal `json:"start_time,omitempty"`
|
||||||
|
Watts *Uint32NoVal `json:"watts,omitempty"`
|
||||||
|
TRES *string `json:"tres,omitempty"`
|
||||||
|
Users *string `json:"users,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReservationInfoMsg is a collection of ReservationInfo objects (v0.0.40_reservation_info_msg).
|
||||||
|
type ReservationInfoMsg []ReservationInfo
|
||||||
|
|
||||||
|
// OpenapiReservationResp represents the response for reservation queries (v0.0.40_openapi_reservation_resp).
|
||||||
|
type OpenapiReservationResp struct {
|
||||||
|
Reservations *ReservationInfoMsg `json:"reservations,omitempty"`
|
||||||
|
LastUpdate *Uint64NoVal `json:"last_update,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
273
internal/slurm/types_reservation_test.go
Normal file
273
internal/slurm/types_reservation_test.go
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
package slurm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReservationCoreSpecRoundTrip(t *testing.T) {
|
||||||
|
orig := ReservationCoreSpec{
|
||||||
|
Node: Ptr("node01"),
|
||||||
|
Core: Ptr("0-3"),
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
var decoded ReservationCoreSpec
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if decoded.Node == nil || *decoded.Node != "node01" {
|
||||||
|
t.Fatalf("node mismatch: %v", decoded.Node)
|
||||||
|
}
|
||||||
|
if decoded.Core == nil || *decoded.Core != "0-3" {
|
||||||
|
t.Fatalf("core mismatch: %v", decoded.Core)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationCoreSpecEmptyRoundTrip(t *testing.T) {
|
||||||
|
orig := ReservationCoreSpec{}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
if string(data) != "{}" {
|
||||||
|
t.Fatalf("empty ReservationCoreSpec should marshal to {}, got %s", data)
|
||||||
|
}
|
||||||
|
var decoded ReservationCoreSpec
|
||||||
|
if err := json.Unmarshal([]byte(`{}`), &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if decoded.Node != nil || decoded.Core != nil {
|
||||||
|
t.Fatal("all fields should be nil for empty object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationInfoCoreSpecRoundTrip(t *testing.T) {
|
||||||
|
orig := ReservationInfoCoreSpec{
|
||||||
|
{Node: Ptr("node01"), Core: Ptr("0-3")},
|
||||||
|
{Node: Ptr("node02"), Core: Ptr("4-7")},
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
var decoded ReservationInfoCoreSpec
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if len(decoded) != 2 {
|
||||||
|
t.Fatalf("expected 2 entries, got %d", len(decoded))
|
||||||
|
}
|
||||||
|
if decoded[0].Node == nil || *decoded[0].Node != "node01" {
|
||||||
|
t.Fatalf("entry[0].node mismatch: %v", decoded[0].Node)
|
||||||
|
}
|
||||||
|
if decoded[1].Core == nil || *decoded[1].Core != "4-7" {
|
||||||
|
t.Fatalf("entry[1].core mismatch: %v", decoded[1].Core)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationInfoRoundTrip(t *testing.T) {
|
||||||
|
orig := ReservationInfo{
|
||||||
|
Accounts: Ptr("admin,dev"),
|
||||||
|
BurstBuffer: Ptr("bb_test"),
|
||||||
|
CoreCount: Ptr(int32(16)),
|
||||||
|
CoreSpecializations: &ReservationInfoCoreSpec{
|
||||||
|
{Node: Ptr("node01"), Core: Ptr("0-3")},
|
||||||
|
},
|
||||||
|
EndTime: &Uint64NoVal{
|
||||||
|
Set: Ptr(true),
|
||||||
|
Number: Ptr(int64(1700000000)),
|
||||||
|
},
|
||||||
|
Features: Ptr("gpu,nvme"),
|
||||||
|
Flags: []string{"MAINT", "DAILY", "IGNORE_JOBS"},
|
||||||
|
Groups: Ptr("slurm"),
|
||||||
|
Licenses: Ptr("matlab:2"),
|
||||||
|
MaxStartDelay: Ptr(int32(300)),
|
||||||
|
Name: Ptr("test_resv"),
|
||||||
|
NodeCount: Ptr(int32(4)),
|
||||||
|
NodeList: Ptr("node[01-04]"),
|
||||||
|
Partition: Ptr("normal"),
|
||||||
|
PurgeCompleted: &ReservationPurgeCompleted{
|
||||||
|
Time: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(3600))},
|
||||||
|
},
|
||||||
|
StartTime: &Uint64NoVal{
|
||||||
|
Set: Ptr(true),
|
||||||
|
Number: Ptr(int64(1699990000)),
|
||||||
|
},
|
||||||
|
Watts: &Uint32NoVal{Set: Ptr(true), Infinite: Ptr(true)},
|
||||||
|
TRES: Ptr("billing=16"),
|
||||||
|
Users: Ptr("user1,user2"),
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
var decoded ReservationInfo
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if decoded.Name == nil || *decoded.Name != "test_resv" {
|
||||||
|
t.Fatalf("name mismatch: %v", decoded.Name)
|
||||||
|
}
|
||||||
|
if decoded.Accounts == nil || *decoded.Accounts != "admin,dev" {
|
||||||
|
t.Fatalf("accounts mismatch: %v", decoded.Accounts)
|
||||||
|
}
|
||||||
|
if decoded.CoreCount == nil || *decoded.CoreCount != 16 {
|
||||||
|
t.Fatalf("core_count mismatch: %v", decoded.CoreCount)
|
||||||
|
}
|
||||||
|
if decoded.CoreSpecializations == nil || len(*decoded.CoreSpecializations) != 1 {
|
||||||
|
t.Fatalf("core_specializations mismatch: %v", decoded.CoreSpecializations)
|
||||||
|
}
|
||||||
|
if decoded.EndTime == nil || decoded.EndTime.Number == nil || *decoded.EndTime.Number != 1700000000 {
|
||||||
|
t.Fatalf("end_time mismatch: %v", decoded.EndTime)
|
||||||
|
}
|
||||||
|
if len(decoded.Flags) != 3 || decoded.Flags[0] != "MAINT" || decoded.Flags[2] != "IGNORE_JOBS" {
|
||||||
|
t.Fatalf("flags mismatch: %v", decoded.Flags)
|
||||||
|
}
|
||||||
|
if decoded.MaxStartDelay == nil || *decoded.MaxStartDelay != 300 {
|
||||||
|
t.Fatalf("max_start_delay mismatch: %v", decoded.MaxStartDelay)
|
||||||
|
}
|
||||||
|
if decoded.NodeCount == nil || *decoded.NodeCount != 4 {
|
||||||
|
t.Fatalf("node_count mismatch: %v", decoded.NodeCount)
|
||||||
|
}
|
||||||
|
if decoded.PurgeCompleted == nil || decoded.PurgeCompleted.Time == nil || decoded.PurgeCompleted.Time.Number == nil || *decoded.PurgeCompleted.Time.Number != 3600 {
|
||||||
|
t.Fatalf("purge_completed.time mismatch: %v", decoded.PurgeCompleted)
|
||||||
|
}
|
||||||
|
if decoded.StartTime == nil || decoded.StartTime.Number == nil || *decoded.StartTime.Number != 1699990000 {
|
||||||
|
t.Fatalf("start_time mismatch: %v", decoded.StartTime)
|
||||||
|
}
|
||||||
|
if decoded.Watts == nil || decoded.Watts.Infinite == nil || !*decoded.Watts.Infinite {
|
||||||
|
t.Fatalf("watts mismatch: %v", decoded.Watts)
|
||||||
|
}
|
||||||
|
if decoded.TRES == nil || *decoded.TRES != "billing=16" {
|
||||||
|
t.Fatalf("tres mismatch: %v", decoded.TRES)
|
||||||
|
}
|
||||||
|
if decoded.Users == nil || *decoded.Users != "user1,user2" {
|
||||||
|
t.Fatalf("users mismatch: %v", decoded.Users)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationInfoEmptyRoundTrip(t *testing.T) {
|
||||||
|
orig := ReservationInfo{}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
if string(data) != "{}" {
|
||||||
|
t.Fatalf("empty ReservationInfo should marshal to {}, got %s", data)
|
||||||
|
}
|
||||||
|
var decoded ReservationInfo
|
||||||
|
if err := json.Unmarshal([]byte(`{}`), &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if decoded.Name != nil {
|
||||||
|
t.Fatal("name should be nil for empty object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReservationInfoMsgRoundTrip(t *testing.T) {
|
||||||
|
orig := ReservationInfoMsg{
|
||||||
|
{Name: Ptr("resv1"), NodeCount: Ptr(int32(2))},
|
||||||
|
{Name: Ptr("resv2"), NodeCount: Ptr(int32(4)), Flags: []string{"MAINT", "FLEX"}},
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
var decoded ReservationInfoMsg
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if len(decoded) != 2 {
|
||||||
|
t.Fatalf("expected 2 reservations, got %d", len(decoded))
|
||||||
|
}
|
||||||
|
if decoded[0].Name == nil || *decoded[0].Name != "resv1" {
|
||||||
|
t.Fatalf("reservations[0].name mismatch: %v", decoded[0].Name)
|
||||||
|
}
|
||||||
|
if decoded[1].Name == nil || *decoded[1].Name != "resv2" {
|
||||||
|
t.Fatalf("reservations[1].name mismatch: %v", decoded[1].Name)
|
||||||
|
}
|
||||||
|
if len(decoded[1].Flags) != 2 || decoded[1].Flags[1] != "FLEX" {
|
||||||
|
t.Fatalf("reservations[1].flags mismatch: %v", decoded[1].Flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenapiReservationRespRoundTrip(t *testing.T) {
|
||||||
|
reservations := ReservationInfoMsg{
|
||||||
|
{
|
||||||
|
Name: Ptr("maint_window"),
|
||||||
|
Partition: Ptr("normal"),
|
||||||
|
NodeList: Ptr("node[01-10]"),
|
||||||
|
Flags: []string{"MAINT", "IGNORE_JOBS"},
|
||||||
|
EndTime: &Uint64NoVal{
|
||||||
|
Set: Ptr(true),
|
||||||
|
Number: Ptr(int64(1700050000)),
|
||||||
|
},
|
||||||
|
StartTime: &Uint64NoVal{
|
||||||
|
Set: Ptr(true),
|
||||||
|
Number: Ptr(int64(1700000000)),
|
||||||
|
},
|
||||||
|
CoreCount: Ptr(int32(160)),
|
||||||
|
NodeCount: Ptr(int32(10)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: Ptr("gpu_resv"),
|
||||||
|
TRES: Ptr("gres/gpu=8"),
|
||||||
|
Users: Ptr("researcher1"),
|
||||||
|
Watts: &Uint32NoVal{Set: Ptr(true), Infinite: Ptr(true)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
orig := OpenapiReservationResp{
|
||||||
|
Reservations: &reservations,
|
||||||
|
LastUpdate: &Uint64NoVal{
|
||||||
|
Set: Ptr(true),
|
||||||
|
Number: Ptr(int64(1700010000)),
|
||||||
|
},
|
||||||
|
Meta: &OpenapiMeta{
|
||||||
|
Slurm: &MetaSlurm{
|
||||||
|
Version: &MetaSlurmVersion{
|
||||||
|
Major: Ptr("24"),
|
||||||
|
Micro: Ptr("5"),
|
||||||
|
Minor: Ptr("05"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Errors: OpenapiErrors{},
|
||||||
|
Warnings: OpenapiWarnings{},
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("marshal: %v", err)
|
||||||
|
}
|
||||||
|
var decoded OpenapiReservationResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if decoded.Reservations == nil || len(*decoded.Reservations) != 2 {
|
||||||
|
t.Fatalf("expected 2 reservations, got %d", len(*decoded.Reservations))
|
||||||
|
}
|
||||||
|
rs := *decoded.Reservations
|
||||||
|
if rs[0].Name == nil || *rs[0].Name != "maint_window" {
|
||||||
|
t.Fatalf("reservations[0].name mismatch")
|
||||||
|
}
|
||||||
|
if rs[0].EndTime == nil || rs[0].EndTime.Number == nil || *rs[0].EndTime.Number != 1700050000 {
|
||||||
|
t.Fatalf("reservations[0].end_time mismatch")
|
||||||
|
}
|
||||||
|
if len(rs[0].Flags) != 2 || rs[0].Flags[0] != "MAINT" {
|
||||||
|
t.Fatalf("reservations[0].flags mismatch: %v", rs[0].Flags)
|
||||||
|
}
|
||||||
|
if rs[1].TRES == nil || *rs[1].TRES != "gres/gpu=8" {
|
||||||
|
t.Fatalf("reservations[1].tres mismatch: %v", rs[1].TRES)
|
||||||
|
}
|
||||||
|
if rs[1].Watts == nil || rs[1].Watts.Infinite == nil || !*rs[1].Watts.Infinite {
|
||||||
|
t.Fatalf("reservations[1].watts.infinite mismatch")
|
||||||
|
}
|
||||||
|
if decoded.LastUpdate == nil || decoded.LastUpdate.Number == nil || *decoded.LastUpdate.Number != 1700010000 {
|
||||||
|
t.Fatalf("last_update mismatch: %v", decoded.LastUpdate)
|
||||||
|
}
|
||||||
|
if decoded.Meta == nil || decoded.Meta.Slurm == nil || decoded.Meta.Slurm.Version == nil {
|
||||||
|
t.Fatal("meta.slurm.version should not be nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user