From be48b91ae4f3136c2ed05d2ee5551df42434cfb0 Mon Sep 17 00:00:00 2001 From: Cael Rowley Date: Wed, 6 May 2026 10:47:27 +0200 Subject: [PATCH 1/2] fix(config): de-flake TestAddFlags by stubbing time source --- pkg/config/config_test.go | 6 ++++++ pkg/config/defaults.go | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 02040ebba9..6e93314d27 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -41,6 +41,12 @@ func TestDefaultConfig(t *testing.T) { } func TestAddFlags(t *testing.T) { + // Freeze the time source feeding randString so AddFlags' internal DefaultConfig() + // and the per-assertion DefaultConfig() calls below produce the same DA.Namespace seed. + origNowUnix := nowUnix + nowUnix = func() int64 { return 2_000_000_000 } + t.Cleanup(func() { nowUnix = origNowUnix }) + // Create a command with flags cmd := &cobra.Command{Use: "test"} AddGlobalFlags(cmd, "test") // Add basic flags first diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 233585e0c5..5ea895a48f 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -133,10 +133,13 @@ func DefaultConfig() Config { } } +// nowUnix returns the current Unix timestamp; package-level so tests can stub it. +var nowUnix = func() int64 { return time.Now().Unix() } + func randString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" result := make([]byte, length) - rng := rand.New(rand.NewSource(time.Now().Unix())) //nolint:gosec // even half random is good enough here. + rng := rand.New(rand.NewSource(nowUnix())) //nolint:gosec // even half random is good enough here. for i := range result { result[i] = charset[rng.Intn(len(charset))] } From 890b7849f167fdd4c8e577100d944c3603856bc5 Mon Sep 17 00:00:00 2001 From: Cael Rowley Date: Thu, 7 May 2026 09:40:22 +0200 Subject: [PATCH 2/2] test(config): de-flake TestAddFlags via testing/synctest --- pkg/config/config_test.go | 247 +++++++++++++++++++------------------- pkg/config/defaults.go | 5 +- 2 files changed, 124 insertions(+), 128 deletions(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 6e93314d27..1f140e5656 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "testing" + "testing/synctest" "time" "github.com/spf13/cobra" @@ -41,133 +42,131 @@ func TestDefaultConfig(t *testing.T) { } func TestAddFlags(t *testing.T) { - // Freeze the time source feeding randString so AddFlags' internal DefaultConfig() - // and the per-assertion DefaultConfig() calls below produce the same DA.Namespace seed. - origNowUnix := nowUnix - nowUnix = func() int64 { return 2_000_000_000 } - t.Cleanup(func() { nowUnix = origNowUnix }) + // AddFlags and the assertions below each call DefaultConfig(), whose DA.Namespace + // is randString-seeded by time.Now().Unix(); run in a synctest bubble so both calls observe the same fake clock. + synctest.Test(t, func(t *testing.T) { + // Create a command with flags + cmd := &cobra.Command{Use: "test"} + AddGlobalFlags(cmd, "test") // Add basic flags first + AddFlags(cmd) - // Create a command with flags - cmd := &cobra.Command{Use: "test"} - AddGlobalFlags(cmd, "test") // Add basic flags first - AddFlags(cmd) + // Get both persistent and regular flags + flags := cmd.Flags() + persistentFlags := cmd.PersistentFlags() + + // Test specific flags + assertFlagValue(t, flags, FlagDBPath, DefaultConfig().DBPath) + assertFlagValue(t, flags, FlagClearCache, DefaultConfig().ClearCache) + + // Node flags + assertFlagValue(t, flags, FlagAggregator, DefaultConfig().Node.Aggregator) + assertFlagValue(t, flags, FlagBasedSequencer, DefaultConfig().Node.BasedSequencer) + assertFlagValue(t, flags, FlagLight, DefaultConfig().Node.Light) + assertFlagValue(t, flags, FlagBlockTime, DefaultConfig().Node.BlockTime.Duration) + assertFlagValue(t, flags, FlagLazyAggregator, DefaultConfig().Node.LazyMode) + assertFlagValue(t, flags, FlagMaxPendingHeadersAndData, DefaultConfig().Node.MaxPendingHeadersAndData) + assertFlagValue(t, flags, FlagLazyBlockTime, DefaultConfig().Node.LazyBlockInterval.Duration) + assertFlagValue(t, flags, FlagReadinessWindowSeconds, DefaultConfig().Node.ReadinessWindowSeconds) + assertFlagValue(t, flags, FlagReadinessMaxBlocksBehind, DefaultConfig().Node.ReadinessMaxBlocksBehind) + assertFlagValue(t, flags, FlagScrapeInterval, DefaultConfig().Node.ScrapeInterval) + + // DA flags + assertFlagValue(t, flags, FlagDAAddress, DefaultConfig().DA.Address) + assertFlagValue(t, flags, FlagDAAuthToken, DefaultConfig().DA.AuthToken) + assertFlagValue(t, flags, FlagDABlockTime, DefaultConfig().DA.BlockTime.Duration) + assertFlagValue(t, flags, FlagDANamespace, DefaultConfig().DA.Namespace) + assertFlagValue(t, flags, FlagDADataNamespace, DefaultConfig().DA.DataNamespace) + assertFlagValue(t, flags, FlagDAForcedInclusionNamespace, DefaultConfig().DA.ForcedInclusionNamespace) + assertFlagValue(t, flags, FlagDASubmitOptions, DefaultConfig().DA.SubmitOptions) + assertFlagValue(t, flags, FlagDASigningAddresses, DefaultConfig().DA.SigningAddresses) + assertFlagValue(t, flags, FlagDAMempoolTTL, DefaultConfig().DA.MempoolTTL) + assertFlagValue(t, flags, FlagDAMaxSubmitAttempts, DefaultConfig().DA.MaxSubmitAttempts) + assertFlagValue(t, flags, FlagDARequestTimeout, DefaultConfig().DA.RequestTimeout.Duration) + + // P2P flags + assertFlagValue(t, flags, FlagP2PListenAddress, DefaultConfig().P2P.ListenAddress) + assertFlagValue(t, flags, FlagP2PPeers, DefaultConfig().P2P.Peers) + assertFlagValue(t, flags, FlagP2PBlockedPeers, DefaultConfig().P2P.BlockedPeers) + assertFlagValue(t, flags, FlagP2PAllowedPeers, DefaultConfig().P2P.AllowedPeers) + assertFlagValue(t, flags, FlagP2PDisableConnectionGater, DefaultConfig().P2P.DisableConnectionGater) + + // Instrumentation flags + instrDef := DefaultInstrumentationConfig() + assertFlagValue(t, flags, FlagPrometheus, instrDef.Prometheus) + assertFlagValue(t, flags, FlagPrometheusListenAddr, instrDef.PrometheusListenAddr) + assertFlagValue(t, flags, FlagMaxOpenConnections, instrDef.MaxOpenConnections) + assertFlagValue(t, flags, FlagPprof, instrDef.Pprof) + assertFlagValue(t, flags, FlagPprofListenAddr, instrDef.PprofListenAddr) + assertFlagValue(t, flags, FlagTracing, instrDef.Tracing) + assertFlagValue(t, flags, FlagTracingEndpoint, instrDef.TracingEndpoint) + assertFlagValue(t, flags, FlagTracingSampleRate, instrDef.TracingSampleRate) + assertFlagValue(t, flags, FlagTracingServiceName, instrDef.TracingServiceName) + + // Logging flags (in persistent flags) + assertFlagValue(t, persistentFlags, FlagLogLevel, DefaultConfig().Log.Level) + assertFlagValue(t, persistentFlags, FlagLogFormat, "text") + assertFlagValue(t, persistentFlags, FlagLogTrace, false) + assertFlagValue(t, persistentFlags, FlagRootDir, DefaultRootDirWithName("test")) + + // Signer flags + assertFlagValue(t, flags, FlagSignerPassphraseFile, "") + assertFlagValue(t, flags, FlagSignerType, "file") + assertFlagValue(t, flags, FlagSignerPath, DefaultConfig().Signer.SignerPath) + assertFlagValue(t, flags, FlagSignerKmsProvider, DefaultConfig().Signer.KMS.Provider) + assertFlagValue(t, flags, FlagSignerKmsAwsKeyID, DefaultConfig().Signer.KMS.AWS.KeyID) + assertFlagValue(t, flags, FlagSignerKmsAwsRegion, DefaultConfig().Signer.KMS.AWS.Region) + assertFlagValue(t, flags, FlagSignerKmsAwsProfile, DefaultConfig().Signer.KMS.AWS.Profile) + assertFlagValue(t, flags, FlagSignerKmsAwsTimeout, DefaultConfig().Signer.KMS.AWS.Timeout.Duration) + assertFlagValue(t, flags, FlagSignerKmsAwsMaxRetries, DefaultConfig().Signer.KMS.AWS.MaxRetries) + assertFlagValue(t, flags, FlagSignerKmsGcpKeyName, DefaultConfig().Signer.KMS.GCP.KeyName) + assertFlagValue(t, flags, FlagSignerKmsGcpCredentialsFile, DefaultConfig().Signer.KMS.GCP.CredentialsFile) + assertFlagValue(t, flags, FlagSignerKmsGcpTimeout, DefaultConfig().Signer.KMS.GCP.Timeout.Duration) + assertFlagValue(t, flags, FlagSignerKmsGcpMaxRetries, DefaultConfig().Signer.KMS.GCP.MaxRetries) + + // RPC flags + assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address) + assertFlagValue(t, flags, FlagRPCEnableDAVisualization, DefaultConfig().RPC.EnableDAVisualization) + + // Raft flags + assertFlagValue(t, flags, FlagRaftEnable, DefaultConfig().Raft.Enable) + assertFlagValue(t, flags, FlagRaftNodeID, DefaultConfig().Raft.NodeID) + assertFlagValue(t, flags, FlagRaftAddr, DefaultConfig().Raft.RaftAddr) + assertFlagValue(t, flags, FlagRaftDir, DefaultConfig().Raft.RaftDir) + assertFlagValue(t, flags, FlagRaftBootstrap, DefaultConfig().Raft.Bootstrap) + assertFlagValue(t, flags, FlagRaftPeers, DefaultConfig().Raft.Peers) + assertFlagValue(t, flags, FlagRaftSnapCount, DefaultConfig().Raft.SnapCount) + assertFlagValue(t, flags, FlagRaftSendTimeout, DefaultConfig().Raft.SendTimeout) + assertFlagValue(t, flags, FlagRaftHeartbeatTimeout, DefaultConfig().Raft.HeartbeatTimeout) + assertFlagValue(t, flags, FlagRaftLeaderLeaseTimeout, DefaultConfig().Raft.LeaderLeaseTimeout) + assertFlagValue(t, flags, FlagRaftElectionTimeout, DefaultConfig().Raft.ElectionTimeout) + assertFlagValue(t, flags, FlagRaftSnapshotThreshold, DefaultConfig().Raft.SnapshotThreshold) + assertFlagValue(t, flags, FlagRaftTrailingLogs, DefaultConfig().Raft.TrailingLogs) + + // Pruning flags + assertFlagValue(t, flags, FlagPruningMode, DefaultConfig().Pruning.Mode) + assertFlagValue(t, flags, FlagPruningKeepRecent, DefaultConfig().Pruning.KeepRecent) + assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) + + // Count the number of flags we're explicitly checking + expectedFlagCount := 82 // Update this number if you add more flag checks above + + // Get the actual number of flags (both regular and persistent) + actualFlagCount := 0 + flags.VisitAll(func(flag *pflag.Flag) { + actualFlagCount++ + }) + persistentFlags.VisitAll(func(flag *pflag.Flag) { + actualFlagCount++ + }) - // Get both persistent and regular flags - flags := cmd.Flags() - persistentFlags := cmd.PersistentFlags() - - // Test specific flags - assertFlagValue(t, flags, FlagDBPath, DefaultConfig().DBPath) - assertFlagValue(t, flags, FlagClearCache, DefaultConfig().ClearCache) - - // Node flags - assertFlagValue(t, flags, FlagAggregator, DefaultConfig().Node.Aggregator) - assertFlagValue(t, flags, FlagBasedSequencer, DefaultConfig().Node.BasedSequencer) - assertFlagValue(t, flags, FlagLight, DefaultConfig().Node.Light) - assertFlagValue(t, flags, FlagBlockTime, DefaultConfig().Node.BlockTime.Duration) - assertFlagValue(t, flags, FlagLazyAggregator, DefaultConfig().Node.LazyMode) - assertFlagValue(t, flags, FlagMaxPendingHeadersAndData, DefaultConfig().Node.MaxPendingHeadersAndData) - assertFlagValue(t, flags, FlagLazyBlockTime, DefaultConfig().Node.LazyBlockInterval.Duration) - assertFlagValue(t, flags, FlagReadinessWindowSeconds, DefaultConfig().Node.ReadinessWindowSeconds) - assertFlagValue(t, flags, FlagReadinessMaxBlocksBehind, DefaultConfig().Node.ReadinessMaxBlocksBehind) - assertFlagValue(t, flags, FlagScrapeInterval, DefaultConfig().Node.ScrapeInterval) - - // DA flags - assertFlagValue(t, flags, FlagDAAddress, DefaultConfig().DA.Address) - assertFlagValue(t, flags, FlagDAAuthToken, DefaultConfig().DA.AuthToken) - assertFlagValue(t, flags, FlagDABlockTime, DefaultConfig().DA.BlockTime.Duration) - assertFlagValue(t, flags, FlagDANamespace, DefaultConfig().DA.Namespace) - assertFlagValue(t, flags, FlagDADataNamespace, DefaultConfig().DA.DataNamespace) - assertFlagValue(t, flags, FlagDAForcedInclusionNamespace, DefaultConfig().DA.ForcedInclusionNamespace) - assertFlagValue(t, flags, FlagDASubmitOptions, DefaultConfig().DA.SubmitOptions) - assertFlagValue(t, flags, FlagDASigningAddresses, DefaultConfig().DA.SigningAddresses) - assertFlagValue(t, flags, FlagDAMempoolTTL, DefaultConfig().DA.MempoolTTL) - assertFlagValue(t, flags, FlagDAMaxSubmitAttempts, DefaultConfig().DA.MaxSubmitAttempts) - assertFlagValue(t, flags, FlagDARequestTimeout, DefaultConfig().DA.RequestTimeout.Duration) - - // P2P flags - assertFlagValue(t, flags, FlagP2PListenAddress, DefaultConfig().P2P.ListenAddress) - assertFlagValue(t, flags, FlagP2PPeers, DefaultConfig().P2P.Peers) - assertFlagValue(t, flags, FlagP2PBlockedPeers, DefaultConfig().P2P.BlockedPeers) - assertFlagValue(t, flags, FlagP2PAllowedPeers, DefaultConfig().P2P.AllowedPeers) - assertFlagValue(t, flags, FlagP2PDisableConnectionGater, DefaultConfig().P2P.DisableConnectionGater) - - // Instrumentation flags - instrDef := DefaultInstrumentationConfig() - assertFlagValue(t, flags, FlagPrometheus, instrDef.Prometheus) - assertFlagValue(t, flags, FlagPrometheusListenAddr, instrDef.PrometheusListenAddr) - assertFlagValue(t, flags, FlagMaxOpenConnections, instrDef.MaxOpenConnections) - assertFlagValue(t, flags, FlagPprof, instrDef.Pprof) - assertFlagValue(t, flags, FlagPprofListenAddr, instrDef.PprofListenAddr) - assertFlagValue(t, flags, FlagTracing, instrDef.Tracing) - assertFlagValue(t, flags, FlagTracingEndpoint, instrDef.TracingEndpoint) - assertFlagValue(t, flags, FlagTracingSampleRate, instrDef.TracingSampleRate) - assertFlagValue(t, flags, FlagTracingServiceName, instrDef.TracingServiceName) - - // Logging flags (in persistent flags) - assertFlagValue(t, persistentFlags, FlagLogLevel, DefaultConfig().Log.Level) - assertFlagValue(t, persistentFlags, FlagLogFormat, "text") - assertFlagValue(t, persistentFlags, FlagLogTrace, false) - assertFlagValue(t, persistentFlags, FlagRootDir, DefaultRootDirWithName("test")) - - // Signer flags - assertFlagValue(t, flags, FlagSignerPassphraseFile, "") - assertFlagValue(t, flags, FlagSignerType, "file") - assertFlagValue(t, flags, FlagSignerPath, DefaultConfig().Signer.SignerPath) - assertFlagValue(t, flags, FlagSignerKmsProvider, DefaultConfig().Signer.KMS.Provider) - assertFlagValue(t, flags, FlagSignerKmsAwsKeyID, DefaultConfig().Signer.KMS.AWS.KeyID) - assertFlagValue(t, flags, FlagSignerKmsAwsRegion, DefaultConfig().Signer.KMS.AWS.Region) - assertFlagValue(t, flags, FlagSignerKmsAwsProfile, DefaultConfig().Signer.KMS.AWS.Profile) - assertFlagValue(t, flags, FlagSignerKmsAwsTimeout, DefaultConfig().Signer.KMS.AWS.Timeout.Duration) - assertFlagValue(t, flags, FlagSignerKmsAwsMaxRetries, DefaultConfig().Signer.KMS.AWS.MaxRetries) - assertFlagValue(t, flags, FlagSignerKmsGcpKeyName, DefaultConfig().Signer.KMS.GCP.KeyName) - assertFlagValue(t, flags, FlagSignerKmsGcpCredentialsFile, DefaultConfig().Signer.KMS.GCP.CredentialsFile) - assertFlagValue(t, flags, FlagSignerKmsGcpTimeout, DefaultConfig().Signer.KMS.GCP.Timeout.Duration) - assertFlagValue(t, flags, FlagSignerKmsGcpMaxRetries, DefaultConfig().Signer.KMS.GCP.MaxRetries) - - // RPC flags - assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address) - assertFlagValue(t, flags, FlagRPCEnableDAVisualization, DefaultConfig().RPC.EnableDAVisualization) - - // Raft flags - assertFlagValue(t, flags, FlagRaftEnable, DefaultConfig().Raft.Enable) - assertFlagValue(t, flags, FlagRaftNodeID, DefaultConfig().Raft.NodeID) - assertFlagValue(t, flags, FlagRaftAddr, DefaultConfig().Raft.RaftAddr) - assertFlagValue(t, flags, FlagRaftDir, DefaultConfig().Raft.RaftDir) - assertFlagValue(t, flags, FlagRaftBootstrap, DefaultConfig().Raft.Bootstrap) - assertFlagValue(t, flags, FlagRaftPeers, DefaultConfig().Raft.Peers) - assertFlagValue(t, flags, FlagRaftSnapCount, DefaultConfig().Raft.SnapCount) - assertFlagValue(t, flags, FlagRaftSendTimeout, DefaultConfig().Raft.SendTimeout) - assertFlagValue(t, flags, FlagRaftHeartbeatTimeout, DefaultConfig().Raft.HeartbeatTimeout) - assertFlagValue(t, flags, FlagRaftLeaderLeaseTimeout, DefaultConfig().Raft.LeaderLeaseTimeout) - assertFlagValue(t, flags, FlagRaftElectionTimeout, DefaultConfig().Raft.ElectionTimeout) - assertFlagValue(t, flags, FlagRaftSnapshotThreshold, DefaultConfig().Raft.SnapshotThreshold) - assertFlagValue(t, flags, FlagRaftTrailingLogs, DefaultConfig().Raft.TrailingLogs) - - // Pruning flags - assertFlagValue(t, flags, FlagPruningMode, DefaultConfig().Pruning.Mode) - assertFlagValue(t, flags, FlagPruningKeepRecent, DefaultConfig().Pruning.KeepRecent) - assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) - - // Count the number of flags we're explicitly checking - expectedFlagCount := 82 // Update this number if you add more flag checks above - - // Get the actual number of flags (both regular and persistent) - actualFlagCount := 0 - flags.VisitAll(func(flag *pflag.Flag) { - actualFlagCount++ + // Verify that the counts match + assert.Equal( + t, + expectedFlagCount, + actualFlagCount, + "Number of flags doesn't match. If you added a new flag, please update the test.", + ) }) - persistentFlags.VisitAll(func(flag *pflag.Flag) { - actualFlagCount++ - }) - - // Verify that the counts match - assert.Equal( - t, - expectedFlagCount, - actualFlagCount, - "Number of flags doesn't match. If you added a new flag, please update the test.", - ) } func TestLoad(t *testing.T) { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 5ea895a48f..233585e0c5 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -133,13 +133,10 @@ func DefaultConfig() Config { } } -// nowUnix returns the current Unix timestamp; package-level so tests can stub it. -var nowUnix = func() int64 { return time.Now().Unix() } - func randString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" result := make([]byte, length) - rng := rand.New(rand.NewSource(nowUnix())) //nolint:gosec // even half random is good enough here. + rng := rand.New(rand.NewSource(time.Now().Unix())) //nolint:gosec // even half random is good enough here. for i := range result { result[i] = charset[rng.Intn(len(charset))] }