Add ui config tests and fix bugs

This commit is contained in:
2025-09-30 09:01:54 +02:00
parent beb92967e5
commit e58b0fa015
5 changed files with 177 additions and 35 deletions

View File

@@ -2,6 +2,7 @@
// All rights reserved. This file is part of cc-backend.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package config
import (

View File

@@ -26,7 +26,7 @@ var (
type UserCfgRepo struct {
DB *sqlx.DB
Lookup *sqlx.Stmt
uiDefaults web.WebConfig
uiDefaults map[string]any
cache *lrucache.Cache
lock sync.RWMutex
}
@@ -43,7 +43,7 @@ func GetUserCfgRepo() *UserCfgRepo {
userCfgRepoInstance = &UserCfgRepo{
DB: db.DB,
Lookup: lookupConfigStmt,
uiDefaults: web.UIDefaults,
uiDefaults: web.UIDefaultsMap,
cache: lrucache.New(1024),
}
})
@@ -55,13 +55,12 @@ func GetUserCfgRepo() *UserCfgRepo {
// user or return the plain default config.
func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]any, error) {
if user == nil {
uCfg.lock.RLock()
copy := make(map[string]any, len(uCfg.uiDefaults))
maps.Copy(copy, uCfg.uiDefaults)
uCfg.lock.RUnlock()
return copy, nil
}
// Is the cache invalidated in case the options are changed?
data := uCfg.cache.Get(user.Username, func() (any, time.Duration, int) {
uiconfig := make(map[string]any, len(uCfg.uiDefaults))
maps.Copy(uiconfig, uCfg.uiDefaults)
@@ -113,15 +112,6 @@ func (uCfg *UserCfgRepo) UpdateConfig(
user *schema.User,
) error {
if user == nil {
var val any
if err := json.Unmarshal([]byte(value), &val); err != nil {
cclog.Warn("Error while unmarshaling raw user config json")
return err
}
uCfg.lock.Lock()
defer uCfg.lock.Unlock()
uCfg.uiDefaults[key] = val
return nil
}

View File

@@ -5,8 +5,7 @@
package web
var configSchema = `
{
const configSchema = `{
"type": "object",
"properties": {
"jobList": {
@@ -21,8 +20,7 @@ var configSchema = `
"description": "If footprint bars are shown as first column by default.",
"type": "boolean"
}
},
"required": ["usePaging", "showFootprint"]
}
},
"nodeList": {
"description": "Node list defaults. Applies to node list view.",
@@ -32,8 +30,7 @@ var configSchema = `
"description": "If classic paging is used instead of continuous scrolling by default.",
"type": "boolean"
}
},
"required": ["usePaging"]
}
},
"jobView": {
"description": "Job view defaults.",
@@ -55,8 +52,7 @@ var configSchema = `
"description": "If the job metric statistics table is shown by default.",
"type": "boolean"
}
},
"required": ["showFootprint"]
}
},
"metricConfig": {
"description": "Global initial metric selections for primary views of all clusters.",
@@ -95,9 +91,33 @@ var configSchema = `
"name": {
"description": "The name of the cluster."
},
"subClusters" {
"jobListMetrics": {
"description": "Initial metrics shown for new users in job lists (User and jobs view) for subcluster.",
"type": "array",
"items": {
"type": "string",
"minItems": 1
}
},
"jobViewPlotMetrics": {
"description": "Initial metrics shown for new users as job view timeplots for subcluster.",
"type": "array",
"items": {
"type": "string",
"minItems": 1
}
},
"jobViewTableMetrics": {
"description": "Initial metrics shown for new users in job view statistics table for subcluster.",
"type": "array",
"items": {
"type": "string",
"minItems": 1
}
},
"subClusters": {
"description": "The array of overrides per subcluster.",
"type":"array",
"type": "array",
"items": {
"type": "object",
"properties": {
@@ -138,8 +158,7 @@ var configSchema = `
"required": ["name", "subClusters"],
"minItems": 1
}
},
"required": ["jobListMetrics", "jobViewPlotMetrics", "jobViewTableMetrics"]
}
}
},
"plotConfiguration": {
@@ -165,7 +184,7 @@ var configSchema = `
"type": "string"
}
}
},
"required": ["colorBackground", "plotsPerRow", "lineWidth"]
}
}
}`
}
}`

View File

@@ -13,7 +13,6 @@ import (
"io/fs"
"net/http"
"strings"
"sync"
"github.com/ClusterCockpit/cc-backend/internal/config"
"github.com/ClusterCockpit/cc-backend/pkg/archive"
@@ -75,8 +74,6 @@ type PlotConfiguration struct {
ColorScheme []string `json:"colorScheme"`
}
var initOnce sync.Once
var UIDefaults = WebConfig{
JobList: JobListConfig{
UsePaging: false,
@@ -104,6 +101,8 @@ var UIDefaults = WebConfig{
},
}
var UIDefaultsMap map[string]any
//
// map[string]any{
// "analysis_view_histogramMetrics": []string{"flops_any", "mem_bw", "mem_used"},
@@ -117,16 +116,60 @@ var UIDefaults = WebConfig{
// "status_view_selectedTopProjectCategory": "totalJobs",
// }
func Init(rawConfig json.RawMessage, disableArchive bool) error {
func Init(rawConfig json.RawMessage) error {
var err error
initOnce.Do(func() {
if rawConfig != nil {
config.Validate(configSchema, rawConfig)
if err = json.Unmarshal(rawConfig, &UIDefaults); err != nil {
cclog.Warn("Error while unmarshaling raw config json")
return
return err
}
})
}
UIDefaultsMap = make(map[string]any)
UIDefaultsMap["joblist_usePaging"] = UIDefaults.JobList.UsePaging
UIDefaultsMap["joblist_showFootprint"] = UIDefaults.JobList.ShowFootprint
UIDefaultsMap["nodelist_usePaging"] = UIDefaults.NodeList.UsePaging
UIDefaultsMap["jobview_showPolarPlot"] = UIDefaults.JobView.ShowPolarPlot
UIDefaultsMap["jobview_showFootprint"] = UIDefaults.JobView.ShowFootprint
UIDefaultsMap["jobview_showRoofline"] = UIDefaults.JobView.ShowRoofline
UIDefaultsMap["jobview_showStatTable"] = UIDefaults.JobView.ShowStatTable
UIDefaultsMap["metricConfig_jobListMetrics"] = UIDefaults.MetricConfig.JobListMetrics
UIDefaultsMap["metricConfig_jobViewPlotMetrics"] = UIDefaults.MetricConfig.JobViewPlotMetrics
UIDefaultsMap["metricConfig_jobViewTableMetrics"] = UIDefaults.MetricConfig.JobViewTableMetrics
UIDefaultsMap["plotConfiguration_colorBackground"] = UIDefaults.PlotConfiguration.ColorBackground
UIDefaultsMap["plotConfiguration_plotsPerRow"] = UIDefaults.PlotConfiguration.PlotsPerRow
UIDefaultsMap["plotConfiguration_lineWidth"] = UIDefaults.PlotConfiguration.LineWidth
UIDefaultsMap["plotConfiguration_colorScheme"] = UIDefaults.PlotConfiguration.ColorScheme
for _, c := range UIDefaults.MetricConfig.Clusters {
if c.JobListMetrics != nil {
UIDefaultsMap["metricConfig_jobListMetrics:"+c.Name] = c.JobListMetrics
}
if c.JobViewPlotMetrics != nil {
UIDefaultsMap["metricConfig_jobViewPlotMetrics:"+c.Name] = c.JobViewPlotMetrics
}
if c.JobViewTableMetrics != nil {
UIDefaultsMap["metricConfig_jobViewTableMetrics:"+c.Name] = c.JobViewTableMetrics
}
for _, sc := range c.SubClusters {
suffix := strings.Join([]string{c.Name, sc.Name}, ":")
if sc.JobListMetrics != nil {
UIDefaultsMap["metricConfig_jobListMetrics:"+suffix] = sc.JobListMetrics
}
if sc.JobViewPlotMetrics != nil {
UIDefaultsMap["metricConfig_jobViewPlotMetrics:"+suffix] = sc.JobViewPlotMetrics
}
if sc.JobViewTableMetrics != nil {
UIDefaultsMap["metricConfig_jobViewTableMetrics:"+suffix] = sc.JobViewTableMetrics
}
}
}
return err
}

89
web/webConfig_test.go Normal file
View File

@@ -0,0 +1,89 @@
// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
// All rights reserved. This file is part of cc-backend.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package web
import (
"encoding/json"
"fmt"
"testing"
ccconf "github.com/ClusterCockpit/cc-lib/ccConfig"
)
func TestInit(t *testing.T) {
fp := "../../configs/config.json"
ccconf.Init(fp)
cfg := ccconf.GetPackageConfig("web")
Init(cfg)
if UIDefaultsMap["nodelist_usePaging"] == false {
t.Errorf("wrong option\ngot: %v \nwant: true", UIDefaultsMap["NodeList_UsePaging"])
}
}
func TestSimpleDefaults(t *testing.T) {
const s = `{
"joblist": {
"showFootprint": false
}
}`
Init(json.RawMessage(s))
if UIDefaultsMap["joblist_usePaging"] == true {
t.Errorf("wrong option\ngot: %v \nwant: false", UIDefaultsMap["NodeList_UsePaging"])
}
}
func TestOverwrite(t *testing.T) {
const s = `{
"metricConfig": {
"jobListMetrics": ["flops_sp", "flops_dp"],
"clusters": [
{
"name": "fritz",
"jobListMetrics": ["flops_any", "mem_bw", "load"],
"subClusters": [
{
"name": "icelake",
"jobListMetrics": ["flops_any", "mem_bw", "power", "load"],
"jobViewPlotMetrics": ["load"]
}
]
}
]
}
}`
Init(json.RawMessage(s))
fmt.Printf("%+v", UIDefaultsMap)
v, ok := UIDefaultsMap["metricConfig_jobListMetrics"].([]string)
if ok {
if v[0] != "flops_sp" {
t.Errorf("wrong metric\ngot: %s \nwant: flops_sp", v[0])
}
} else {
t.Errorf("missing Key\nkey: metricConfig_jobListMetrics")
}
v, ok = UIDefaultsMap["metricConfig_jobListMetrics:fritz"].([]string)
if ok {
if v[0] != "flops_any" {
t.Errorf("wrong metric\ngot: %s \nwant: flops_any", v[0])
}
} else {
t.Errorf("missing Key\nkey: metricConfig_jobListMetrics:fritz")
}
v, ok = UIDefaultsMap["metricConfig_jobListMetrics:fritz:icelake"].([]string)
if ok {
if v[3] != "load" {
t.Errorf("wrong metric\ngot: %s \nwant: load", v[3])
}
} else {
t.Errorf("missing Key\nkey: metricConfig_jobListMetrics:fritz:icelake")
}
}