Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bal_tools/ts_config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ def handle_spread_with_map(text):

obj = handle_spread_with_map(obj)

# Drop spreads of imported identifiers we can't resolve, e.g.
# workerJobs: [...activeChainWorkerJobsGeneric, ...activeChainWorkerJobsV2]
# These reference imported arrays that aren't available to the parser.
obj = re.sub(r"\.\.\.[A-Za-z_$][\w$]*\s*,?", "", obj)
# Remove any leftover leading comma the removal above may produce ([, ...])
obj = re.sub(r"\[\s*,", "[", obj)

# Then handle simple spread operators: ...[array] -> [array]
obj = re.sub(r"\.\.\.\s*\[", "[", obj)

Expand Down
9 changes: 0 additions & 9 deletions tests/test_ecosystem.py

This file was deleted.

65 changes: 64 additions & 1 deletion tests/test_ts_config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,59 @@
"""

import requests
from bal_tools.ts_config_loader import ts_config_loader
from bal_tools.ts_config_loader import (
ts_config_loader,
_to_json,
_extract_object_literal,
)


def test_spread_of_imported_identifiers():
"""Regression: spreads of imported identifiers (e.g. workerJobs:
[...activeChainWorkerJobsGeneric]) must not break JSON parsing.

The backend network configs import worker-job arrays and spread them into
`workerJobs`. The parser can't resolve those imports, so it should drop the
spreads and still produce a valid object rather than raising JSONDecodeError.
"""
import json

ts = """
import { activeChainWorkerJobsGeneric, activeChainWorkerJobsV2 } from './worker-jobs';

export default <NetworkData>{
chain: {
slug: 'mychain',
},
subgraphs: {
balancer: `https://example.com/v2-mychain-smol/latest/gn`,
gauge: `https://example.com/balancer-gauges-mychain/latest/gn`,
},
workerJobs: [...activeChainWorkerJobsGeneric, ...activeChainWorkerJobsV2],
};
"""
parsed = json.loads(_to_json(_extract_object_literal(ts)))
assert parsed["workerJobs"] == []
assert parsed["subgraphs"]["balancer"].endswith("v2-mychain-smol/latest/gn")
assert parsed["chain"]["slug"] == "mychain"


def test_spread_of_import_mixed_with_literal():
"""A spread of an import mixed with real array entries should drop only the
unresolved spread and keep the literal values."""
import json

ts = """
import { extraJobs } from './worker-jobs';

export default <NetworkData>{
stakingServices: ['gauge', ...extraJobs],
workerJobs: [...extraJobs, 'literalJob'],
};
"""
parsed = json.loads(_to_json(_extract_object_literal(ts)))
assert parsed["stakingServices"] == ["gauge"]
assert parsed["workerJobs"] == ["literalJob"]


def test_all_backend_configs_load():
Expand All @@ -32,19 +84,30 @@ def test_all_backend_configs_load():

failed_configs = []

loaded_any = False
for config_file in config_files:
chain = config_file.replace(".ts", "")
url = f"https://raw.githubusercontent.com/balancer/backend/refs/heads/v3-main/config/{config_file}"

# The config/ directory also holds helper modules (e.g. worker-jobs.ts,
# types.ts, chain-id-to-chain.ts) that aren't network configs and have no
# `export default` literal. Skip those; only network configs are parseable.
raw = requests.get(url).text
if "export default" not in raw:
continue

try:
# Should not raise any exceptions
config = ts_config_loader(url)
assert isinstance(
config, dict
), f"Config for {chain} should be a dictionary"
loaded_any = True
except Exception as e:
failed_configs.append((chain, str(e)))

assert loaded_any, "No network configs were loaded"

assert (
len(failed_configs) == 0
), f"Failed to load {len(failed_configs)} configs: {failed_configs}"
Loading