Skip to content

Commit 2abc1b5

Browse files
authored
[Testing] Logging improvements, better archival checks, and improved load test scripts (#311)
## Summary Improve Near chain support, enhance error logging with emojis, and refactor E2E testing infrastructure ### Primary Changes: - Remove `near` from EVM services and add proper Near-specific QoS configuration with TODO for full implementation - Upgrade error logging from `Info` to `Error` level across QoS modules with descriptive emoji indicators - Refactor E2E test execution flow by removing manual pass/fail tracking and improving test output formatting - Optimize archival balance consensus checking with parallel processing and proper timeout handling ### Secondary changes: - Add comprehensive TODO list for unsupported services in `services_shannon.yaml` - Enhance E2E test progress reporting with better visual separators and service summaries - Improve error messages with service-specific context and endpoint identification - Add sorting functionality to service summary tables by success rate and latency metrics
1 parent fcf4c90 commit 2abc1b5

25 files changed

+249
-147
lines changed

config/service_qos_config.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,6 @@ var shannonServices = []ServiceQoSConfig{
385385
// Moonriver
386386
evm.NewEVMServiceQoSConfig("moonriver", "0x505", nil),
387387

388-
// Near
389-
evm.NewEVMServiceQoSConfig("near", "0x18d", nil),
390-
391388
// opBNB
392389
evm.NewEVMServiceQoSConfig("opbnb", "0xcc", nil),
393390

@@ -406,6 +403,12 @@ var shannonServices = []ServiceQoSConfig{
406403
// Sei
407404
evm.NewEVMServiceQoSConfig("sei", "0x531", nil),
408405

406+
// *** Near EVM Services ***
407+
408+
// Near
409+
// TODO_TECHDEBT: Add support for Near QoS
410+
// near.NewNearServiceQoSConfig("near", "0x18d", nil),
411+
409412
// *** CometBFT Services ***
410413

411414
// TODO_MVP(@commoddity): Ensure that QoS observations are being applied correctly and that

e2e/config/services_shannon.yaml

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@
55
# 2. Use it as the source of truth for service QoS configs by embedding and YAML unmarshalling
66
# 3. Remove the `config/service_qos_config.go` file in favour of using this file
77

8+
# TODO_TECHDEBT: Add support for the following services:
9+
# - bitcoin
10+
# - evmos
11+
# - near
12+
# - radix
13+
# - xrpl_evm_dev
14+
# - base-testnet
15+
# - moonriver
16+
# - sei
17+
# - svc-poktminer
18+
# - tron
19+
# - fraxtal
20+
# - kava
21+
# - sui
22+
823
####################################################
924
# ---------------- Shannon Services -------------- #
1025
####################################################
@@ -292,6 +307,18 @@ services:
292307
transaction_hash: "0xfc30dd922a20302b88108422da4cfc09a82853d896a5f45f4ea777e12136af70"
293308
call_data: "0x18160ddd"
294309

310+
# TODO_TECHDEBT: Add support for Near E2E tests
311+
# - name: "Shannon - near (Near) Test"
312+
# service_id: "near"
313+
# service_type: "near"
314+
# archival: true
315+
# service_params:
316+
# https://nearblocks.io/txns/4PVpwA42LpaUZ5LPD6fwNd94hjB5ue1ND2WSG4UzQVM8
317+
# contract_address: "usdt.tether-token.near"
318+
# contract_start_block: 74076817
319+
# transaction_hash: "4PVpwA42LpaUZ5LPD6fwNd94hjB5ue1ND2WSG4UzQVM8"
320+
# call_data: "0x18160ddd"
321+
295322
# Shannon - Metis (Archival)
296323
- name: "Shannon - metis (Metis) Test"
297324
service_id: "metis"
@@ -563,11 +590,6 @@ services:
563590
# service_id: "moonriver"
564591
# service_type: "evm"
565592

566-
# # Shannon - Near
567-
# - name: "Shannon - near (Near) Test"
568-
# service_id: "near"
569-
# service_type: "evm"
570-
571593
# # Shannon - Radix
572594
# - name: "Shannon - radix (Radix) Test"
573595
# service_id: "radix"

e2e/config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,9 @@ func (c *Config) getTestServices() ([]*TestService, error) {
308308
for id := range serviceIdsWithNoTestCases {
309309
missingServiceIds = append(missingServiceIds, id)
310310
}
311-
fmt.Printf("Warning: The following service IDs had no test cases: [%s]\n", strings.Join(missingServiceIds, ", "))
312311
servicesFile := fmt.Sprintf(servicesFileTemplate, testProtocol)
313-
return nil, fmt.Errorf("Please refer to the `e2e/%s` file to see which services are configured for the `%s` protocol.", servicesFile, testProtocol)
312+
fmt.Printf("⚠️ The following service IDs have no E2E / Load test cases and will there be skipped: [%s] ⚠️\n", strings.Join(missingServiceIds, ", "))
313+
fmt.Printf("⚠️ Please refer to the `e2e/%s` file to see which services are configured for the `%s` protocol ⚠️\n", servicesFile, testProtocol)
314314
}
315315

316316
return filteredTestCases, nil

e2e/main_test.go

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ func Test_PATH_E2E(t *testing.T) {
7575
// Log the test service IDs
7676
logTestServiceIDs(testServices)
7777

78-
allPassed := true
7978
for _, ts := range testServices {
8079
serviceConfig, exists := testServiceConfigs[ts.ServiceID]
8180
if !exists {
@@ -116,21 +115,9 @@ func Test_PATH_E2E(t *testing.T) {
116115
logTestServiceInfo(ts, serviceGatewayURL, serviceConfig)
117116

118117
// Run the service test
119-
serviceTestFailed := runServiceTest(t, ctx, ts)
120-
121-
if serviceTestFailed {
122-
fmt.Printf("\n%s❌ TEST FAILED: Service %s failed assertions%s\n", RED, ts.ServiceID, RESET)
123-
allPassed = false
124-
} else {
125-
fmt.Printf("\n%s✅ Service %s test passed%s\n", GREEN, ts.ServiceID, RESET)
126-
}
118+
runServiceTest(t, ctx, ts)
127119
}
128120

129-
if allPassed {
130-
fmt.Printf("\n%s✅ Test Success: All %d services passed%s\n", GREEN, len(testServices), RESET)
131-
} else {
132-
fmt.Printf("\n%s❌ Test Failure: One or more services failed%s\n", RED, RESET)
133-
}
134121
printServiceSummaries(serviceSummaries)
135122
}
136123

@@ -199,7 +186,8 @@ func logTestStartInfo(gatewayURL string, testServices []*TestService) {
199186
}
200187

201188
func logTestServiceIDs(testServices []*TestService) {
202-
fmt.Printf("\n⛓️ Running tests for service IDs:\n")
189+
fmt.Printf("\n\n=======================================================\n")
190+
fmt.Printf("⛓️ Will be running tests for service IDs:\n")
203191
for _, ts := range testServices {
204192
if ts.Archival {
205193
fmt.Printf(" 🗄️ %s%s%s (Archival)\n", GREEN, ts.ServiceID, RESET)
@@ -210,7 +198,8 @@ func logTestServiceIDs(testServices []*TestService) {
210198
}
211199

212200
func logTestServiceInfo(ts *TestService, serviceGatewayURL string, serviceConfig ServiceConfig) {
213-
fmt.Printf("\n🛠️ Running test: %s%s%s\n", BOLD_BLUE, ts.Name, RESET)
201+
fmt.Printf("\n\n=======================================================\n")
202+
fmt.Printf("🛠️ Starting test for : %s%s%s\n", BOLD_BLUE, ts.Name, RESET)
214203
fmt.Printf(" 🖥️ Service Gateway URL: %s%s%s\n", BLUE, serviceGatewayURL, RESET)
215204
fmt.Printf(" 🏎️ Global Requests per Second: %s%d%s\n", GREEN, serviceConfig.GlobalRPS, RESET)
216205
fmt.Printf(" 🚗 Total Requests per Method: %s%d%s\n\n", GREEN, serviceConfig.RequestsPerMethod, RESET)

e2e/service_evm_test.go

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func getEVMVegetaTargets(
123123

124124
blockNumber, err := getEVMBlockNumber(ts, headers, gatewayURL)
125125
if err != nil {
126-
return nil, fmt.Errorf("failed to get EVM block number: %w", err)
126+
return nil, fmt.Errorf("failed to get EVM block number for service '%s': %w", ts.ServiceID, err)
127127
}
128128
ts.ServiceParams.blockNumber = blockNumber
129129

@@ -140,7 +140,7 @@ func getEVMVegetaTargets(
140140
// Marshal the request body
141141
body, err := json.Marshal(jsonrpcReq)
142142
if err != nil {
143-
return nil, fmt.Errorf("failed to marshal JSON-RPC request for method %s: %w", method, err)
143+
return nil, fmt.Errorf("failed to marshal JSON-RPC request for method '%s' for service '%s': %w", method, ts.ServiceID, err)
144144
}
145145

146146
// Create vegeta target
@@ -161,67 +161,93 @@ func getEVMVegetaTargets(
161161
// Get Test Block Number helpers - Used for EVM archival services
162162
// -----------------------------------------------------------------------------
163163

164-
func getEVMBlockNumber(testService *TestService, headers http.Header, gatewayURL string) (string, error) {
164+
// getEVMBlockNumber returns the block number to use for testing.
165+
// - If non-archival, returns "latest"
166+
// - If archival, returns a random block number between the current block and the contract start block
167+
func getEVMBlockNumber(
168+
testService *TestService,
169+
headers http.Header,
170+
gatewayURL string,
171+
) (string, error) {
165172
if !testService.Archival {
166173
return "latest", nil
167174
} else {
168-
blockNumber, err := setTestBlockNumber(
175+
// randomBlockNumber is a block between the:
176+
// - Start height: the start block height of the contract used for testing
177+
// - End height: the current block height
178+
randomBlockNumber, err := getTestBlockNumberForArchivalTest(
169179
gatewayURL,
170180
headers,
171181
testService.ServiceParams.ContractStartBlock,
172182
)
173183
if err != nil {
174-
return "", fmt.Errorf("Could not get archival block number: %w", err)
184+
return "", fmt.Errorf("Could not get random block number for archival test: %w", err)
175185
}
176-
return blockNumber, nil
186+
return randomBlockNumber, nil
177187
}
178188
}
179189

180-
// setTestBlockNumber gets an archival block number for testing or fails the test.
190+
// getTestBlockNumberForArchivalTest gets an archival block number for testing or fails the test.
181191
// Selected by picking a random block number between the current block and the contract start block.
182-
func setTestBlockNumber(
192+
func getTestBlockNumberForArchivalTest(
183193
gatewayURL string,
184194
headers http.Header,
185195
contractStartBlock uint64,
186196
) (string, error) {
187197
// Get current block height - fail test if this doesn't work
188-
currentBlock, err := getCurrentBlockNumber(gatewayURL, headers)
198+
currentBlockHeight, err := getCurrentConsensusBlockHeight(gatewayURL, headers)
189199
if err != nil {
190200
return "", fmt.Errorf("Could not get current block height: %w", err)
191201
}
192202

193203
// Get random historical block number
194-
return calculateArchivalBlockNumber(currentBlock, contractStartBlock), nil
204+
return calculateArchivalBlockNumber(currentBlockHeight, contractStartBlock), nil
195205
}
196206

197-
// getCurrentBlockNumber gets current block height with consensus from multiple requests.
198-
func getCurrentBlockNumber(gatewayURL string, headers http.Header) (uint64, error) {
199-
// Track frequency of each block height seen
207+
// getCurrentConsensusBlockHeight gets current block height with consensus from multiple requests.
208+
func getCurrentConsensusBlockHeight(gatewayURL string, headers http.Header) (uint64, error) {
200209
blockHeights := make(map[uint64]int)
201210
maxAttempts := 10
202211
requiredAgreement := 3
203-
client := &http.Client{Timeout: 5 * time.Second}
212+
client := &http.Client{Timeout: 2 * time.Second}
204213

205-
// Make multiple attempts to get consensus
214+
// Make requests to get current block height rapidly in parallel
215+
results := make(chan uint64, maxAttempts)
206216
for range maxAttempts {
207-
blockNum, err := fetchBlockNumber(client, gatewayURL, headers)
208-
if err != nil {
209-
continue
210-
}
217+
go func() {
218+
if height, err := getCurrentBlockHeight(client, gatewayURL, headers); err == nil {
219+
results <- height
220+
}
221+
}()
222+
}
211223

212-
// Update consensus tracking
213-
blockHeights[blockNum]++
214-
if blockHeights[blockNum] >= requiredAgreement {
215-
return blockNum, nil
224+
// Collect results quickly
225+
timeout := time.After(5 * time.Second)
226+
collect:
227+
for range maxAttempts {
228+
select {
229+
case height := <-results:
230+
blockHeights[height]++
231+
if blockHeights[height] >= requiredAgreement {
232+
return height, nil
233+
}
234+
case <-timeout:
235+
break collect
216236
}
217237
}
218238

219-
// If we get here, we didn't reach consensus
220-
return 0, fmt.Errorf("failed to reach consensus on block height after %d attempts", maxAttempts)
239+
// Return the most recent height seen if no consensus
240+
var maxHeight uint64
241+
for height := range blockHeights {
242+
if height > maxHeight {
243+
maxHeight = height
244+
}
245+
}
246+
return maxHeight, nil
221247
}
222248

223-
// fetchBlockNumber makes a single request to get the current block number.
224-
func fetchBlockNumber(client *http.Client, gatewayURL string, headers http.Header) (uint64, error) {
249+
// getCurrentBlockHeight makes a single request to get the current block number.
250+
func getCurrentBlockHeight(client *http.Client, gatewayURL string, headers http.Header) (uint64, error) {
225251
// Build and send request
226252
req, err := buildBlockNumberRequest(gatewayURL, headers)
227253
if err != nil {
@@ -235,19 +261,19 @@ func fetchBlockNumber(client *http.Client, gatewayURL string, headers http.Heade
235261
defer resp.Body.Close()
236262

237263
if resp.StatusCode != http.StatusOK {
238-
return 0, fmt.Errorf("bad status code: %d", resp.StatusCode)
264+
return 0, fmt.Errorf("Error getting current block height: %d", resp.StatusCode)
239265
}
240266

241267
// Parse response
242268
var jsonRPC jsonrpc.Response
243269
if err := json.NewDecoder(resp.Body).Decode(&jsonRPC); err != nil {
244-
return 0, err
270+
return 0, fmt.Errorf("Error getting current block height: %w", err)
245271
}
246272

247273
// Process hex string result
248274
hexString, ok := jsonRPC.Result.(string)
249275
if !ok {
250-
return 0, fmt.Errorf("expected string result, got %T", jsonRPC.Result)
276+
return 0, fmt.Errorf("Error getting current block height: %T", jsonRPC.Result)
251277
}
252278

253279
// Parse hex (remove "0x" prefix if present)

e2e/service_solana_test.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -168,41 +168,57 @@ type getEpochInfoResponse struct {
168168

169169
func getSolanaBlockNumber(_ *TestService, headers http.Header, gatewayURL string) (string, error) {
170170
// Get the current slot number
171-
slotNumber, err := getCurrentSlotNumber(gatewayURL, headers)
171+
slotNumber, err := getCurrentConsensusSlotNumber(gatewayURL, headers)
172172
if err != nil {
173173
return "", fmt.Errorf("Could not get current slot number: %w", err)
174174
}
175175
return strconv.FormatUint(slotNumber, 10), nil
176176
}
177177

178-
// getCurrentSlotNumber gets current slot number with consensus from multiple requests.
179-
func getCurrentSlotNumber(gatewayURL string, headers http.Header) (uint64, error) {
180-
// Track frequency of each slot number seen
178+
// getCurrentConsensusSlotNumber gets current slot number with consensus from multiple requests.
179+
func getCurrentConsensusSlotNumber(gatewayURL string, headers http.Header) (uint64, error) {
181180
slotNumbers := make(map[uint64]int)
182181
maxAttempts := 10
183182
requiredAgreement := 3
184-
client := &http.Client{Timeout: 5 * time.Second}
183+
client := &http.Client{Timeout: 2 * time.Second}
185184

186-
// Make multiple attempts to get consensus
185+
// Make requests to get current slot number rapidly in parallel
186+
results := make(chan uint64, maxAttempts)
187187
for range maxAttempts {
188-
slotNum, err := fetchSlotNumber(client, gatewayURL, headers)
189-
if err != nil {
190-
continue
191-
}
188+
go func() {
189+
if height, err := getCurrentSlotNumber(client, gatewayURL, headers); err == nil {
190+
results <- height
191+
}
192+
}()
193+
}
192194

193-
// Update consensus tracking
194-
slotNumbers[slotNum]++
195-
if slotNumbers[slotNum] >= requiredAgreement {
196-
return slotNum, nil
195+
// Collect results quickly
196+
timeout := time.After(5 * time.Second)
197+
collect:
198+
for range maxAttempts {
199+
select {
200+
case height := <-results:
201+
slotNumbers[height]++
202+
if slotNumbers[height] >= requiredAgreement {
203+
return height, nil
204+
}
205+
case <-timeout:
206+
break collect
197207
}
198208
}
199209

200-
// If we get here, we didn't reach consensus
201-
return 0, fmt.Errorf("failed to reach consensus on slot number after %d attempts", maxAttempts)
210+
// Return the most recent height seen if no consensus
211+
var maxSlotNumber uint64
212+
for slotNumber := range slotNumbers {
213+
if slotNumber > maxSlotNumber {
214+
maxSlotNumber = slotNumber
215+
}
216+
}
217+
return maxSlotNumber, nil
202218
}
203219

204-
// fetchSlotNumber makes a single request to get the current slot number using getEpochInfo.
205-
func fetchSlotNumber(client *http.Client, gatewayURL string, headers http.Header) (uint64, error) {
220+
// getCurrentSlotNumber makes a single request to get the current slot number using getEpochInfo.
221+
func getCurrentSlotNumber(client *http.Client, gatewayURL string, headers http.Header) (uint64, error) {
206222
// Build and send request
207223
req, err := buildEpochInfoRequest(gatewayURL, headers)
208224
if err != nil {

0 commit comments

Comments
 (0)