feat(slurmdb): add all slurmdb response types and supporting types
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
309
internal/slurm/types_slurmdb.go
Normal file
309
internal/slurm/types_slurmdb.go
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
package slurm
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// SlurmDBD Response wrapper types — v0.0.40 OpenAPI SlurmDBD schemas
|
||||||
|
// All fields are optional (pointer types) with json:"name,omitempty".
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// OpenapiSlurmdbdJobsResp is the response for listing SlurmDBD jobs.
|
||||||
|
// Corresponds to v0.0.40_openapi_slurmdbd_jobs_resp.
|
||||||
|
type OpenapiSlurmdbdJobsResp struct {
|
||||||
|
Jobs JobList `json:"jobs,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiSlurmdbdConfigResp is the response for SlurmDBD configuration.
|
||||||
|
// Corresponds to v0.0.40_openapi_slurmdbd_config_resp.
|
||||||
|
type OpenapiSlurmdbdConfigResp struct {
|
||||||
|
Clusters ClusterRecList `json:"clusters,omitempty"`
|
||||||
|
Tres TresList `json:"tres,omitempty"`
|
||||||
|
Accounts AccountList `json:"accounts,omitempty"`
|
||||||
|
Users UserList `json:"users,omitempty"`
|
||||||
|
Qos QosList `json:"qos,omitempty"`
|
||||||
|
Wckeys WckeyList `json:"wckeys,omitempty"`
|
||||||
|
Associations AssocList `json:"associations,omitempty"`
|
||||||
|
Instances InstanceList `json:"instances,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiSlurmdbdQosResp is the response for listing QOS.
|
||||||
|
// Corresponds to v0.0.40_openapi_slurmdbd_qos_resp.
|
||||||
|
type OpenapiSlurmdbdQosResp struct {
|
||||||
|
Qos QosList `json:"qos,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiSlurmdbdQosRemovedResp is the response for removed QOS.
|
||||||
|
// Corresponds to v0.0.40_openapi_slurmdbd_qos_removed_resp.
|
||||||
|
type OpenapiSlurmdbdQosRemovedResp struct {
|
||||||
|
RemovedQos StringList `json:"removed_qos,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiSlurmdbdStatsResp is the response for SlurmDBD diagnostics.
|
||||||
|
// Corresponds to v0.0.40_openapi_slurmdbd_stats_resp.
|
||||||
|
type OpenapiSlurmdbdStatsResp struct {
|
||||||
|
Statistics *StatsRec `json:"statistics,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiAssocsResp is the response for listing associations.
|
||||||
|
// Corresponds to v0.0.40_openapi_assocs_resp.
|
||||||
|
type OpenapiAssocsResp struct {
|
||||||
|
Associations AssocList `json:"associations,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiAssocsRemovedResp is the response for removed associations.
|
||||||
|
// Corresponds to v0.0.40_openapi_assocs_removed_resp.
|
||||||
|
type OpenapiAssocsRemovedResp struct {
|
||||||
|
RemovedAssociations StringList `json:"removed_associations,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiUsersResp is the response for listing users.
|
||||||
|
// Corresponds to v0.0.40_openapi_users_resp.
|
||||||
|
type OpenapiUsersResp struct {
|
||||||
|
Users UserList `json:"users,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiClustersResp is the response for listing clusters.
|
||||||
|
// Corresponds to v0.0.40_openapi_clusters_resp.
|
||||||
|
type OpenapiClustersResp struct {
|
||||||
|
Clusters ClusterRecList `json:"clusters,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiClustersRemovedResp is the response for deleted clusters.
|
||||||
|
// Corresponds to v0.0.40_openapi_clusters_removed_resp.
|
||||||
|
type OpenapiClustersRemovedResp struct {
|
||||||
|
DeletedClusters StringList `json:"deleted_clusters,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiWckeyResp is the response for listing WCKeys.
|
||||||
|
// Corresponds to v0.0.40_openapi_wckey_resp.
|
||||||
|
type OpenapiWckeyResp struct {
|
||||||
|
Wckeys WckeyList `json:"wckeys,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiWckeyRemovedResp is the response for deleted WCKeys.
|
||||||
|
// Corresponds to v0.0.40_openapi_wckey_removed_resp.
|
||||||
|
type OpenapiWckeyRemovedResp struct {
|
||||||
|
DeletedWckeys StringList `json:"deleted_wckeys,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiAccountsResp is the response for listing accounts.
|
||||||
|
// Corresponds to v0.0.40_openapi_accounts_resp.
|
||||||
|
type OpenapiAccountsResp struct {
|
||||||
|
Accounts AccountList `json:"accounts,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiAccountsRemovedResp is the response for removed accounts.
|
||||||
|
// Corresponds to v0.0.40_openapi_accounts_removed_resp.
|
||||||
|
type OpenapiAccountsRemovedResp struct {
|
||||||
|
RemovedAccounts StringList `json:"removed_accounts,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiInstancesResp is the response for listing instances.
|
||||||
|
// Corresponds to v0.0.40_openapi_instances_resp.
|
||||||
|
type OpenapiInstancesResp struct {
|
||||||
|
Instances InstanceList `json:"instances,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiTresResp is the response for listing TRES.
|
||||||
|
// Corresponds to v0.0.40_openapi_tres_resp.
|
||||||
|
// Note: JSON key for TRES is UPPERCASE per OpenAPI spec.
|
||||||
|
type OpenapiTresResp struct {
|
||||||
|
TRES TresList `json:"TRES,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiUsersAddCondResp is the response for adding users with conditions.
|
||||||
|
// Corresponds to v0.0.40_openapi_users_add_cond_resp.
|
||||||
|
type OpenapiUsersAddCondResp struct {
|
||||||
|
AssociationCondition *UsersAddCond `json:"association_condition,omitempty"`
|
||||||
|
User *UserShort `json:"user,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiUsersAddCondRespStr is the string response for adding users with conditions.
|
||||||
|
// Corresponds to v0.0.40_openapi_users_add_cond_resp_str.
|
||||||
|
type OpenapiUsersAddCondRespStr struct {
|
||||||
|
AddedUsers *string `json:"added_users,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiAccountsAddCondResp is the response for adding accounts with conditions.
|
||||||
|
// Corresponds to v0.0.40_openapi_accounts_add_cond_resp.
|
||||||
|
type OpenapiAccountsAddCondResp struct {
|
||||||
|
AssociationCondition *AccountsAddCond `json:"association_condition,omitempty"`
|
||||||
|
Account *AccountShort `json:"account,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenapiAccountsAddCondRespStr is the string response for adding accounts with conditions.
|
||||||
|
// Corresponds to v0.0.40_openapi_accounts_add_cond_resp_str.
|
||||||
|
type OpenapiAccountsAddCondRespStr struct {
|
||||||
|
AddedAccounts *string `json:"added_accounts,omitempty"`
|
||||||
|
Meta *OpenapiMeta `json:"meta,omitempty"`
|
||||||
|
Errors OpenapiErrors `json:"errors,omitempty"`
|
||||||
|
Warnings OpenapiWarnings `json:"warnings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Supporting types for SlurmDBD diagnostics
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// StatsRec represents SlurmDBD diagnostic statistics.
|
||||||
|
// Corresponds to v0.0.40_stats_rec.
|
||||||
|
type StatsRec struct {
|
||||||
|
TimeStart *int64 `json:"time_start,omitempty"`
|
||||||
|
Rollups RollupStatsList `json:"rollups,omitempty"`
|
||||||
|
RPCs StatsRpcList `json:"RPCs,omitempty"`
|
||||||
|
Users StatsUserList `json:"users,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RollupStatsList is a list of RollupStats.
|
||||||
|
// Corresponds to v0.0.40_rollup_stats.
|
||||||
|
type RollupStatsList []RollupStats
|
||||||
|
|
||||||
|
// RollupStats represents recorded rollup statistics.
|
||||||
|
type RollupStats struct {
|
||||||
|
Type *string `json:"type,omitempty"`
|
||||||
|
LastRun *int32 `json:"last run,omitempty"`
|
||||||
|
MaxCycle *int64 `json:"max_cycle,omitempty"`
|
||||||
|
TotalTime *int64 `json:"total_time,omitempty"`
|
||||||
|
TotalCycles *int64 `json:"total_cycles,omitempty"`
|
||||||
|
MeanCycles *int64 `json:"mean_cycles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsRpcList is a list of StatsRpc.
|
||||||
|
// Corresponds to v0.0.40_stats_rpc_list.
|
||||||
|
type StatsRpcList []StatsRpc
|
||||||
|
|
||||||
|
// StatsRpc represents RPC statistics.
|
||||||
|
// Corresponds to v0.0.40_stats_rpc.
|
||||||
|
type StatsRpc struct {
|
||||||
|
RPC *string `json:"rpc,omitempty"`
|
||||||
|
Count *int32 `json:"count,omitempty"`
|
||||||
|
Time *StatsRpcTime `json:"time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsRpcTime represents timing information for RPC statistics.
|
||||||
|
type StatsRpcTime struct {
|
||||||
|
Average *int64 `json:"average,omitempty"`
|
||||||
|
Total *int64 `json:"total,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsUserList is a list of StatsUser.
|
||||||
|
// Corresponds to v0.0.40_stats_user_list.
|
||||||
|
type StatsUserList []StatsUser
|
||||||
|
|
||||||
|
// StatsUser represents per-user statistics.
|
||||||
|
// Corresponds to v0.0.40_stats_user.
|
||||||
|
type StatsUser struct {
|
||||||
|
User *string `json:"user,omitempty"`
|
||||||
|
Count *int32 `json:"count,omitempty"`
|
||||||
|
Time *StatsUserTime `json:"time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsUserTime represents timing information for user statistics.
|
||||||
|
type StatsUserTime struct {
|
||||||
|
Average *int64 `json:"average,omitempty"`
|
||||||
|
Total *int64 `json:"total,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// UsersAddCond and AccountsAddCond — association condition types
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// UsersAddCond represents filters to select associations for users.
|
||||||
|
// Corresponds to v0.0.40_users_add_cond.
|
||||||
|
type UsersAddCond struct {
|
||||||
|
Accounts StringList `json:"accounts,omitempty"`
|
||||||
|
Association *AssocRecSet `json:"association,omitempty"`
|
||||||
|
Clusters StringList `json:"clusters,omitempty"`
|
||||||
|
Partitions StringList `json:"partitions,omitempty"`
|
||||||
|
Users StringList `json:"users,omitempty"`
|
||||||
|
Wckeys StringList `json:"wckeys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountsAddCond represents filters for adding accounts.
|
||||||
|
// Corresponds to v0.0.40_accounts_add_cond.
|
||||||
|
type AccountsAddCond struct {
|
||||||
|
Accounts StringList `json:"accounts,omitempty"`
|
||||||
|
Association *AssocRecSet `json:"association,omitempty"`
|
||||||
|
Clusters StringList `json:"clusters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssocRecSet represents association limits and options for user/account creation.
|
||||||
|
// Corresponds to v0.0.40_assoc_rec_set.
|
||||||
|
type AssocRecSet struct {
|
||||||
|
Comment *string `json:"comment,omitempty"`
|
||||||
|
Defaultqos *string `json:"defaultqos,omitempty"`
|
||||||
|
Grpjobs *Uint32NoVal `json:"grpjobs,omitempty"`
|
||||||
|
Grpjobsaccrue *Uint32NoVal `json:"grpjobsaccrue,omitempty"`
|
||||||
|
Grpsubmitjobs *Uint32NoVal `json:"grpsubmitjobs,omitempty"`
|
||||||
|
Grptres TresList `json:"grptres,omitempty"`
|
||||||
|
Grptresmins TresList `json:"grptresmins,omitempty"`
|
||||||
|
Grptresrunmins TresList `json:"grptresrunmins,omitempty"`
|
||||||
|
Grpwall *Uint32NoVal `json:"grpwall,omitempty"`
|
||||||
|
Maxjobs *Uint32NoVal `json:"maxjobs,omitempty"`
|
||||||
|
Maxjobsaccrue *Uint32NoVal `json:"maxjobsaccrue,omitempty"`
|
||||||
|
Maxsubmitjobs *Uint32NoVal `json:"maxsubmitjobs,omitempty"`
|
||||||
|
Maxtresminsperjob TresList `json:"maxtresminsperjob,omitempty"`
|
||||||
|
Maxtresrunmins TresList `json:"maxtresrunmins,omitempty"`
|
||||||
|
Maxtresperjob TresList `json:"maxtresperjob,omitempty"`
|
||||||
|
Maxtrespernode TresList `json:"maxtrespernode,omitempty"`
|
||||||
|
Maxwalldurationperjob *Uint32NoVal `json:"maxwalldurationperjob,omitempty"`
|
||||||
|
Minpriothresh *Uint32NoVal `json:"minpriothresh,omitempty"`
|
||||||
|
Parent *string `json:"parent,omitempty"`
|
||||||
|
Priority *Uint32NoVal `json:"priority,omitempty"`
|
||||||
|
Qoslevel QosStringIdList `json:"qoslevel,omitempty"`
|
||||||
|
Fairshare *int32 `json:"fairshare,omitempty"`
|
||||||
|
}
|
||||||
816
internal/slurm/types_slurmdb_test.go
Normal file
816
internal/slurm/types_slurmdb_test.go
Normal file
@@ -0,0 +1,816 @@
|
|||||||
|
package slurm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSlurmdbJobsRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiSlurmdbdJobsResp{
|
||||||
|
Jobs: JobList{
|
||||||
|
{JobID: Ptr(int32(1)), Name: Ptr("test-job")},
|
||||||
|
{JobID: Ptr(int32(2)), Name: Ptr("other-job")},
|
||||||
|
},
|
||||||
|
Meta: &OpenapiMeta{},
|
||||||
|
Errors: OpenapiErrors{},
|
||||||
|
Warnings: OpenapiWarnings{},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiSlurmdbdJobsResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Jobs) != 2 {
|
||||||
|
t.Fatalf("expected 2 jobs, got %d", len(decoded.Jobs))
|
||||||
|
}
|
||||||
|
if *decoded.Jobs[0].JobID != 1 {
|
||||||
|
t.Fatal("first job id mismatch")
|
||||||
|
}
|
||||||
|
if *decoded.Jobs[1].Name != "other-job" {
|
||||||
|
t.Fatal("second job name mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbConfigRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiSlurmdbdConfigResp{
|
||||||
|
Clusters: ClusterRecList{
|
||||||
|
{Name: Ptr("cluster1")},
|
||||||
|
},
|
||||||
|
Tres: TresList{
|
||||||
|
{Type: Ptr("cpu"), ID: Ptr(int32(1))},
|
||||||
|
},
|
||||||
|
Accounts: AccountList{
|
||||||
|
{Name: Ptr("acct1")},
|
||||||
|
},
|
||||||
|
Users: UserList{
|
||||||
|
{Name: Ptr("user1")},
|
||||||
|
},
|
||||||
|
Qos: QosList{
|
||||||
|
{Name: Ptr("normal")},
|
||||||
|
},
|
||||||
|
Wckeys: WckeyList{
|
||||||
|
{Name: Ptr("wckey1")},
|
||||||
|
},
|
||||||
|
Associations: AssocList{
|
||||||
|
{Account: Ptr("acct1"), User: Ptr("user1")},
|
||||||
|
},
|
||||||
|
Instances: InstanceList{
|
||||||
|
{InstanceID: Ptr("i-123")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify lowercase "tres" key in JSON
|
||||||
|
raw := string(data)
|
||||||
|
if strings.Contains(raw, `"TRES"`) {
|
||||||
|
t.Fatal("OpenapiSlurmdbdConfigResp should use lowercase 'tres', not uppercase 'TRES'")
|
||||||
|
}
|
||||||
|
if !strings.Contains(raw, `"tres"`) {
|
||||||
|
t.Fatal("OpenapiSlurmdbdConfigResp should contain lowercase 'tres' key")
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiSlurmdbdConfigResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Clusters) != 1 || *decoded.Clusters[0].Name != "cluster1" {
|
||||||
|
t.Fatal("clusters mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Tres) != 1 || *decoded.Tres[0].Type != "cpu" {
|
||||||
|
t.Fatal("tres mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Accounts) != 1 || *decoded.Accounts[0].Name != "acct1" {
|
||||||
|
t.Fatal("accounts mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Users) != 1 || *decoded.Users[0].Name != "user1" {
|
||||||
|
t.Fatal("users mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Qos) != 1 || *decoded.Qos[0].Name != "normal" {
|
||||||
|
t.Fatal("qos mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Wckeys) != 1 || *decoded.Wckeys[0].Name != "wckey1" {
|
||||||
|
t.Fatal("wckeys mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Associations) != 1 || *decoded.Associations[0].Account != "acct1" {
|
||||||
|
t.Fatal("associations mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Instances) != 1 || *decoded.Instances[0].InstanceID != "i-123" {
|
||||||
|
t.Fatal("instances mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbQosRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiSlurmdbdQosResp{
|
||||||
|
Qos: QosList{
|
||||||
|
{Name: Ptr("high"), ID: Ptr(int32(1))},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiSlurmdbdQosResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Qos) != 1 || *decoded.Qos[0].Name != "high" {
|
||||||
|
t.Fatal("qos mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbQosRemovedRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiSlurmdbdQosRemovedResp{
|
||||||
|
RemovedQos: StringList{"normal", "high"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiSlurmdbdQosRemovedResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.RemovedQos) != 2 || decoded.RemovedQos[0] != "normal" {
|
||||||
|
t.Fatal("removed_qos mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbStatsRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiSlurmdbdStatsResp{
|
||||||
|
Statistics: &StatsRec{
|
||||||
|
TimeStart: Ptr(int64(1700000000)),
|
||||||
|
Rollups: RollupStatsList{
|
||||||
|
{Type: Ptr("internal"), MaxCycle: Ptr(int64(5))},
|
||||||
|
},
|
||||||
|
RPCs: StatsRpcList{
|
||||||
|
{RPC: Ptr("SLURMCTLD_GET_JOB_INFO"), Count: Ptr(int32(100))},
|
||||||
|
},
|
||||||
|
Users: StatsUserList{
|
||||||
|
{User: Ptr("root"), Count: Ptr(int32(50))},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiSlurmdbdStatsResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if decoded.Statistics == nil {
|
||||||
|
t.Fatal("statistics is nil")
|
||||||
|
}
|
||||||
|
if *decoded.Statistics.TimeStart != 1700000000 {
|
||||||
|
t.Fatal("time_start mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Statistics.Rollups) != 1 || *decoded.Statistics.Rollups[0].Type != "internal" {
|
||||||
|
t.Fatal("rollups mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Statistics.RPCs) != 1 || *decoded.Statistics.RPCs[0].RPC != "SLURMCTLD_GET_JOB_INFO" {
|
||||||
|
t.Fatal("RPCs mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Statistics.Users) != 1 || *decoded.Statistics.Users[0].User != "root" {
|
||||||
|
t.Fatal("users mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAssocsRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiAssocsResp{
|
||||||
|
Associations: AssocList{
|
||||||
|
{Account: Ptr("acct1"), User: Ptr("user1")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiAssocsResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Associations) != 1 || *decoded.Associations[0].Account != "acct1" {
|
||||||
|
t.Fatal("associations mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAssocsRemovedRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiAssocsRemovedResp{
|
||||||
|
RemovedAssociations: StringList{"acct1_user1_cluster1"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiAssocsRemovedResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.RemovedAssociations) != 1 || decoded.RemovedAssociations[0] != "acct1_user1_cluster1" {
|
||||||
|
t.Fatal("removed_associations mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbUsersRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiUsersResp{
|
||||||
|
Users: UserList{
|
||||||
|
{Name: Ptr("admin"), AdministratorLevel: []string{"Administrator"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiUsersResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Users) != 1 || *decoded.Users[0].Name != "admin" {
|
||||||
|
t.Fatal("users mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbClustersRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiClustersResp{
|
||||||
|
Clusters: ClusterRecList{
|
||||||
|
{Name: Ptr("test-cluster"), Nodes: Ptr("node[1-10]")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiClustersResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Clusters) != 1 || *decoded.Clusters[0].Name != "test-cluster" {
|
||||||
|
t.Fatal("clusters mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbClustersRemovedRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiClustersRemovedResp{
|
||||||
|
DeletedClusters: StringList{"old-cluster"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiClustersRemovedResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.DeletedClusters) != 1 || decoded.DeletedClusters[0] != "old-cluster" {
|
||||||
|
t.Fatal("deleted_clusters mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbWckeyRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiWckeyResp{
|
||||||
|
Wckeys: WckeyList{
|
||||||
|
{Name: Ptr("wckey1"), Cluster: Ptr("cluster1")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiWckeyResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Wckeys) != 1 || *decoded.Wckeys[0].Name != "wckey1" {
|
||||||
|
t.Fatal("wckeys mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbWckeyRemovedRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiWckeyRemovedResp{
|
||||||
|
DeletedWckeys: StringList{"old-wckey"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiWckeyRemovedResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.DeletedWckeys) != 1 || decoded.DeletedWckeys[0] != "old-wckey" {
|
||||||
|
t.Fatal("deleted_wckeys mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAccountsRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiAccountsResp{
|
||||||
|
Accounts: AccountList{
|
||||||
|
{Name: Ptr("science"), Description: Ptr("Science dept"), Organization: Ptr("org1")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiAccountsResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Accounts) != 1 || *decoded.Accounts[0].Name != "science" {
|
||||||
|
t.Fatal("accounts mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAccountsRemovedRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiAccountsRemovedResp{
|
||||||
|
RemovedAccounts: StringList{"old-acct"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiAccountsRemovedResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.RemovedAccounts) != 1 || decoded.RemovedAccounts[0] != "old-acct" {
|
||||||
|
t.Fatal("removed_accounts mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbInstancesRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiInstancesResp{
|
||||||
|
Instances: InstanceList{
|
||||||
|
{InstanceID: Ptr("i-abc123"), Cluster: Ptr("cluster1")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiInstancesResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Instances) != 1 || *decoded.Instances[0].InstanceID != "i-abc123" {
|
||||||
|
t.Fatal("instances mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbTresRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiTresResp{
|
||||||
|
TRES: TresList{
|
||||||
|
{Type: Ptr("cpu"), ID: Ptr(int32(1)), Count: Ptr(int64(100))},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify UPPERCASE "TRES" key in JSON
|
||||||
|
raw := string(data)
|
||||||
|
if !strings.Contains(raw, `"TRES"`) {
|
||||||
|
t.Fatalf("OpenapiTresResp should use UPPERCASE 'TRES' key, got: %s", raw)
|
||||||
|
}
|
||||||
|
if strings.Contains(raw, `"tres"`) {
|
||||||
|
t.Fatal("OpenapiTresResp should NOT contain lowercase 'tres'")
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiTresResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.TRES) != 1 || *decoded.TRES[0].Type != "cpu" {
|
||||||
|
t.Fatal("TRES mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbTresRespUppercaseKey(t *testing.T) {
|
||||||
|
raw := `{"TRES": [{"type": "mem", "id": 2, "count": 1000}]}`
|
||||||
|
var decoded OpenapiTresResp
|
||||||
|
if err := json.Unmarshal([]byte(raw), &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(decoded.TRES) != 1 || *decoded.TRES[0].Type != "mem" {
|
||||||
|
t.Fatal("TRES uppercase key parse failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbConfigRespLowercaseTresKey(t *testing.T) {
|
||||||
|
raw := `{"tres": [{"type": "cpu", "id": 1, "count": 100}]}`
|
||||||
|
var decoded OpenapiSlurmdbdConfigResp
|
||||||
|
if err := json.Unmarshal([]byte(raw), &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(decoded.Tres) != 1 || *decoded.Tres[0].Type != "cpu" {
|
||||||
|
t.Fatal("config resp lowercase tres key parse failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbUsersAddCondRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiUsersAddCondResp{
|
||||||
|
AssociationCondition: &UsersAddCond{
|
||||||
|
Accounts: StringList{"acct1", "acct2"},
|
||||||
|
Clusters: StringList{"cluster1"},
|
||||||
|
Users: StringList{"user1", "user2"},
|
||||||
|
Wckeys: StringList{"wckey1"},
|
||||||
|
Partitions: StringList{"partition1"},
|
||||||
|
},
|
||||||
|
User: &UserShort{
|
||||||
|
Adminlevel: []string{"Administrator"},
|
||||||
|
Defaultaccount: Ptr("acct1"),
|
||||||
|
Defaultwckey: Ptr("wckey1"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiUsersAddCondResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if decoded.AssociationCondition == nil {
|
||||||
|
t.Fatal("association_condition is nil")
|
||||||
|
}
|
||||||
|
if len(decoded.AssociationCondition.Accounts) != 2 {
|
||||||
|
t.Fatal("accounts count mismatch")
|
||||||
|
}
|
||||||
|
if decoded.User == nil || *decoded.User.Defaultaccount != "acct1" {
|
||||||
|
t.Fatal("user mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbUsersAddCondRespStrRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiUsersAddCondRespStr{
|
||||||
|
AddedUsers: Ptr("user1,user2,user3"),
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiUsersAddCondRespStr
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if decoded.AddedUsers == nil || *decoded.AddedUsers != "user1,user2,user3" {
|
||||||
|
t.Fatal("added_users mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAccountsAddCondRespRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiAccountsAddCondResp{
|
||||||
|
AssociationCondition: &AccountsAddCond{
|
||||||
|
Accounts: StringList{"acct1"},
|
||||||
|
Clusters: StringList{"cluster1"},
|
||||||
|
},
|
||||||
|
Account: &AccountShort{
|
||||||
|
Description: Ptr("Science department"),
|
||||||
|
Organization: Ptr("university"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiAccountsAddCondResp
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if decoded.AssociationCondition == nil || len(decoded.AssociationCondition.Accounts) != 1 {
|
||||||
|
t.Fatal("association_condition mismatch")
|
||||||
|
}
|
||||||
|
if decoded.Account == nil || *decoded.Account.Description != "Science department" {
|
||||||
|
t.Fatal("account mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAccountsAddCondRespStrRoundTrip(t *testing.T) {
|
||||||
|
orig := OpenapiAccountsAddCondRespStr{
|
||||||
|
AddedAccounts: Ptr("acct1,acct2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded OpenapiAccountsAddCondRespStr
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if decoded.AddedAccounts == nil || *decoded.AddedAccounts != "acct1,acct2" {
|
||||||
|
t.Fatal("added_accounts mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbStatsRecRoundTrip(t *testing.T) {
|
||||||
|
orig := StatsRec{
|
||||||
|
TimeStart: Ptr(int64(1700000000)),
|
||||||
|
Rollups: RollupStatsList{
|
||||||
|
{
|
||||||
|
Type: Ptr("internal"),
|
||||||
|
LastRun: Ptr(int32(1700001000)),
|
||||||
|
MaxCycle: Ptr(int64(10)),
|
||||||
|
TotalTime: Ptr(int64(100)),
|
||||||
|
TotalCycles: Ptr(int64(50)),
|
||||||
|
MeanCycles: Ptr(int64(2)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RPCs: StatsRpcList{
|
||||||
|
{
|
||||||
|
RPC: Ptr("GET_JOBS"),
|
||||||
|
Count: Ptr(int32(500)),
|
||||||
|
Time: &StatsRpcTime{
|
||||||
|
Average: Ptr(int64(100)),
|
||||||
|
Total: Ptr(int64(50000)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Users: StatsUserList{
|
||||||
|
{
|
||||||
|
User: Ptr("admin"),
|
||||||
|
Count: Ptr(int32(200)),
|
||||||
|
Time: &StatsUserTime{
|
||||||
|
Average: Ptr(int64(50)),
|
||||||
|
Total: Ptr(int64(10000)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded StatsRec
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *decoded.TimeStart != 1700000000 {
|
||||||
|
t.Fatal("time_start mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Rollups) != 1 || *decoded.Rollups[0].Type != "internal" {
|
||||||
|
t.Fatal("rollups mismatch")
|
||||||
|
}
|
||||||
|
if *decoded.Rollups[0].MaxCycle != 10 {
|
||||||
|
t.Fatal("max_cycle mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.RPCs) != 1 || *decoded.RPCs[0].RPC != "GET_JOBS" {
|
||||||
|
t.Fatal("RPCs mismatch")
|
||||||
|
}
|
||||||
|
if decoded.RPCs[0].Time == nil || *decoded.RPCs[0].Time.Average != 100 {
|
||||||
|
t.Fatal("RPC time.average mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Users) != 1 || *decoded.Users[0].User != "admin" {
|
||||||
|
t.Fatal("users mismatch")
|
||||||
|
}
|
||||||
|
if decoded.Users[0].Time == nil || *decoded.Users[0].Time.Total != 10000 {
|
||||||
|
t.Fatal("user time.total mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbStatsRecRPCsUppercaseKey(t *testing.T) {
|
||||||
|
raw := `{"time_start": 1700000000, "RPCs": [{"rpc": "TEST", "count": 1}]}`
|
||||||
|
var decoded StatsRec
|
||||||
|
if err := json.Unmarshal([]byte(raw), &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(decoded.RPCs) != 1 || *decoded.RPCs[0].RPC != "TEST" {
|
||||||
|
t.Fatal("RPCs uppercase key parse failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbRollupStatsRoundTrip(t *testing.T) {
|
||||||
|
orig := RollupStats{
|
||||||
|
Type: Ptr("user"),
|
||||||
|
LastRun: Ptr(int32(1700002000)),
|
||||||
|
MaxCycle: Ptr(int64(20)),
|
||||||
|
TotalTime: Ptr(int64(200)),
|
||||||
|
TotalCycles: Ptr(int64(100)),
|
||||||
|
MeanCycles: Ptr(int64(2)),
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded RollupStats
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *decoded.Type != "user" {
|
||||||
|
t.Fatal("type mismatch")
|
||||||
|
}
|
||||||
|
if *decoded.LastRun != 1700002000 {
|
||||||
|
t.Fatal("last run mismatch")
|
||||||
|
}
|
||||||
|
if *decoded.TotalCycles != 100 {
|
||||||
|
t.Fatal("total_cycles mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAssocRecSetRoundTrip(t *testing.T) {
|
||||||
|
orig := AssocRecSet{
|
||||||
|
Comment: Ptr("test comment"),
|
||||||
|
Defaultqos: Ptr("normal"),
|
||||||
|
Fairshare: Ptr(int32(100)),
|
||||||
|
Parent: Ptr("parent-acct"),
|
||||||
|
Grptres: TresList{{Type: Ptr("cpu"), Count: Ptr(int64(1000))}},
|
||||||
|
Maxtresperjob: TresList{{Type: Ptr("mem"), Count: Ptr(int64(4096))}},
|
||||||
|
Qoslevel: QosStringIdList{"high", "normal"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded AssocRecSet
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *decoded.Comment != "test comment" {
|
||||||
|
t.Fatal("comment mismatch")
|
||||||
|
}
|
||||||
|
if *decoded.Defaultqos != "normal" {
|
||||||
|
t.Fatal("defaultqos mismatch")
|
||||||
|
}
|
||||||
|
if *decoded.Fairshare != 100 {
|
||||||
|
t.Fatal("fairshare mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Grptres) != 1 || *decoded.Grptres[0].Type != "cpu" {
|
||||||
|
t.Fatal("grptres mismatch")
|
||||||
|
}
|
||||||
|
if len(decoded.Qoslevel) != 2 || decoded.Qoslevel[0] != "high" {
|
||||||
|
t.Fatal("qoslevel mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbUsersAddCondRoundTrip(t *testing.T) {
|
||||||
|
orig := UsersAddCond{
|
||||||
|
Accounts: StringList{"acct1"},
|
||||||
|
Clusters: StringList{"cluster1"},
|
||||||
|
Partitions: StringList{"part1"},
|
||||||
|
Users: StringList{"user1", "user2"},
|
||||||
|
Wckeys: StringList{"wckey1"},
|
||||||
|
Association: &AssocRecSet{
|
||||||
|
Comment: Ptr("assoc comment"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded UsersAddCond
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Users) != 2 {
|
||||||
|
t.Fatal("users count mismatch")
|
||||||
|
}
|
||||||
|
if decoded.Association == nil || *decoded.Association.Comment != "assoc comment" {
|
||||||
|
t.Fatal("association mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbAccountsAddCondRoundTrip(t *testing.T) {
|
||||||
|
orig := AccountsAddCond{
|
||||||
|
Accounts: StringList{"acct1", "acct2"},
|
||||||
|
Clusters: StringList{"cluster1"},
|
||||||
|
Association: &AssocRecSet{
|
||||||
|
Fairshare: Ptr(int32(50)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(orig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded AccountsAddCond
|
||||||
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded.Accounts) != 2 {
|
||||||
|
t.Fatal("accounts count mismatch")
|
||||||
|
}
|
||||||
|
if decoded.Association == nil || *decoded.Association.Fairshare != 50 {
|
||||||
|
t.Fatal("association mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlurmdbEmptyResponses(t *testing.T) {
|
||||||
|
types := []interface{}{
|
||||||
|
OpenapiSlurmdbdJobsResp{},
|
||||||
|
OpenapiSlurmdbdConfigResp{},
|
||||||
|
OpenapiSlurmdbdQosResp{},
|
||||||
|
OpenapiSlurmdbdQosRemovedResp{},
|
||||||
|
OpenapiSlurmdbdStatsResp{},
|
||||||
|
OpenapiAssocsResp{},
|
||||||
|
OpenapiAssocsRemovedResp{},
|
||||||
|
OpenapiUsersResp{},
|
||||||
|
OpenapiClustersResp{},
|
||||||
|
OpenapiClustersRemovedResp{},
|
||||||
|
OpenapiWckeyResp{},
|
||||||
|
OpenapiWckeyRemovedResp{},
|
||||||
|
OpenapiAccountsResp{},
|
||||||
|
OpenapiAccountsRemovedResp{},
|
||||||
|
OpenapiInstancesResp{},
|
||||||
|
OpenapiTresResp{},
|
||||||
|
OpenapiUsersAddCondResp{},
|
||||||
|
OpenapiUsersAddCondRespStr{},
|
||||||
|
OpenapiAccountsAddCondResp{},
|
||||||
|
OpenapiAccountsAddCondRespStr{},
|
||||||
|
StatsRec{},
|
||||||
|
RollupStats{},
|
||||||
|
StatsRpc{},
|
||||||
|
StatsUser{},
|
||||||
|
UsersAddCond{},
|
||||||
|
AccountsAddCond{},
|
||||||
|
AssocRecSet{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range types {
|
||||||
|
data, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("type %d: marshal error: %v", i, err)
|
||||||
|
}
|
||||||
|
if string(data) != "{}" {
|
||||||
|
t.Fatalf("type %d: empty struct should marshal to {}, got %s", i, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user