mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-30 23:45:06 +01:00 
			
		
		
		
	Add ui config tests and fix bugs
This commit is contained in:
		| @@ -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 ( | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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"] | ||||
|       } | ||||
|     } | ||||
|   }` | ||||
|   } | ||||
| }` | ||||
|   | ||||
							
								
								
									
										57
									
								
								web/web.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								web/web.go
									
									
									
									
									
								
							| @@ -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
									
								
							
							
						
						
									
										89
									
								
								web/webConfig_test.go
									
									
									
									
									
										Normal 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") | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user