feat: 添加 Diag/Ping/Licenses/Reconfigure 类型和对应服务

包含 StatsMsg(含调度和回填统计)、ControllerPing、License 等类型。实现 4 个服务:DiagService.GetDiag、PingService.Ping、LicensesService.GetLicenses、ReconfigureService.Reconfigure。

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
dailz
2026-04-08 18:30:11 +08:00
parent 02ed2e7b38
commit 263363a2ba
4 changed files with 687 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
package slurm
import "context"
// GetDiag returns slurm diagnostics information.
func (s *DiagService) GetDiag(ctx context.Context) (*OpenapiDiagResp, *Response, error) {
path := "slurm/v0.0.40/diag"
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var result OpenapiDiagResp
resp, err := s.client.Do(ctx, req, &result)
if err != nil {
return nil, resp, err
}
return &result, resp, nil
}
// Ping returns slurm controller ping information.
func (s *PingService) Ping(ctx context.Context) (*OpenapiPingArrayResp, *Response, error) {
path := "slurm/v0.0.40/ping"
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var result OpenapiPingArrayResp
resp, err := s.client.Do(ctx, req, &result)
if err != nil {
return nil, resp, err
}
return &result, resp, nil
}
// GetLicenses returns slurm license information.
func (s *LicensesService) GetLicenses(ctx context.Context) (*OpenapiLicensesResp, *Response, error) {
path := "slurm/v0.0.40/licenses"
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var result OpenapiLicensesResp
resp, err := s.client.Do(ctx, req, &result)
if err != nil {
return nil, resp, err
}
return &result, resp, nil
}
// Reconfigure requests slurm reconfigure.
func (s *ReconfigureService) Reconfigure(ctx context.Context) (*OpenapiResp, *Response, error) {
path := "slurm/v0.0.40/reconfigure"
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var result OpenapiResp
resp, err := s.client.Do(ctx, req, &result)
if err != nil {
return nil, resp, err
}
return &result, resp, nil
}

View File

@@ -0,0 +1,162 @@
package slurm
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestDiagService_GetDiag(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/diag", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"statistics": {"parts": 1}, "meta": {"plugin": {"type": "openapi/slurmctld"}}}`)
})
server := httptest.NewServer(mux)
defer server.Close()
client, _ := NewClient(server.URL, nil)
resp, _, err := client.Diag.GetDiag(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if resp.Statistics == nil {
t.Fatal("expected non-nil statistics")
}
}
func TestDiagService_GetDiag_Error(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/diag", 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.Diag.GetDiag(context.Background())
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 TestPingService_Ping(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/ping", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"pings": [{"hostname": "slurmctl", "pinged": "UP", "latency": 100, "mode": "primary"}]}`)
})
server := httptest.NewServer(mux)
defer server.Close()
client, _ := NewClient(server.URL, nil)
resp, _, err := client.Ping.Ping(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if len(resp.Pings) != 1 {
t.Errorf("expected 1 ping, got %d", len(resp.Pings))
}
}
func TestPingService_Ping_Error(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/ping", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
fmt.Fprint(w, `{"errors": [{"error": "service unavailable"}]}`)
})
server := httptest.NewServer(mux)
defer server.Close()
client, _ := NewClient(server.URL, nil)
_, _, err := client.Ping.Ping(context.Background())
if err == nil {
t.Fatal("expected error for 503 response")
}
}
func TestLicensesService_GetLicenses(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/licenses", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"licenses": [{"LicenseName": "matlab", "Total": 10, "Used": 3, "Free": 7, "Remote": false}]}`)
})
server := httptest.NewServer(mux)
defer server.Close()
client, _ := NewClient(server.URL, nil)
resp, _, err := client.Licenses.GetLicenses(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if len(resp.Licenses) != 1 {
t.Errorf("expected 1 license, got %d", len(resp.Licenses))
}
}
func TestLicensesService_GetLicenses_Error(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/licenses", 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.Licenses.GetLicenses(context.Background())
if err == nil {
t.Fatal("expected error for 500 response")
}
}
func TestReconfigureService_Reconfigure(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/reconfigure", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{}`)
})
server := httptest.NewServer(mux)
defer server.Close()
client, _ := NewClient(server.URL, nil)
resp, _, err := client.Reconfigure.Reconfigure(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
}
func TestReconfigureService_Reconfigure_Error(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/slurm/v0.0.40/reconfigure", 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.Reconfigure.Reconfigure(context.Background())
if err == nil {
t.Fatal("expected error for 500 response")
}
}

View File

@@ -0,0 +1,149 @@
package slurm
// ScheduleExitFields represents schedule exit fields (v0.0.40_schedule_exit_fields).
type ScheduleExitFields struct {
EndJobQueue *int32 `json:"end_job_queue,omitempty"`
DefaultQueueDepth *int32 `json:"default_queue_depth,omitempty"`
MaxJobStart *int32 `json:"max_job_start,omitempty"`
MaxRpcCnt *int32 `json:"max_rpc_cnt,omitempty"`
MaxSchedTime *int32 `json:"max_sched_time,omitempty"`
Licenses *int32 `json:"licenses,omitempty"`
}
// BfExitFields represents backfill exit fields (v0.0.40_bf_exit_fields).
type BfExitFields struct {
EndJobQueue *int32 `json:"end_job_queue,omitempty"`
BfMaxJobStart *int32 `json:"bf_max_job_start,omitempty"`
BfMaxJobTest *int32 `json:"bf_max_job_test,omitempty"`
BfMaxTime *int32 `json:"bf_max_time,omitempty"`
BfNodeSpaceSize *int32 `json:"bf_node_space_size,omitempty"`
StateChanged *int32 `json:"state_changed,omitempty"`
}
// StatsMsgRpcsByTypeItem represents a single RPC stats by message type entry.
type StatsMsgRpcsByTypeItem struct {
MessageType *string `json:"message_type,omitempty"`
TypeID *int32 `json:"type_id,omitempty"`
Count *int64 `json:"count,omitempty"`
AverageTime *int64 `json:"average_time,omitempty"`
TotalTime *int64 `json:"total_time,omitempty"`
}
// StatsMsgRpcsByType represents RPC stats by message type (v0.0.40_stats_msg_rpcs_by_type).
type StatsMsgRpcsByType []StatsMsgRpcsByTypeItem
// StatsMsgRpcsByUserItem represents a single RPC stats by user entry.
type StatsMsgRpcsByUserItem struct {
User *string `json:"user,omitempty"`
UserID *int32 `json:"user_id,omitempty"`
Count *int64 `json:"count,omitempty"`
AverageTime *int64 `json:"average_time,omitempty"`
TotalTime *int64 `json:"total_time,omitempty"`
}
// StatsMsgRpcsByUser represents RPC stats by user (v0.0.40_stats_msg_rpcs_by_user).
type StatsMsgRpcsByUser []StatsMsgRpcsByUserItem
// StatsMsg represents Slurm statistics message (v0.0.40_stats_msg).
type StatsMsg struct {
PartsPacked *int32 `json:"parts_packed,omitempty"`
ReqTime *Uint64NoVal `json:"req_time,omitempty"`
ReqTimeStart *Uint64NoVal `json:"req_time_start,omitempty"`
ServerThreadCount *int32 `json:"server_thread_count,omitempty"`
AgentQueueSize *int32 `json:"agent_queue_size,omitempty"`
AgentCount *int32 `json:"agent_count,omitempty"`
AgentThreadCount *int32 `json:"agent_thread_count,omitempty"`
DbdAgentQueueSize *int32 `json:"dbd_agent_queue_size,omitempty"`
GettimeofdayLatency *int32 `json:"gettimeofday_latency,omitempty"`
ScheduleCycleMax *int32 `json:"schedule_cycle_max,omitempty"`
ScheduleCycleLast *int32 `json:"schedule_cycle_last,omitempty"`
ScheduleCycleTotal *int32 `json:"schedule_cycle_total,omitempty"`
ScheduleCycleMean *int64 `json:"schedule_cycle_mean,omitempty"`
ScheduleCycleMeanDepth *int64 `json:"schedule_cycle_mean_depth,omitempty"`
ScheduleCyclePerMinute *int64 `json:"schedule_cycle_per_minute,omitempty"`
ScheduleQueueLength *int32 `json:"schedule_queue_length,omitempty"`
ScheduleExit *ScheduleExitFields `json:"schedule_exit,omitempty"`
JobsSubmitted *int32 `json:"jobs_submitted,omitempty"`
JobsStarted *int32 `json:"jobs_started,omitempty"`
JobsCompleted *int32 `json:"jobs_completed,omitempty"`
JobsCanceled *int32 `json:"jobs_canceled,omitempty"`
JobsFailed *int32 `json:"jobs_failed,omitempty"`
JobsPending *int32 `json:"jobs_pending,omitempty"`
JobsRunning *int32 `json:"jobs_running,omitempty"`
JobStatesTs *Uint64NoVal `json:"job_states_ts,omitempty"`
BfBackfilledJobs *int32 `json:"bf_backfilled_jobs,omitempty"`
BfLastBackfilledJobs *int32 `json:"bf_last_backfilled_jobs,omitempty"`
BfBackfilledHetJobs *int32 `json:"bf_backfilled_het_jobs,omitempty"`
BfCycleCounter *int32 `json:"bf_cycle_counter,omitempty"`
BfCycleMean *int64 `json:"bf_cycle_mean,omitempty"`
BfDepthMean *int64 `json:"bf_depth_mean,omitempty"`
BfDepthMeanTry *int64 `json:"bf_depth_mean_try,omitempty"`
BfCycleSum *int64 `json:"bf_cycle_sum,omitempty"`
BfCycleLast *int32 `json:"bf_cycle_last,omitempty"`
BfLastDepth *int32 `json:"bf_last_depth,omitempty"`
BfLastDepthTry *int32 `json:"bf_last_depth_try,omitempty"`
BfDepthSum *int32 `json:"bf_depth_sum,omitempty"`
BfDepthTrySum *int32 `json:"bf_depth_try_sum,omitempty"`
BfQueueLen *int32 `json:"bf_queue_len,omitempty"`
BfQueueLenMean *int64 `json:"bf_queue_len_mean,omitempty"`
BfQueueLenSum *int32 `json:"bf_queue_len_sum,omitempty"`
BfTableSize *int32 `json:"bf_table_size,omitempty"`
BfTableSizeMean *int64 `json:"bf_table_size_mean,omitempty"`
BfWhenLastCycle *Uint64NoVal `json:"bf_when_last_cycle,omitempty"`
BfActive *bool `json:"bf_active,omitempty"`
BfExit *BfExitFields `json:"bf_exit,omitempty"`
RpcsByMessageType *StatsMsgRpcsByType `json:"rpcs_by_message_type,omitempty"`
RpcsByUser *StatsMsgRpcsByUser `json:"rpcs_by_user,omitempty"`
}
// ControllerPing represents a controller ping result (v0.0.40_controller_ping).
type ControllerPing struct {
Hostname *string `json:"hostname,omitempty"`
Pinged *string `json:"pinged,omitempty"`
Latency *int64 `json:"latency,omitempty"`
Mode *string `json:"mode,omitempty"`
}
// ControllerPingArray represents an array of controller ping results (v0.0.40_controller_ping_array).
type ControllerPingArray []ControllerPing
// License represents Slurm license information (v0.0.40_license).
type License struct {
LicenseName *string `json:"LicenseName,omitempty"`
Total *int32 `json:"Total,omitempty"`
Used *int32 `json:"Used,omitempty"`
Free *int32 `json:"Free,omitempty"`
Remote *bool `json:"Remote,omitempty"`
Reserved *int32 `json:"Reserved,omitempty"`
LastConsumed *int32 `json:"LastConsumed,omitempty"`
LastDeficit *int32 `json:"LastDeficit,omitempty"`
LastUpdate *int64 `json:"LastUpdate,omitempty"`
}
// Licenses represents a list of licenses (v0.0.40_licenses).
type Licenses []License
// OpenapiDiagResp represents the diag API response (v0.0.40_openapi_diag_resp).
type OpenapiDiagResp struct {
Statistics *StatsMsg `json:"statistics,omitempty"`
Meta *OpenapiMeta `json:"meta,omitempty"`
Errors OpenapiErrors `json:"errors,omitempty"`
Warnings OpenapiWarnings `json:"warnings,omitempty"`
}
// OpenapiPingArrayResp represents the ping API response (v0.0.40_openapi_ping_array_resp).
type OpenapiPingArrayResp struct {
Pings ControllerPingArray `json:"pings,omitempty"`
Meta *OpenapiMeta `json:"meta,omitempty"`
Errors OpenapiErrors `json:"errors,omitempty"`
Warnings OpenapiWarnings `json:"warnings,omitempty"`
}
// OpenapiLicensesResp represents the licenses API response (v0.0.40_openapi_licenses_resp).
type OpenapiLicensesResp struct {
Licenses Licenses `json:"licenses,omitempty"`
LastUpdate *Uint64NoVal `json:"last_update,omitempty"`
Meta *OpenapiMeta `json:"meta,omitempty"`
Errors OpenapiErrors `json:"errors,omitempty"`
Warnings OpenapiWarnings `json:"warnings,omitempty"`
}

View File

@@ -0,0 +1,309 @@
package slurm
import (
"encoding/json"
"testing"
)
func TestStatsMsgRoundTrip(t *testing.T) {
orig := StatsMsg{
PartsPacked: Ptr(int32(1)),
ReqTime: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1700000000))},
ReqTimeStart: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1699999999))},
ServerThreadCount: Ptr(int32(4)),
AgentQueueSize: Ptr(int32(0)),
AgentCount: Ptr(int32(10)),
AgentThreadCount: Ptr(int32(4)),
DbdAgentQueueSize: Ptr(int32(0)),
GettimeofdayLatency: Ptr(int32(1)),
ScheduleCycleMax: Ptr(int32(100)),
ScheduleCycleLast: Ptr(int32(5)),
ScheduleCycleTotal: Ptr(int32(500)),
ScheduleCycleMean: Ptr(int64(10)),
ScheduleCycleMeanDepth: Ptr(int64(3)),
ScheduleCyclePerMinute: Ptr(int64(60)),
ScheduleQueueLength: Ptr(int32(42)),
ScheduleExit: &ScheduleExitFields{
EndJobQueue: Ptr(int32(1)),
DefaultQueueDepth: Ptr(int32(100)),
MaxJobStart: Ptr(int32(0)),
MaxRpcCnt: Ptr(int32(0)),
MaxSchedTime: Ptr(int32(0)),
Licenses: Ptr(int32(0)),
},
JobsSubmitted: Ptr(int32(1000)),
JobsStarted: Ptr(int32(900)),
JobsCompleted: Ptr(int32(850)),
JobsCanceled: Ptr(int32(20)),
JobsFailed: Ptr(int32(5)),
JobsPending: Ptr(int32(30)),
JobsRunning: Ptr(int32(45)),
JobStatesTs: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1700000000))},
BfBackfilledJobs: Ptr(int32(200)),
BfLastBackfilledJobs: Ptr(int32(5)),
BfBackfilledHetJobs: Ptr(int32(0)),
BfCycleCounter: Ptr(int32(500)),
BfCycleMean: Ptr(int64(2)),
BfDepthMean: Ptr(int64(10)),
BfDepthMeanTry: Ptr(int64(8)),
BfCycleSum: Ptr(int64(1000)),
BfCycleLast: Ptr(int32(3)),
BfLastDepth: Ptr(int32(15)),
BfLastDepthTry: Ptr(int32(12)),
BfDepthSum: Ptr(int32(5000)),
BfDepthTrySum: Ptr(int32(4000)),
BfQueueLen: Ptr(int32(30)),
BfQueueLenMean: Ptr(int64(25)),
BfQueueLenSum: Ptr(int32(15000)),
BfTableSize: Ptr(int32(500)),
BfTableSizeMean: Ptr(int64(450)),
BfWhenLastCycle: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1700000050))},
BfActive: Ptr(true),
BfExit: &BfExitFields{
EndJobQueue: Ptr(int32(0)),
BfMaxJobStart: Ptr(int32(0)),
BfMaxJobTest: Ptr(int32(0)),
BfMaxTime: Ptr(int32(0)),
BfNodeSpaceSize: Ptr(int32(0)),
StateChanged: Ptr(int32(0)),
},
RpcsByMessageType: &StatsMsgRpcsByType{
{MessageType: Ptr("REQUEST_JOB_INFO"), TypeID: Ptr(int32(2001)), Count: Ptr(int64(500)), AverageTime: Ptr(int64(10)), TotalTime: Ptr(int64(5000))},
{MessageType: Ptr("REQUEST_NODE_INFO"), TypeID: Ptr(int32(2002)), Count: Ptr(int64(300)), AverageTime: Ptr(int64(5)), TotalTime: Ptr(int64(1500))},
},
RpcsByUser: &StatsMsgRpcsByUser{
{User: Ptr("root"), UserID: Ptr(int32(0)), Count: Ptr(int64(100)), AverageTime: Ptr(int64(3)), TotalTime: Ptr(int64(300))},
{User: Ptr("alice"), UserID: Ptr(int32(1001)), Count: Ptr(int64(50)), AverageTime: Ptr(int64(7)), TotalTime: Ptr(int64(350))},
},
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded StatsMsg
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if *decoded.PartsPacked != *orig.PartsPacked {
t.Errorf("PartsPacked: got %d, want %d", *decoded.PartsPacked, *orig.PartsPacked)
}
if *decoded.ScheduleExit.DefaultQueueDepth != *orig.ScheduleExit.DefaultQueueDepth {
t.Errorf("ScheduleExit.DefaultQueueDepth: got %d, want %d", *decoded.ScheduleExit.DefaultQueueDepth, *orig.ScheduleExit.DefaultQueueDepth)
}
if *decoded.BfActive != *orig.BfActive {
t.Errorf("BfActive: got %v, want %v", *decoded.BfActive, *orig.BfActive)
}
if len(*decoded.RpcsByMessageType) != len(*orig.RpcsByMessageType) {
t.Errorf("RpcsByMessageType length: got %d, want %d", len(*decoded.RpcsByMessageType), len(*orig.RpcsByMessageType))
}
if len(*decoded.RpcsByUser) != len(*orig.RpcsByUser) {
t.Errorf("RpcsByUser length: got %d, want %d", len(*decoded.RpcsByUser), len(*orig.RpcsByUser))
}
if *(*decoded.RpcsByMessageType)[0].MessageType != *(*orig.RpcsByMessageType)[0].MessageType {
t.Errorf("RpcsByMessageType[0].MessageType: got %s, want %s", *(*decoded.RpcsByMessageType)[0].MessageType, *(*orig.RpcsByMessageType)[0].MessageType)
}
}
func TestScheduleExitFieldsRoundTrip(t *testing.T) {
orig := ScheduleExitFields{
EndJobQueue: Ptr(int32(1)),
DefaultQueueDepth: Ptr(int32(100)),
MaxJobStart: Ptr(int32(50)),
MaxRpcCnt: Ptr(int32(20)),
MaxSchedTime: Ptr(int32(30)),
Licenses: Ptr(int32(5)),
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded ScheduleExitFields
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if *decoded.EndJobQueue != *orig.EndJobQueue {
t.Errorf("EndJobQueue: got %d, want %d", *decoded.EndJobQueue, *orig.EndJobQueue)
}
if *decoded.Licenses != *orig.Licenses {
t.Errorf("Licenses: got %d, want %d", *decoded.Licenses, *orig.Licenses)
}
}
func TestBfExitFieldsRoundTrip(t *testing.T) {
orig := BfExitFields{
EndJobQueue: Ptr(int32(1)),
BfMaxJobStart: Ptr(int32(10)),
BfMaxJobTest: Ptr(int32(100)),
BfMaxTime: Ptr(int32(60)),
BfNodeSpaceSize: Ptr(int32(500)),
StateChanged: Ptr(int32(3)),
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded BfExitFields
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if *decoded.BfMaxJobTest != *orig.BfMaxJobTest {
t.Errorf("BfMaxJobTest: got %d, want %d", *decoded.BfMaxJobTest, *orig.BfMaxJobTest)
}
if *decoded.StateChanged != *orig.StateChanged {
t.Errorf("StateChanged: got %d, want %d", *decoded.StateChanged, *orig.StateChanged)
}
}
func TestControllerPingRoundTrip(t *testing.T) {
orig := ControllerPing{
Hostname: Ptr("controller1"),
Pinged: Ptr("UP"),
Latency: Ptr(int64(42)),
Mode: Ptr("primary"),
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded ControllerPing
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if *decoded.Hostname != *orig.Hostname {
t.Errorf("Hostname: got %s, want %s", *decoded.Hostname, *orig.Hostname)
}
if *decoded.Latency != *orig.Latency {
t.Errorf("Latency: got %d, want %d", *decoded.Latency, *orig.Latency)
}
}
func TestLicenseRoundTrip(t *testing.T) {
orig := License{
LicenseName: Ptr("matlab"),
Total: Ptr(int32(100)),
Used: Ptr(int32(50)),
Free: Ptr(int32(50)),
Remote: Ptr(false),
Reserved: Ptr(int32(10)),
LastConsumed: Ptr(int32(45)),
LastDeficit: Ptr(int32(0)),
LastUpdate: Ptr(int64(1700000000)),
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded License
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if *decoded.LicenseName != *orig.LicenseName {
t.Errorf("LicenseName: got %s, want %s", *decoded.LicenseName, *orig.LicenseName)
}
if *decoded.Total != *orig.Total {
t.Errorf("Total: got %d, want %d", *decoded.Total, *orig.Total)
}
if *decoded.Remote != *orig.Remote {
t.Errorf("Remote: got %v, want %v", *decoded.Remote, *orig.Remote)
}
}
func TestOpenapiDiagRespRoundTrip(t *testing.T) {
orig := OpenapiDiagResp{
Statistics: &StatsMsg{
PartsPacked: Ptr(int32(1)),
ServerThreadCount: Ptr(int32(4)),
AgentCount: Ptr(int32(10)),
JobsRunning: Ptr(int32(45)),
BfActive: Ptr(true),
},
Meta: &OpenapiMeta{
Slurm: &MetaSlurm{
Version: &MetaSlurmVersion{Major: Ptr("24"), Minor: Ptr("05"), Micro: Ptr("5")},
Release: Ptr("24.05.5"),
},
},
Errors: OpenapiErrors{},
Warnings: OpenapiWarnings{},
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded OpenapiDiagResp
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if *decoded.Statistics.PartsPacked != *orig.Statistics.PartsPacked {
t.Errorf("Statistics.PartsPacked: got %d, want %d", *decoded.Statistics.PartsPacked, *orig.Statistics.PartsPacked)
}
if *decoded.Meta.Slurm.Release != *orig.Meta.Slurm.Release {
t.Errorf("Meta.Slurm.Release: got %s, want %s", *decoded.Meta.Slurm.Release, *orig.Meta.Slurm.Release)
}
}
func TestOpenapiPingArrayRespRoundTrip(t *testing.T) {
orig := OpenapiPingArrayResp{
Pings: ControllerPingArray{
{Hostname: Ptr("ctrl1"), Pinged: Ptr("UP"), Latency: Ptr(int64(10))},
{Hostname: Ptr("ctrl2"), Pinged: Ptr("DOWN"), Latency: Ptr(int64(9999))},
},
Meta: &OpenapiMeta{
Slurm: &MetaSlurm{
Version: &MetaSlurmVersion{Major: Ptr("24"), Minor: Ptr("05"), Micro: Ptr("5")},
},
},
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded OpenapiPingArrayResp
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if len(decoded.Pings) != len(orig.Pings) {
t.Fatalf("Pings length: got %d, want %d", len(decoded.Pings), len(orig.Pings))
}
if *decoded.Pings[0].Hostname != *orig.Pings[0].Hostname {
t.Errorf("Pings[0].Hostname: got %s, want %s", *decoded.Pings[0].Hostname, *orig.Pings[0].Hostname)
}
if *decoded.Pings[1].Pinged != *orig.Pings[1].Pinged {
t.Errorf("Pings[1].Pinged: got %s, want %s", *decoded.Pings[1].Pinged, *orig.Pings[1].Pinged)
}
}
func TestOpenapiLicensesRespRoundTrip(t *testing.T) {
orig := OpenapiLicensesResp{
Licenses: Licenses{
{LicenseName: Ptr("matlab"), Total: Ptr(int32(100)), Used: Ptr(int32(50))},
{LicenseName: Ptr("ansys"), Total: Ptr(int32(10)), Used: Ptr(int32(3))},
},
LastUpdate: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1700000000))},
Meta: &OpenapiMeta{
Slurm: &MetaSlurm{
Version: &MetaSlurmVersion{Major: Ptr("24"), Minor: Ptr("05"), Micro: Ptr("5")},
},
},
}
data, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var decoded OpenapiLicensesResp
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if len(decoded.Licenses) != len(orig.Licenses) {
t.Fatalf("Licenses length: got %d, want %d", len(decoded.Licenses), len(orig.Licenses))
}
if *decoded.Licenses[0].LicenseName != *orig.Licenses[0].LicenseName {
t.Errorf("Licenses[0].LicenseName: got %s, want %s", *decoded.Licenses[0].LicenseName, *orig.Licenses[0].LicenseName)
}
if *decoded.Licenses[1].Used != *orig.Licenses[1].Used {
t.Errorf("Licenses[1].Used: got %d, want %d", *decoded.Licenses[1].Used, *orig.Licenses[1].Used)
}
if *decoded.LastUpdate.Number != *orig.LastUpdate.Number {
t.Errorf("LastUpdate.Number: got %d, want %d", *decoded.LastUpdate.Number, *orig.LastUpdate.Number)
}
}