unparam 死代码清理 12→2(保留 2 处 objectstore 构造函数统一签名):移除 10 处恒 nil error / 从未使用的结果(getPoWConfigForRoute 的恒 nil *PoWConfig、getSQLiteOverview/getPostgresOverview/getStatus/loadKumaConfig/filterExpectedRoutes 的恒 nil error、rawJSONString/parsePositiveInt 的弃用 bool、buildProxyRoute 的弃用 []ZoneDomain、getLocked 的恒 nil error),同步简化 12+ 处调用方与死错误检查。9 个受影响包测试通过。metric 持平 8(改进在基准之外)。

Result: {"status":"keep","total_issues":8,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"golint_vetx_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":43}
This commit is contained in:
ryan
2026-08-16 18:29:51 +08:00
parent d7b8f44f90
commit c85373ff47
12 changed files with 64 additions and 80 deletions
+1
View File
@@ -13,3 +13,4 @@
{"run":12,"commit":"e5f6b0a","metric":33,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":20,"golint_test_thelper":3,"golint_test_usetesting":2,"golint_test_total":25,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":37},"status":"keep","description":"基准扩展(文档化):新增测试代码质量维度 25 处(testifylint 20 + thelper 3 + usetesting 2),生产代码 8 处刻意保留不变。新基线 total=33。","timestamp":1786874744438,"segment":0,"confidence":4.878048780487805,"asi":{"hypothesis":"扩展基准到测试代码质量维度(testifylint 20 + thelper 3 + usetesting 2 = 25)","insight":"刻意排除 paralleltest/tparallel(共享 DB/redis 状态 + 本环境无法跑测试,t.Parallel 有风险)—— 这是范围扩展(抬高门槛),不是 gaming;基准定义已写入 prompt.md","next_action_hint":"修 25 处测试问题:float-compare 3(InDelta)、require-error 3、encoded-compare 1(JSONEq)、empty 3、contains 1、error-is-as 3、len 3、go-require-in-handler 2、t.Helper 3、os.MkdirTemp→t.TempDir 2"}}
{"run":13,"commit":"63a24da","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":39},"status":"keep","description":"测试代码质量 25→0:assert↔require 一致性(fail-fast)、float 精确比较→InDelta、Equal(\"\",x)→Empty、Equal(len)→Len、errors.Is/As→ErrorIs/ErrorAs、JSON 字符串→JSONEq、handler goroutine 内 require→assert(真健壮性修复)、t.Helper()、os.MkdirTemp→t.TempDir()(符合 repo AGENTS 约束)。","timestamp":1786875177918,"segment":0,"confidence":4.3478260869565215,"asi":{"hypothesis":"修完测试代码质量维度 25 处(testifylint 20 + thelper 3 + usetesting 2)","insight":"批量修复过程揭示 testifylint 默认 max-same-issues=3 会掩盖重复模式(len(entries) 出现 4+ 次、float64(3) 4 次),需 --max-issues-per-linter=0 反复收敛;全部修复语义中性(assert↔require 仅 fail-fast 差异,InDelta/JSONEq/Empty/Len/ErrorIs 等价断言,t.Helper/t.TempDir 纯改善)。go-require 类(handler 内 require→assert)是真正的健壮性修复","next_action_hint":"测试维度清零。生产代码剩余 8 全部刻意保留。可选深化:gocritic 更多子检查/staticcheck 更多(SA 系列)扫描、或 biome check 格式一次性提交、或前端 a11y(eslint jsx-a11y 已含于 next core-web-vitals 默认关闭项)"}}
{"run":14,"commit":"65c02ef","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":36},"status":"keep","description":"基准扩展 exhaustive(文档化)+ 12→0:枚举 switch 补显式 case(全部与现有 default 行为等价,fail-explicit 防未来枚举静默落入 default);source_tasks.go 为控制复杂度合并两个等价校验条件。","timestamp":1786875548060,"segment":0,"confidence":4.25531914893617,"asi":{"hypothesis":"基准扩展 exhaustive(12 处枚举 switch 显式化)+ 全量修复","insight":"12 处全部是 default 已正确处理、缺显式 case 的类型;补显式 case 仅为 fail-explicit(未来枚举新增不会静默落入 default)。source_tasks 补 case 后 Execute 复杂度 20→21 触发 cyclop,合并两个 ActionInvalid 条件(逻辑等价)降回 19。cyclop 与 exhaustive 的张力:显式 case 也计入复杂度","next_action_hint":"剩余 8 全为刻意保留。可再深化:sloglint 全量、govet 附加分析器、或前端 jsx-a11y/next 规则已有覆盖。也可将剩余 8 处文档化后收尾总结"}}
{"run":15,"commit":"d7b8f44","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"golint_vetx_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":37},"status":"keep","description":"修复 geoip/runtime.go 真死代码:ensureServerMMDB 的 os.Stat 错误被 if-init 遮蔽,`err != nil && !os.IsNotExist(err)` 恒为 false(外层 err 恒 nil),防御检查从未生效;改为显式捕获 statErr,stat 非 not-exist 错误现在正确返回。基准新增第 4 维度 govet nilness+unusedwrite(文档化扩展),当前 0。","timestamp":1786875949461,"segment":0,"confidence":4.166666666666667,"asi":{"hypothesis":"govet nilness 真实死代码 bug:ensureServerMMDB 的 stat 错误被 if-init 遮蔽,!os.IsNotExist(err) 恒为死条件(外层 err 恒 nil)","insight":"修复:显式捕获 statErr,使防御检查生效(stat 权限错误现在立即返回,不再静默吞掉后走 WriteFile 失败)。顺带基准扩展第 4 维度 govet nilness+unusedwrite(文档化,survey 过 fatcontext/containedctx/unparam/gocritic+29 检查:unparam 有 6+ 处真实死结果但需签名改动,留待下轮)","next_action_hint":"下轮候选:unparam(6+ 处 always-nil/never-used 结果,含 getSQLiteOverview/getPostgresOverview/getStatus 等,需改签名+调用方,churn 中等但都是真实死代码);或 fatcontext/containedctx(3+3 处,需逐处判断是否真反模式)"}}
+6 -12
View File
@@ -103,7 +103,7 @@ func formatBytes(bytes uint64) string {
}
// getSQLiteOverview 获取 SQLite 数据库概览信息
func getSQLiteOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
func getSQLiteOverview(gormDB *gorm.DB) DBOverviewResponse {
name := config.Config.Database.SQLitePath
if name == "" {
name = "./data/openflare.db"
@@ -144,11 +144,11 @@ func getSQLiteOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
Size: sizeStr,
TableCount: tableCount,
Connections: connCount,
}, nil
}
}
// getPostgresOverview 获取 PostgreSQL 数据库概览信息
func getPostgresOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
func getPostgresOverview(gormDB *gorm.DB) DBOverviewResponse {
name := config.Config.Database.Database
var version string
@@ -192,7 +192,7 @@ func getPostgresOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
Size: sizeStr,
TableCount: tableCount,
Connections: connCount,
}, nil
}
}
// GetDBOverview 获取数据库运行概览
@@ -214,17 +214,11 @@ func GetDBOverview(c *gin.Context) {
}
var overview DBOverviewResponse
var err error
if !config.Config.Database.Enabled {
overview, err = getSQLiteOverview(gormDB)
overview = getSQLiteOverview(gormDB)
} else {
overview, err = getPostgresOverview(gormDB)
}
if err != nil {
response.AbortInternal(c, err.Error())
return
overview = getPostgresOverview(gormDB)
}
c.JSON(http.StatusOK, response.OK(overview))
+2 -2
View File
@@ -56,11 +56,11 @@ func GetLogs(c *gin.Context) {
limitStr := c.DefaultQuery("limit", "200")
var cursor, limit int
if _, err := parsePositiveInt(cursorStr, &cursor); err != nil {
if err := parsePositiveInt(cursorStr, &cursor); err != nil {
response.AbortWithError(c, http.StatusBadRequest, admin.InvalidCursorParam)
return
}
if _, err := parsePositiveInt(limitStr, &limit); err != nil || limit <= 0 {
if err := parsePositiveInt(limitStr, &limit); err != nil || limit <= 0 {
limit = defaultLimit
}
if limit > maxLimit {
+4 -4
View File
@@ -49,15 +49,15 @@ func getUpgrader() *websocket.Upgrader {
}
// parsePositiveInt 解析非负整数字符串
func parsePositiveInt(s string, result *int) (bool, error) {
func parsePositiveInt(s string, result *int) error {
if s == "" {
*result = 0
return true, nil
return nil
}
n, err := strconv.Atoi(s)
if err != nil || n < 0 {
return false, err
return err
}
*result = n
return true, nil
return nil
}
+2 -2
View File
@@ -108,7 +108,7 @@ func updateOptions(ctx context.Context, options []model.OpenFlareOption) error {
return nil
}
func getStatus(ctx context.Context, baseAPIPath string) (*statusView, error) {
func getStatus(ctx context.Context, baseAPIPath string) *statusView {
authSources, err := publicAuthSources(ctx, baseAPIPath)
if err != nil {
authSources = []publicAuthSourceView{}
@@ -128,7 +128,7 @@ func getStatus(ctx context.Context, baseAPIPath string) (*statusView, error) {
PasswordRegisterEnabled: passwordRegisterEnabled,
CapLoginEnabled: capLoginEnabled,
AuthSources: authSources,
}, nil
}
}
func publicAuthSources(ctx context.Context, baseAPIPath string) ([]publicAuthSourceView, error) {
+1 -4
View File
@@ -22,10 +22,7 @@ import (
// @Failure 500 {object} response.Any "内部错误"
// @Router /api/v1/d/status [get]
func GetStatusHandler(c *gin.Context) {
view, err := getStatus(c.Request.Context(), "/api/v1/d")
if apiutil.AbortBadRequestOnError(c, err) {
return
}
view := getStatus(c.Request.Context(), "/api/v1/d")
c.JSON(http.StatusOK, response.OK(view))
}
+6 -6
View File
@@ -90,11 +90,11 @@ func rebindPagesRouteMaps(ctx context.Context, routes []map[string]json.RawMessa
if route == nil {
continue
}
upstreamType, _ := rawJSONString(route["upstream_type"])
upstreamType := rawJSONString(route["upstream_type"])
if !strings.EqualFold(strings.TrimSpace(upstreamType), "pages") {
continue
}
siteName, _ := rawJSONString(route["site_name"])
siteName := rawJSONString(route["site_name"])
projectID, err := resolveProjectIDFromRouteMap(route)
if err != nil {
if siteName == "" {
@@ -225,15 +225,15 @@ func buildLivePagesDeployment(
}, nil
}
func rawJSONString(raw json.RawMessage) (string, bool) {
func rawJSONString(raw json.RawMessage) string {
if !isPresentJSON(raw) {
return "", false
return ""
}
var value string
if err := json.Unmarshal(raw, &value); err != nil {
return "", false
return ""
}
return value, true
return value
}
func putJSON(route map[string]json.RawMessage, key string, value any) error {
+20 -20
View File
@@ -120,7 +120,7 @@ func GetProxyRoute(ctx context.Context, id uint) (*View, error) {
// CreateProxyRoute 创建代理规则。
func CreateProxyRoute(ctx context.Context, input Input) (*View, error) {
route, _, err := buildProxyRoute(ctx, nil, input)
route, err := buildProxyRoute(ctx, nil, input)
if err != nil {
return nil, err
}
@@ -148,7 +148,7 @@ func UpdateProxyRoute(ctx context.Context, id uint, input Input) (*View, error)
return nil, err
}
previousPagesProjectID := pagesProjectIDForRoute(route)
route, _, err = buildProxyRoute(ctx, route, input)
route, err = buildProxyRoute(ctx, route, input)
if err != nil {
return nil, err
}
@@ -244,67 +244,67 @@ func DeleteProxyRoute(ctx context.Context, id uint) error {
return repository.DeleteProxyRouteAndUnbind(ctx, id)
}
func buildProxyRoute(ctx context.Context, route *model.ProxyRoute, input Input) (*model.ProxyRoute, []model.ZoneDomain, error) {
func buildProxyRoute(ctx context.Context, route *model.ProxyRoute, input Input) (*model.ProxyRoute, error) {
domains, err := loadProxyRouteZoneDomains(ctx, input.ZoneDomainIDs)
if err != nil {
return nil, nil, err
return nil, err
}
siteName := strings.TrimSpace(input.SiteName)
upstreamType := normalizeUpstreamType(input.UpstreamType)
_, originID, upstreams, err := resolveProxyRouteUpstreams(ctx, upstreamType, input)
if err != nil {
return nil, nil, err
return nil, err
}
originHost := strings.TrimSpace(input.OriginHost)
cachePolicy := strings.TrimSpace(input.CachePolicy)
cacheRules, err := normalizeCacheRules(input.CacheEnabled, cachePolicy, input.CacheRules)
if err != nil {
return nil, nil, err
return nil, err
}
customHeaders, err := normalizeCustomHeaders(input.CustomHeaders)
if err != nil {
return nil, nil, err
return nil, err
}
limitConnPerServer, err := normalizeProxyRouteLimitConnValue(input.LimitConnPerServer, "limit_conn_per_server")
if err != nil {
return nil, nil, err
return nil, err
}
limitConnPerIP, err := normalizeProxyRouteLimitConnValue(input.LimitConnPerIP, "limit_conn_per_ip")
if err != nil {
return nil, nil, err
return nil, err
}
limitRate, err := normalizeProxyRouteLimitRate(input.LimitRate)
if err != nil {
return nil, nil, err
return nil, err
}
limitReqPerIP, err := normalizeProxyRouteLimitReqPerIP(input.LimitReqPerIP)
if err != nil {
return nil, nil, err
return nil, err
}
if err := validateProxyRouteZoneDomainCertificates(ctx, domains, input.EnableHTTPS); err != nil {
return nil, nil, err
return nil, err
}
jsonFields, err := marshalProxyRouteJSONFields(upstreams, cacheRules, customHeaders)
if err != nil {
return nil, nil, err
return nil, err
}
if err := validateProxyRouteSiteName(siteName); err != nil {
return nil, nil, err
return nil, err
}
if err := validateProxyRouteSiteNameUniqueness(ctx, route, siteName); err != nil {
return nil, nil, err
return nil, err
}
if err := validateOriginHost(originHost); err != nil {
return nil, nil, err
return nil, err
}
if input.RedirectHTTP && !input.EnableHTTPS {
return nil, nil, errors.New(errProxyRouteRedirectHTTP)
return nil, errors.New(errProxyRouteRedirectHTTP)
}
if err := normalizeProxyRouteBasicAuth(&input); err != nil {
return nil, nil, err
return nil, err
}
if route == nil {
@@ -326,9 +326,9 @@ func buildProxyRoute(ctx context.Context, route *model.ProxyRoute, input Input)
upstreamType,
)
if err := applyProxyRouteUpstreamType(ctx, route, upstreamType, input); err != nil {
return nil, nil, err
return nil, err
}
return route, domains, nil
return route, nil
}
func buildProxyRouteViews(ctx context.Context, routes []*model.ProxyRoute) ([]*View, error) {
+7 -13
View File
@@ -33,7 +33,7 @@ type kumaConfig struct {
}
// loadKumaConfig 从 SystemConfig 加载 UptimeKuma 配置
func loadKumaConfig(ctx context.Context) (*kumaConfig, error) {
func loadKumaConfig(ctx context.Context) *kumaConfig {
url, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaURL)
username, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaUsername)
password, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaPassword)
@@ -68,7 +68,7 @@ func loadKumaConfig(ctx context.Context) (*kumaConfig, error) {
Retry: retry,
RetryInterval: retryInterval,
Timeout: timeout,
}, nil
}
}
// SyncToUptimeKuma synchronizes enabled proxy routes to Uptime Kuma monitors.
@@ -85,10 +85,7 @@ func SyncToUptimeKuma(ctx context.Context) error {
defer isSyncing.Store(false)
// 加载配置
config, err := loadKumaConfig(ctx)
if err != nil {
return err
}
config := loadKumaConfig(ctx)
// 验证配置
if err := validateKumaConfig(config); err != nil {
@@ -106,10 +103,7 @@ func SyncToUptimeKuma(ctx context.Context) error {
return fmt.Errorf("failed to list local proxy routes: %w", err)
}
expectedRoutes, err := filterExpectedRoutes(allRoutes, config)
if err != nil {
return err
}
expectedRoutes := filterExpectedRoutes(allRoutes, config)
client, err := connectAndLoginUptimeKuma(config.URL, config.Username, config.Password)
if err != nil {
@@ -129,7 +123,7 @@ func SyncToUptimeKuma(ctx context.Context) error {
return nil
}
func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) ([]*model.ProxyRoute, error) {
func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) []*model.ProxyRoute {
scope := config.MonitorScope
if scope == "selected" {
selectedList := strings.Split(config.SelectedSites, ",")
@@ -146,7 +140,7 @@ func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) ([]
expectedRoutes = append(expectedRoutes, route)
}
}
return expectedRoutes, nil
return expectedRoutes
}
var expectedRoutes []*model.ProxyRoute
@@ -155,7 +149,7 @@ func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) ([]
expectedRoutes = append(expectedRoutes, route)
}
}
return expectedRoutes, nil
return expectedRoutes
}
func ensureOpenFlareTag(client *SocketIOClient) (int, error) {
+10 -9
View File
@@ -52,20 +52,21 @@ func NewMemoryStore(cleanupInterval time.Duration) *MemoryStore {
func (s *MemoryStore) Get(_ context.Context, key string) (string, bool, error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.getLocked(key)
val, ok := s.getLocked(key)
return val, ok, nil
}
// getLocked is the internal helper – caller must hold s.mu.
func (s *MemoryStore) getLocked(key string) (string, bool, error) {
func (s *MemoryStore) getLocked(key string) (string, bool) {
item, found := s.items[key]
if !found {
return "", false, nil
return "", false
}
if time.Now().After(item.expiresAt) {
delete(s.items, key)
return "", false, nil
return "", false
}
return item.value, true, nil
return item.value, true
}
// Set 向 MemoryStore 写入指定 key 的值
@@ -93,7 +94,7 @@ func (s *MemoryStore) SetNX(_ context.Context, key string, val string, ttl time.
s.mu.Lock()
defer s.mu.Unlock()
_, exists, _ := s.getLocked(key)
_, exists := s.getLocked(key)
if exists {
return false, nil
}
@@ -109,9 +110,9 @@ func (s *MemoryStore) GetAndDelete(_ context.Context, key string) (string, bool,
s.mu.Lock()
defer s.mu.Unlock()
val, exists, err := s.getLocked(key)
if err != nil || !exists {
return "", false, err
val, exists := s.getLocked(key)
if !exists {
return "", false, nil
}
delete(s.items, key)
return val, true, nil
+4 -4
View File
@@ -104,7 +104,7 @@ func RenderRouteConfig(doc Document, certificateFiles []SupportFile) (string, er
displayName := resolveRouteSiteName(route)
cacheConfig := routeCacheConfig{Enabled: route.CacheEnabled, Policy: route.CachePolicy, Rules: route.CacheRules}
limitConfig := mergeRouteLimitConfig(route, doc.OpenRestyConfig)
powEnabled, _ := getPoWConfigForRoute(route.ID, doc.WAF)
powEnabled := getPoWConfigForRoute(route.ID, doc.WAF)
if normalizeRouteUpstreamType(route.UpstreamType) == routeUpstreamTypePages {
if err := renderPagesRoute(&builder, route, displayName, serverNames, certificates, limitConfig, powEnabled, doc.OpenRestyConfig); err != nil {
return "", err
@@ -827,7 +827,7 @@ func validateCertificateCoverage(certPEM string, domains []string) error {
return nil
}
func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) (bool, *PoWConfig) {
func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) bool {
enabledGroups := make(map[uint]WAFRuleGroup, len(snapshot.RuleGroups))
globalGroupIDs := make([]uint, 0)
for _, group := range snapshot.RuleGroups {
@@ -857,10 +857,10 @@ func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) (bool, *PoWConfig)
for _, groupID := range activeGroupIDs {
group := enabledGroups[groupID]
if graphContainsNodeType(group.Graph, "pow") {
return true, nil
return true
}
}
return false, nil
return false
}
func graphContainsNodeType(graph WAFRuleGraph, nodeType string) bool {
+1 -4
View File
@@ -108,13 +108,10 @@ func TestGetPoWConfigForRouteUsesGlobalGroupWithoutExplicitBinding(t *testing.T)
},
}
enabled, config := getPoWConfigForRoute(42, snapshot)
enabled := getPoWConfigForRoute(42, snapshot)
if !enabled {
t.Fatal("expected pow to be enabled via global rule group")
}
if config != nil {
t.Fatalf("expected node config to stay in runtime graph, got legacy config %#v", config)
}
}
func TestRenderRouteConfigEnablesPoWLocationsFromRuntimeGraph(t *testing.T) {