Skip to content

Commit b1c30ad

Browse files
committed
test(cmd): drop incorrect_password case, revert to txdb
Remove the incorrect_password subtests from TestShell_BeforeNode and TestShell_RunNode_WithBeforeNode. They were assertions about keystore decryption failure layered on top of the CLI, and the same invariant is already covered at the correct layer by TestMasterKeystore_Unlock_Save/won't_load_a_saved_keyRing_if_the_password_is_incorrect in core/services/keystore/master_test.go. With the wrong-password case gone, these tests no longer need cross-connection state sharing, so they revert to txdb via the codebase-default configtest.NewGeneralConfig. No seeding plumbing, no heavyweight.FullTestDBV2, no per-test physical DB — one conn per test, rolled back at Close, matching how the rest of core/cmd works. Simplifies TestShell_RunNode_WithBeforeNode to a single straight-line happy-path run (the "expectStart=false" branch only existed to host the incorrect_password case, which is gone). Removes now-unused commonkeystore import. Refs: CORE-2388
1 parent 7d9b3f5 commit b1c30ad

1 file changed

Lines changed: 69 additions & 137 deletions

File tree

core/cmd/shell_local_test.go

Lines changed: 69 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"github.com/stretchr/testify/require"
1818
"github.com/urfave/cli"
1919

20-
commonkeystore "github.com/smartcontractkit/chainlink-common/keystore"
2120
commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
2221
"github.com/smartcontractkit/chainlink-common/pkg/sqlutil"
2322
pgcommon "github.com/smartcontractkit/chainlink-common/pkg/sqlutil/pg"
@@ -509,64 +508,34 @@ func TestShell_RemoveBlocks(t *testing.T) {
509508
func TestShell_BeforeNode(t *testing.T) {
510509
testutils.SkipShortDB(t)
511510

512-
// Use heavyweight.FullTestDBV2 (real isolated per-test Postgres DB,
513-
// dropped on t.Cleanup) because both subtests must share a persisted
514-
// encrypted key ring:
515-
//
516-
// 1. keystore.Unlock on an *empty* keystore silently creates a fresh
517-
// key ring encrypted with whatever password you pass — so any
518-
// password "works" against an empty DB, including the wrong one.
519-
// The incorrect_password subtest can only fail meaningfully if
520-
// there is an existing ring to fail decryption against.
521-
//
522-
// 2. txdb isolation can't be used here: chainlink-common's
523-
// pg.NewConnection deliberately passes uuid.New().String() as the
524-
// DSN to the txdb driver on every call (see
525-
// chainlink-common/pkg/sqlutil/pg/pg.go:69 — "Each ORM should be
526-
// encapsulated in its own transaction"). That means the seed
527-
// connection and BeforeNode's LockedDB land on different keys in
528-
// the txdb driver's DSN-keyed conns map and get independent
529-
// transactions, so the seed would be invisible to BeforeNode.
530-
// This convention is also why Erik's PR #21504, which tried to
531-
// force txdb via a newAppWithOpts override, couldn't make the
532-
// incorrect_password case work.
533-
//
534-
// A real per-test physical DB gives us the cross-connection visibility
535-
// the test needs, with its own cleanup and without polluting the
536-
// shared chainlink_test DB used by every other test.
537-
cfg, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) {
538-
s.Password.Keystore = models.NewSecret("dummy-to-pass-validation")
539-
c.EVM[0].Nodes[0].Name = ptr("fake")
540-
c.EVM[0].Nodes[0].HTTPURL = commonconfig.MustParseURL("http://fake.com")
541-
c.EVM[0].Nodes[0].WSURL = commonconfig.MustParseURL("WSS://fake.com/ws")
542-
c.Insecure.OCRDevelopmentMode = nil
543-
})
544-
545-
// Seed one CSA key encrypted with the correct password so the
546-
// incorrect_password subtest has something to fail decryption against.
547-
correctPwd, err := utils.PasswordFromFile("../internal/fixtures/correct_password.txt")
548-
require.NoError(t, err)
549-
ctx := testutils.Context(t)
550-
seedDS := sqlutil.WrapDataSource(sqlxDB, logger.TestLogger(t),
551-
sqlutil.TimeoutHook(cfg.Database().DefaultQueryTimeout),
552-
sqlutil.MonitorHook(cfg.Database().LogSQL))
553-
seedKS := keystore.New(seedDS, commonkeystore.FastScryptParams, logger.TestLogger(t).Infof)
554-
require.NoError(t, seedKS.Unlock(ctx, correctPwd))
555-
_, err = seedKS.CSA().Create(ctx)
556-
require.NoError(t, err)
557-
511+
// Uses txdb via configtest.NewGeneralConfig (the codebase default) so
512+
// each subtest runs in its own rolled-back transaction. We deliberately
513+
// do NOT test the "wrong password against an existing key ring" case
514+
// here — that is a keystore invariant, already covered by
515+
// TestMasterKeystore_Unlock_Save/won't_load_a_saved_keyRing_if_the_password_is_incorrect
516+
// in core/services/keystore/master_test.go, which owns that layer.
517+
// Testing it at the CLI level would require cross-connection visibility
518+
// that txdb doesn't provide in this codebase (pg.NewConnection generates
519+
// a fresh UUID DSN per call — see chainlink-common/pkg/sqlutil/pg/pg.go:69).
558520
tests := []struct {
559521
name string
560522
pwdfile string
561523
wantUnlocked bool
562524
}{
563525
{"correct password", "../internal/fixtures/correct_password.txt", true},
564-
{"incorrect password", "../internal/fixtures/incorrect_password.txt", false},
565526
{"wrong file", "doesntexist.txt", false},
566527
}
567528

568529
for _, test := range tests {
569530
t.Run(test.name, func(t *testing.T) {
531+
cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {
532+
s.Password.Keystore = models.NewSecret("dummy-to-pass-validation")
533+
c.EVM[0].Nodes[0].Name = ptr("fake")
534+
c.EVM[0].Nodes[0].HTTPURL = commonconfig.MustParseURL("http://fake.com")
535+
c.EVM[0].Nodes[0].WSURL = commonconfig.MustParseURL("WSS://fake.com/ws")
536+
c.Insecure.OCRDevelopmentMode = nil
537+
})
538+
570539
shell := cmd.Shell{
571540
Config: cfg,
572541
KeyStoreAuthenticator: cmd.TerminalKeyStoreAuthenticator{
@@ -611,112 +580,75 @@ func TestShell_BeforeNode(t *testing.T) {
611580
func TestShell_RunNode_WithBeforeNode(t *testing.T) {
612581
testutils.SkipShortDB(t)
613582

614-
// See TestShell_BeforeNode for the full rationale (TL;DR: txdb can't
615-
// be used to seed shared state because pg.NewConnection generates a
616-
// fresh UUID DSN per call, isolating every pool into its own
617-
// transaction — so we need a real per-test physical DB instead).
618-
cfg, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) {
583+
// Uses txdb via configtest.NewGeneralConfig. We only exercise the
584+
// happy path here — the wrong-password case is covered at the correct
585+
// layer by TestMasterKeystore_Unlock_Save in core/services/keystore
586+
// and does not need to be reasserted through the CLI.
587+
cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {
619588
s.Password.Keystore = models.NewSecret("dummy-to-pass-validation")
620589
c.EVM[0].Nodes[0].Name = ptr("fake")
621590
c.EVM[0].Nodes[0].HTTPURL = commonconfig.MustParseURL("http://fake.com")
622591
c.EVM[0].Nodes[0].WSURL = commonconfig.MustParseURL("WSS://fake.com/ws")
623592
c.Insecure.OCRDevelopmentMode = nil
624593
})
625594

626-
// Seed one CSA key encrypted with the correct password so the
627-
// incorrect_password subtest has something to fail decryption against.
628-
correctPwd, err := utils.PasswordFromFile("../internal/fixtures/correct_password.txt")
629-
require.NoError(t, err)
630-
seedCtx := testutils.Context(t)
631-
seedDS := sqlutil.WrapDataSource(sqlxDB, logger.TestLogger(t),
632-
sqlutil.TimeoutHook(cfg.Database().DefaultQueryTimeout),
633-
sqlutil.MonitorHook(cfg.Database().LogSQL))
634-
seedKS := keystore.New(seedDS, commonkeystore.FastScryptParams, logger.TestLogger(t).Infof)
635-
require.NoError(t, seedKS.Unlock(seedCtx, correctPwd))
636-
_, err = seedKS.CSA().Create(seedCtx)
637-
require.NoError(t, err)
638-
639-
tests := []struct {
640-
name string
641-
pwdfile string
642-
expectStart bool
643-
}{
644-
{"correct password", "../internal/fixtures/correct_password.txt", true},
645-
{"incorrect password", "../internal/fixtures/incorrect_password.txt", false},
646-
}
595+
db := pgtest.NewSqlxDB(t)
596+
keyStore := cltest.NewKeyStore(t, db)
597+
authProviderORM := localauth.NewORM(db, time.Minute, logger.TestLogger(t), audit.NoopLogger)
647598

648-
for _, test := range tests {
649-
t.Run(test.name, func(t *testing.T) {
650-
// Use the shared sqlxDB for the mock application's keystore and
651-
// auth provider so they see the seeded key ring and any state
652-
// written by BeforeNode.
653-
db := sqlxDB
654-
keyStore := cltest.NewKeyStore(t, db)
655-
authProviderORM := localauth.NewORM(db, time.Minute, logger.TestLogger(t), audit.NoopLogger)
599+
testRelayers := genTestEVMRelayers(t, cfg, db, keyStore.Eth(), &keystore.CSASigner{CSA: keyStore.CSA()})
656600

657-
testRelayers := genTestEVMRelayers(t, cfg, db, keyStore.Eth(), &keystore.CSASigner{CSA: keyStore.CSA()})
601+
// Purge fixture users to test assumption of single admin
602+
pgtest.MustExec(t, db, "DELETE FROM users;")
658603

659-
// Purge fixture users to test assumption of single admin
660-
pgtest.MustExec(t, db, "DELETE FROM users;")
604+
app := mocks.NewApplication(t)
605+
app.On("AuthenticationProvider").Return(authProviderORM).Maybe()
606+
app.On("BasicAdminUsersORM").Return(authProviderORM).Maybe()
607+
app.On("GetKeyStore").Return(keyStore).Maybe()
608+
app.On("GetRelayers").Return(testRelayers).Maybe()
609+
app.On("Start", mock.Anything).Maybe().Return(nil)
610+
app.On("Stop").Maybe().Return(nil)
611+
app.On("ID").Maybe().Return(uuid.New())
661612

662-
app := mocks.NewApplication(t)
663-
app.On("AuthenticationProvider").Return(authProviderORM).Maybe()
664-
app.On("BasicAdminUsersORM").Return(authProviderORM).Maybe()
665-
app.On("GetKeyStore").Return(keyStore).Maybe()
666-
app.On("GetRelayers").Return(testRelayers).Maybe()
667-
app.On("Start", mock.Anything).Maybe().Return(nil)
668-
app.On("Stop").Maybe().Return(nil)
669-
app.On("ID").Maybe().Return(uuid.New())
613+
ethClient := clienttest.NewClient(t)
614+
ethClient.On("Dial", mock.Anything).Return(nil).Maybe()
615+
ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(10), nil).Maybe()
670616

671-
ethClient := clienttest.NewClient(t)
672-
ethClient.On("Dial", mock.Anything).Return(nil).Maybe()
673-
ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(10), nil).Maybe()
617+
cltest.MustInsertRandomKey(t, keyStore.Eth())
618+
apiPrompt := cltest.NewMockAPIInitializer(t)
674619

675-
cltest.MustInsertRandomKey(t, keyStore.Eth())
676-
apiPrompt := cltest.NewMockAPIInitializer(t)
677-
678-
shell := cmd.Shell{
679-
Config: cfg,
680-
FallbackAPIInitializer: apiPrompt,
681-
Runner: cltest.EmptyRunner{},
682-
AppFactory: cltest.InstanceAppFactory{App: app},
683-
KeyStoreAuthenticator: cmd.TerminalKeyStoreAuthenticator{
684-
Prompter: &cltest.MockCountingPrompter{T: t, NotTerminal: true},
685-
},
686-
Logger: logger.TestLogger(t),
687-
}
688-
// Reset for test isolation
689-
defer resetShellForTest(&shell)
620+
shell := cmd.Shell{
621+
Config: cfg,
622+
FallbackAPIInitializer: apiPrompt,
623+
Runner: cltest.EmptyRunner{},
624+
AppFactory: cltest.InstanceAppFactory{App: app},
625+
KeyStoreAuthenticator: cmd.TerminalKeyStoreAuthenticator{
626+
Prompter: &cltest.MockCountingPrompter{T: t, NotTerminal: true},
627+
},
628+
Logger: logger.TestLogger(t),
629+
}
630+
// Reset for test isolation
631+
defer resetShellForTest(&shell)
690632

691-
set := flag.NewFlagSet("test", 0)
692-
flagSetApplyFromAction(shell.RunNode, set, "")
693-
require.NoError(t, set.Set("password", test.pwdfile))
633+
set := flag.NewFlagSet("test", 0)
634+
flagSetApplyFromAction(shell.RunNode, set, "")
635+
require.NoError(t, set.Set("password", "../internal/fixtures/correct_password.txt"))
694636

695-
c := cli.NewContext(nil, set, nil)
637+
c := cli.NewContext(nil, set, nil)
696638

697-
err := shell.BeforeNode(c)
639+
err := shell.BeforeNode(c)
640+
require.NoError(t, err, "BeforeNode should succeed")
641+
assert.NotNil(t, shell.KeyStore)
642+
assert.NotNil(t, shell.DS)
643+
assert.NotNil(t, shell.LDB)
698644

699-
if test.expectStart {
700-
require.NoError(t, err, "BeforeNode should succeed")
701-
// Verify components are initialized
702-
assert.NotNil(t, shell.KeyStore)
703-
assert.NotNil(t, shell.DS)
704-
assert.NotNil(t, shell.LDB)
645+
// Now test RunNode with pre-authenticated keystore.
646+
err = shell.RunNode(c)
647+
require.NoError(t, err, "RunNode should succeed with authenticated keystore")
648+
assert.Equal(t, 1, apiPrompt.Count, "API should be initialized")
705649

706-
// Now test RunNode with pre-authenticated keystore
707-
// Note: RunNode will start the app but we expect it to work since keystore is authenticated
708-
err = shell.RunNode(c)
709-
require.NoError(t, err, "RunNode should succeed with authenticated keystore")
710-
assert.Equal(t, 1, apiPrompt.Count, "API should be initialized")
711-
} else {
712-
require.Error(t, err, "BeforeNode should fail with incorrect password")
713-
// Don't test RunNode if BeforeNode failed
714-
}
715-
// Clean up database if it was opened
716-
if shell.LDB != nil {
717-
cleanupErr := shell.AfterNode(c)
718-
require.NoError(t, cleanupErr)
719-
}
720-
})
650+
// Clean up database if it was opened
651+
if shell.LDB != nil {
652+
require.NoError(t, shell.AfterNode(c))
721653
}
722654
}

0 commit comments

Comments
 (0)