Skip to content

Commit cc9d6f0

Browse files
authored
Update integration tests to use MANUAL_CLOSE (#3167)
Use Core's `MANUAL_CLOSE` mode in integration tests so that we don't have to wait for ledgers to be closed (we close them explicitly). This paves the way for enabling integration tests for Captive Core.
1 parent 0b17a67 commit cc9d6f0

File tree

3 files changed

+84
-26
lines changed

3 files changed

+84
-26
lines changed

Diff for: services/horizon/internal/integration/protocol14_sponsorship_ops_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ func TestSponsorships(t *testing.T) {
363363
// Submit the preauthorized transaction
364364
var txResult xdr.TransactionResult
365365
tt.NoError(err)
366-
txResp, err = client.SubmitTransactionXDR(preAuthTxB64)
366+
txResp, err = itest.SubmitTransactionXDR(preAuthTxB64)
367367
tt.NoError(err)
368368
err = xdr.SafeUnmarshalBase64(txResp.ResultXdr, &txResult)
369369
tt.NoError(err)
@@ -716,11 +716,11 @@ func TestSponsorships(t *testing.T) {
716716

717717
// Check that effects populate.
718718
expectedEffects := map[string][]uint{
719-
effects.EffectTypeNames[effects.EffectClaimableBalanceSponsorshipCreated]: []uint{0, 1},
720-
effects.EffectTypeNames[effects.EffectClaimableBalanceCreated]: []uint{0, 1},
721-
effects.EffectTypeNames[effects.EffectClaimableBalanceClaimantCreated]: []uint{0, 2},
722-
effects.EffectTypeNames[effects.EffectClaimableBalanceSponsorshipRemoved]: []uint{0, 1},
723-
effects.EffectTypeNames[effects.EffectClaimableBalanceClaimed]: []uint{0, 1},
719+
effects.EffectTypeNames[effects.EffectClaimableBalanceSponsorshipCreated]: {0, 1},
720+
effects.EffectTypeNames[effects.EffectClaimableBalanceCreated]: {0, 1},
721+
effects.EffectTypeNames[effects.EffectClaimableBalanceClaimantCreated]: {0, 2},
722+
effects.EffectTypeNames[effects.EffectClaimableBalanceSponsorshipRemoved]: {0, 1},
723+
effects.EffectTypeNames[effects.EffectClaimableBalanceClaimed]: {0, 1},
724724
}
725725

726726
effectsPage, err := client.Effects(sdk.EffectRequest{Order: "desc", Limit: 100})

Diff for: services/horizon/internal/integration/protocol14_state_verifier_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,11 @@ func TestProtocol14StateVerifier(t *testing.T) {
9898
assert.NoError(t, err)
9999
assert.True(t, txResp.Successful)
100100

101-
// Wait for the first checkpoint ledger
101+
// Reach the first checkpoint ledger
102102
for !itest.LedgerIngested(63) {
103-
t.Log("First checkpoint ledger (63) not closed yet...")
104-
time.Sleep(5 * time.Second)
103+
err := itest.CloseCoreLedger()
104+
assert.NoError(t, err)
105+
time.Sleep(50 * time.Millisecond)
105106
}
106107

107108
var metrics string

Diff for: services/horizon/internal/test/integration/integration.go

+74-17
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ import (
2222
"github.com/spf13/cobra"
2323

2424
sdk "github.com/stellar/go/clients/horizonclient"
25+
"github.com/stellar/go/clients/stellarcore"
2526
"github.com/stellar/go/keypair"
2627
proto "github.com/stellar/go/protocols/horizon"
2728
horizon "github.com/stellar/go/services/horizon/internal"
2829
"github.com/stellar/go/support/db/dbtest"
2930
"github.com/stellar/go/support/errors"
31+
"github.com/stellar/go/support/log"
3032
"github.com/stellar/go/txnbuild"
3133
"github.com/stellar/go/xdr"
3234
"github.com/stretchr/testify/assert"
@@ -60,6 +62,7 @@ type Test struct {
6062
config Config
6163
cli client.APIClient
6264
hclient *sdk.Client
65+
cclient *stellarcore.Client
6366
container container.ContainerCreateCreatedBody
6467
app *horizon.App
6568
}
@@ -143,13 +146,23 @@ func NewTest(t *testing.T, config Config) *Test {
143146
t.Fatal(errors.Wrap(err, "error starting docker container"))
144147
}
145148

149+
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
150+
defer cancel()
151+
152+
containerInfo, err := i.cli.ContainerInspect(ctx, i.container.ID)
153+
if err != nil {
154+
i.t.Fatal(errors.Wrap(err, "error inspecting container"))
155+
}
156+
stellarCoreBinding := containerInfo.NetworkSettings.Ports[stellarCorePort][0]
157+
coreURL := fmt.Sprintf("http://%s:%s", stellarCoreBinding.HostIP, stellarCoreBinding.HostPort)
146158
// only use horizon from quickstart container when testing captive core
147159
if os.Getenv("HORIZON_INTEGRATION_ENABLE_CAPTIVE_CORE") == "" {
148-
i.startHorizon()
160+
i.startHorizon(containerInfo, coreURL)
149161
}
150162

151163
doCleanup = false
152164
i.hclient = &sdk.Client{HorizonURL: "http://localhost:8000"}
165+
i.cclient = &stellarcore.Client{URL: coreURL}
153166

154167
// Register cleanup handlers (on panic and ctrl+c) so the container is
155168
// removed even if ingestion or testing fails.
@@ -162,6 +175,7 @@ func NewTest(t *testing.T, config Config) *Test {
162175
os.Exit(0)
163176
}()
164177

178+
i.waitForCore()
165179
i.waitForIngestionAndUpgrade()
166180
return i
167181
}
@@ -208,26 +222,16 @@ func (i *Test) setupHorizonBinary() {
208222
}
209223
}
210224

211-
func (i *Test) startHorizon() {
212-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
213-
defer cancel()
214-
215-
info, err := i.cli.ContainerInspect(ctx, i.container.ID)
216-
if err != nil {
217-
i.t.Fatal(errors.Wrap(err, "error inspecting container"))
218-
}
219-
220-
stellarCore := info.NetworkSettings.Ports[stellarCorePort][0]
221-
222-
stellarCorePostgres := info.NetworkSettings.Ports[stellarCorePostgresPort][0]
225+
func (i *Test) startHorizon(containerInfo types.ContainerJSON, coreURL string) {
226+
stellarCorePostgres := containerInfo.NetworkSettings.Ports[stellarCorePostgresPort][0]
223227
stellarCorePostgresURL := fmt.Sprintf(
224228
"postgres://stellar:%s@%s:%s/core",
225229
stellarCorePostgresPassword,
226230
stellarCorePostgres.HostIP,
227231
stellarCorePostgres.HostPort,
228232
)
229233

230-
historyArchive := info.NetworkSettings.Ports[historyArchivePort][0]
234+
historyArchive := containerInfo.NetworkSettings.Ports[historyArchivePort][0]
231235

232236
horizonPostgresURL := dbtest.Postgres(i.t).DSN
233237

@@ -243,7 +247,7 @@ func (i *Test) startHorizon() {
243247
}
244248
cmd.SetArgs([]string{
245249
"--stellar-core-url",
246-
fmt.Sprintf("http://%s:%s", stellarCore.HostIP, stellarCore.HostPort),
250+
coreURL,
247251
"--history-archive-urls",
248252
fmt.Sprintf("http://%s:%s", historyArchive.HostIP, historyArchive.HostPort),
249253
"--ingest",
@@ -259,17 +263,48 @@ func (i *Test) startHorizon() {
259263
})
260264
configOpts.Init(cmd)
261265

262-
if err = cmd.Execute(); err != nil {
266+
if err := cmd.Execute(); err != nil {
263267
i.t.Fatalf("cannot initialize horizon: %s", err)
264268
}
265269

266-
if err = i.app.Ingestion().BuildGenesisState(); err != nil {
270+
if err := i.app.Ingestion().BuildGenesisState(); err != nil {
267271
i.t.Fatalf("cannot build genesis state: %s", err)
268272
}
269273

270274
go i.app.Serve()
271275
}
272276

277+
// Wait for core to be up and manually close the first ledger
278+
func (i *Test) waitForCore() {
279+
for t := 30 * time.Second; t >= 0; t -= time.Second {
280+
i.t.Log("Waiting for core to be up...")
281+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
282+
_, err := i.cclient.Info(ctx)
283+
cancel()
284+
if err == nil {
285+
break
286+
}
287+
time.Sleep(time.Second)
288+
}
289+
290+
// We need to wait for Core to be armed before closing the first ledger
291+
// Otherwise, for some reason, the protocol version of the ledger stays at 0
292+
// TODO: instead of sleeping we should ensure Core's status (in GET /info) is "Armed"
293+
// but, to do so, we should first expose it in Core's client.
294+
time.Sleep(time.Second)
295+
if err := i.CloseCoreLedger(); err != nil {
296+
i.t.Fatalf("Failed to manually close the second ledger: %s", err)
297+
}
298+
299+
// Make sure that the Sleep above was successful
300+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
301+
info, err := i.cclient.Info(ctx)
302+
cancel()
303+
if err != nil || !info.IsSynced() {
304+
log.Fatal("failed to wait for Core to be synced")
305+
}
306+
}
307+
273308
func (i *Test) waitForIngestionAndUpgrade() {
274309
for t := 30 * time.Second; t >= 0; t -= time.Second {
275310
i.t.Log("Waiting for ingestion and protocol upgrade...")
@@ -380,6 +415,7 @@ func createTestContainer(i *Test, image string) error {
380415
Cmd: []string{
381416
"--standalone",
382417
"--protocol-version", strconv.FormatInt(int64(i.config.ProtocolVersion), 10),
418+
"--enable-core-manual-close",
383419
},
384420
}
385421
hostConfig := &container.HostConfig{}
@@ -602,11 +638,32 @@ func (i *Test) CreateSignedTransaction(
602638
return tx, nil
603639
}
604640

641+
func (i *Test) CloseCoreLedger() error {
642+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
643+
defer cancel()
644+
return i.cclient.ManualClose(ctx)
645+
}
646+
605647
func (i *Test) SubmitTransaction(tx *txnbuild.Transaction) (proto.Transaction, error) {
606648
txb64, err := tx.Base64()
607649
if err != nil {
608650
return proto.Transaction{}, err
609651
}
652+
return i.SubmitTransactionXDR(txb64)
653+
}
654+
655+
func (i *Test) SubmitTransactionXDR(txb64 string) (proto.Transaction, error) {
656+
// Core runs in manual-close mode, so we need to close ledgers explicitly
657+
// We need to close the ledger in parallel because Horizon's submission endpoint
658+
// blocks until the transaction is in a closed ledger
659+
go func() {
660+
// This sleep is ugly, but a better approach would probably require
661+
// instrumenting Horizon to tell us when the transaction was sent to core.
662+
time.Sleep(time.Millisecond * 100)
663+
if err := i.CloseCoreLedger(); err != nil {
664+
log.Fatalf("failed to CloseCoreLedger(): %s", err)
665+
}
666+
}()
610667

611668
return i.Client().SubmitTransactionXDR(txb64)
612669
}

0 commit comments

Comments
 (0)