Skip to content

Commit 3b94ffd

Browse files
committed
chore: clean up and tests
1 parent 7866df0 commit 3b94ffd

File tree

5 files changed

+247
-24
lines changed

5 files changed

+247
-24
lines changed

.github/workflows/framework-golden-tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ jobs:
4747
config: smoke_ton.toml
4848
count: 1
4949
timeout: 10m
50+
- name: TestTonParallel
51+
config: parallel_ton.toml
52+
count: 1
53+
timeout: 10m
5054
- name: TestUpgrade
5155
config: upgrade.toml
5256
count: 1

framework/components/blockchain/ton.go

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ const (
3131
DefaultTonHlWalletMnemonic = "twenty unfair stay entry during please water april fabric morning length lumber style tomorrow melody similar forum width ride render void rather custom coin"
3232
)
3333

34+
var (
35+
commonDBVars = map[string]string{
36+
"POSTGRES_DIALECT": "postgresql+asyncpg",
37+
"POSTGRES_HOST": "index-postgres",
38+
"POSTGRES_PORT": "5432",
39+
"POSTGRES_USER": "postgres",
40+
"POSTGRES_DB": "ton_index",
41+
"POSTGRES_PASSWORD": "PostgreSQL1234",
42+
"POSTGRES_DBNAME": "ton_index",
43+
"TON_INDEXER_TON_HTTP_API_ENDPOINT": "http://tonhttpapi:8081/",
44+
"TON_INDEXER_IS_TESTNET": "0",
45+
"TON_INDEXER_REDIS_DSN": "redis://redis:6379",
46+
"TON_WORKER_FROM": "1",
47+
"TON_WORKER_DBROOT": "/tondb",
48+
"TON_WORKER_BINARY": "ton-index-postgres-v2",
49+
"TON_WORKER_ADDITIONAL_ARGS": "",
50+
}
51+
)
52+
3453
type containerTemplate struct {
3554
Name string
3655
Image string
@@ -55,25 +74,6 @@ type hostPortMapping struct {
5574
IndexAPIPort string
5675
}
5776

58-
var (
59-
commonDBVars = map[string]string{
60-
"POSTGRES_DIALECT": "postgresql+asyncpg",
61-
"POSTGRES_HOST": "index-postgres",
62-
"POSTGRES_PORT": "5432",
63-
"POSTGRES_USER": "postgres",
64-
"POSTGRES_DB": "ton_index",
65-
"POSTGRES_PASSWORD": "PostgreSQL1234",
66-
"POSTGRES_DBNAME": "ton_index",
67-
"TON_INDEXER_TON_HTTP_API_ENDPOINT": "http://tonhttpapi:8081/",
68-
"TON_INDEXER_IS_TESTNET": "0",
69-
"TON_INDEXER_REDIS_DSN": "redis://redis:6379",
70-
"TON_WORKER_FROM": "1",
71-
"TON_WORKER_DBROOT": "/tondb",
72-
"TON_WORKER_BINARY": "ton-index-postgres-v2",
73-
"TON_WORKER_ADDITIONAL_ARGS": "",
74-
}
75-
)
76-
7777
func commonContainer(
7878
ctx context.Context,
7979
name string,
@@ -120,9 +120,9 @@ func generateUniquePortsFromBase(basePort string) (*hostPortMapping, error) {
120120
return nil, fmt.Errorf("invalid base port %s: %w", basePort, err)
121121
}
122122

123-
// Use larger multipliers to ensure no overlap between different base ports
124-
// Each base port gets a range of 100 ports to avoid conflicts
125-
portMultiplier := (base - DefaultTonSimpleServerPort) * 100 // 8000->0, 8001->100, 8002->200, etc.
123+
// use larger multipliers to ensure no overlap between different base ports
124+
// 8000->0, 8001->100, 8002->200, etc.
125+
portMultiplier := (base - DefaultTonSimpleServerPort) * 100
126126

127127
mapping := &hostPortMapping{
128128
SimpleServer: basePort,
@@ -169,6 +169,7 @@ func newTon(in *Input) (*Output, error) {
169169
Labels: framework.DefaultTCLabels(),
170170
},
171171
})
172+
framework.L.Info().Str("output", string(networkName)).Msg("TON Docker network created")
172173

173174
tonServices := []containerTemplate{
174175
{
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[blockchain_a]
2+
type = "ton"
3+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
4+
port = "8000"
5+
6+
[blockchain_b]
7+
type = "ton"
8+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
9+
port = "8001"
10+
11+
[blockchain_c]
12+
type = "ton"
13+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
14+
port = "8002"
15+
16+
[blockchain_d]
17+
type = "ton"
18+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
19+
port = "8003"
20+
21+
[blockchain_e]
22+
type = "ton"
23+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
24+
port = "8004"
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package examples
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
"sync"
8+
"testing"
9+
"time"
10+
11+
"github.com/stretchr/testify/require"
12+
"github.com/xssnick/tonutils-go/liteclient"
13+
"github.com/xssnick/tonutils-go/ton"
14+
"github.com/xssnick/tonutils-go/ton/wallet"
15+
16+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
17+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
18+
)
19+
20+
type CfgTonParallel struct {
21+
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
22+
BlockchainB *blockchain.Input `toml:"blockchain_b" validate:"required"`
23+
BlockchainC *blockchain.Input `toml:"blockchain_c" validate:"required"`
24+
BlockchainD *blockchain.Input `toml:"blockchain_d" validate:"required"`
25+
BlockchainE *blockchain.Input `toml:"blockchain_e" validate:"required"`
26+
}
27+
28+
type DeploymentResult struct {
29+
Name string
30+
Blockchain *blockchain.Output
31+
Client ton.APIClientWrapped
32+
Error error
33+
StartTime time.Time
34+
EndTime time.Time
35+
}
36+
37+
func TestTonParallel(t *testing.T) {
38+
in, err := framework.Load[CfgTonParallel](t)
39+
require.NoError(t, err)
40+
41+
configs := map[string]*blockchain.Input{
42+
"blockchain_a": in.BlockchainA,
43+
"blockchain_b": in.BlockchainB,
44+
"blockchain_c": in.BlockchainC,
45+
"blockchain_d": in.BlockchainD,
46+
"blockchain_e": in.BlockchainE,
47+
}
48+
49+
var mu sync.Mutex
50+
results := make(map[string]DeploymentResult)
51+
overallStartTime := time.Now()
52+
53+
// Deploy all chains in parallel
54+
for name, config := range configs {
55+
t.Run("deploy_"+name, func(t *testing.T) {
56+
t.Parallel()
57+
58+
result := deployTonInstance(name, config)
59+
60+
mu.Lock()
61+
results[name] = result
62+
mu.Unlock()
63+
64+
if result.Error != nil {
65+
t.Errorf("Failed to deploy %s: %v", result.Name, result.Error)
66+
return
67+
}
68+
69+
deploymentDuration := result.EndTime.Sub(result.StartTime)
70+
t.Logf("✅ Successfully deployed %s in %v", result.Name, deploymentDuration)
71+
72+
// Basic connectivity test
73+
validateConnectivity(t, result)
74+
75+
// Wallet test
76+
validateWallet(t, result)
77+
})
78+
}
79+
80+
overallDuration := time.Since(overallStartTime)
81+
t.Logf("🎉 All 5 blockchain deployments completed in %v", overallDuration)
82+
83+
// Validate port isolation
84+
var successfulResults []DeploymentResult
85+
for _, result := range results {
86+
if result.Error == nil {
87+
successfulResults = append(successfulResults, result)
88+
}
89+
}
90+
91+
if len(successfulResults) == 5 {
92+
validatePortIsolation(t, successfulResults)
93+
}
94+
}
95+
96+
func deployTonInstance(name string, config *blockchain.Input) DeploymentResult {
97+
result := DeploymentResult{
98+
Name: name,
99+
StartTime: time.Now(),
100+
}
101+
102+
bc, err := blockchain.NewBlockchainNetwork(config)
103+
if err != nil {
104+
result.Error = fmt.Errorf("failed to create blockchain network: %w", err)
105+
result.EndTime = time.Now()
106+
return result
107+
}
108+
109+
result.Blockchain = bc
110+
111+
connectionPool := liteclient.NewConnectionPool()
112+
cfg, err := liteclient.GetConfigFromUrl(context.Background(),
113+
fmt.Sprintf("http://%s/localhost.global.config.json", bc.Nodes[0].ExternalHTTPUrl))
114+
if err != nil {
115+
result.Error = fmt.Errorf("failed to get config from URL: %w", err)
116+
result.EndTime = time.Now()
117+
return result
118+
}
119+
120+
err = connectionPool.AddConnectionsFromConfig(context.Background(), cfg)
121+
if err != nil {
122+
result.Error = fmt.Errorf("failed to add connections from config: %w", err)
123+
result.EndTime = time.Now()
124+
return result
125+
}
126+
127+
result.Client = ton.NewAPIClient(connectionPool).WithRetry()
128+
result.EndTime = time.Now()
129+
return result
130+
}
131+
132+
// validateConnectivity tests basic blockchain connectivity
133+
func validateConnectivity(t *testing.T, result DeploymentResult) {
134+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
135+
defer cancel()
136+
137+
master, err := result.Client.GetMasterchainInfo(ctx)
138+
require.NoError(t, err, "Should be able to get masterchain info for %s", result.Name)
139+
require.NotNil(t, master, "Masterchain info should not be nil for %s", result.Name)
140+
141+
t.Logf("✓ %s: Connected, seqno: %d", result.Name, master.SeqNo)
142+
}
143+
144+
func validateWallet(t *testing.T, result DeploymentResult) {
145+
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
146+
defer cancel()
147+
148+
rawHlWallet, err := wallet.FromSeed(result.Client,
149+
strings.Fields(blockchain.DefaultTonHlWalletMnemonic), wallet.HighloadV2Verified)
150+
require.NoError(t, err, "Failed to create wallet for %s", result.Name)
151+
152+
mcFunderWallet, err := wallet.FromPrivateKeyWithOptions(result.Client,
153+
rawHlWallet.PrivateKey(), wallet.HighloadV2Verified, wallet.WithWorkchain(-1))
154+
require.NoError(t, err, "Failed to create funder wallet for %s", result.Name)
155+
156+
funder, err := mcFunderWallet.GetSubwallet(uint32(42))
157+
require.NoError(t, err, "Failed to get subwallet for %s", result.Name)
158+
159+
require.Equal(t, funder.Address().StringRaw(), blockchain.DefaultTonHlWalletAddress,
160+
"Funder address mismatch for %s", result.Name)
161+
162+
master, err := result.Client.GetMasterchainInfo(ctx)
163+
require.NoError(t, err, "Failed to get masterchain info for %s", result.Name)
164+
165+
funderBalance, err := funder.GetBalance(ctx, master)
166+
require.NoError(t, err, "Failed to get funder balance for %s", result.Name)
167+
require.Equal(t, funderBalance.Nano().String(), "1000000000000000",
168+
"Funder balance mismatch for %s", result.Name)
169+
170+
t.Logf("✓ %s: Wallet OK, balance: %s TON", result.Name, funderBalance.String())
171+
}
172+
173+
func validatePortIsolation(t *testing.T, results []DeploymentResult) {
174+
portUsage := make(map[string][]string)
175+
176+
for _, result := range results {
177+
httpUrl := result.Blockchain.Nodes[0].ExternalHTTPUrl
178+
parts := strings.Split(httpUrl, ":")
179+
if len(parts) == 2 {
180+
port := parts[1]
181+
portUsage[port] = append(portUsage[port], result.Name)
182+
}
183+
}
184+
185+
conflicts := 0
186+
for port, chains := range portUsage {
187+
if len(chains) > 1 {
188+
t.Errorf("❌ Port conflict! Port %s used by: %v", port, chains)
189+
conflicts++
190+
}
191+
}
192+
193+
if conflicts == 0 {
194+
t.Logf("🎯 Perfect port isolation across %d chains!", len(results))
195+
}
196+
}

framework/examples/myproject/smoke_ton_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,5 @@ func TestTonSmoke(t *testing.T) {
5454
require.NoError(t, err, "failed to get funder balance")
5555
require.Equal(t, funderBalance.Nano().String(), "1000000000000000", "funder balance mismatch")
5656
})
57-
58-
// time.Sleep(500 * time.Second) // wait for connections to be established
5957
})
6058
}

0 commit comments

Comments
 (0)