diff --git a/database/console/migration/migrate_rollback_command.go b/database/console/migration/migrate_rollback_command.go index f875069c6..40526eabe 100644 --- a/database/console/migration/migrate_rollback_command.go +++ b/database/console/migration/migrate_rollback_command.go @@ -65,11 +65,6 @@ func (r *MigrateRollbackCommand) Handle(ctx console.Context) error { return nil } - // Set defaults if neither option is provided - if step == 0 && batch == 0 { - batch = 1 - } - if err := r.migrator.Rollback(step, batch); err != nil { ctx.Error(errors.MigrationMigrateFailed.Args(err).Error()) return nil diff --git a/database/console/migration/migrate_rollback_command_test.go b/database/console/migration/migrate_rollback_command_test.go index 5d5da9ce1..b0e8f9806 100644 --- a/database/console/migration/migrate_rollback_command_test.go +++ b/database/console/migration/migrate_rollback_command_test.go @@ -30,7 +30,7 @@ func TestMigrateRollbackCommand(t *testing.T) { setup: func() { mockContext.EXPECT().OptionInt("step").Return(0).Once() mockContext.EXPECT().OptionInt("batch").Return(0).Once() - mockMigrator.EXPECT().Rollback(0, 1).Return(nil).Once() + mockMigrator.EXPECT().Rollback(0, 0).Return(nil).Once() mockContext.EXPECT().Success("Migration rollback success").Once() }, }, @@ -81,7 +81,7 @@ func TestMigrateRollbackCommand(t *testing.T) { setup: func() { mockContext.EXPECT().OptionInt("step").Return(0).Once() mockContext.EXPECT().OptionInt("batch").Return(0).Once() - mockMigrator.EXPECT().Rollback(0, 1).Return(assert.AnError).Once() + mockMigrator.EXPECT().Rollback(0, 0).Return(assert.AnError).Once() mockContext.EXPECT().Error(errors.MigrationMigrateFailed.Args(assert.AnError).Error()).Once() }, }, diff --git a/foundation/application.go b/foundation/application.go index 81a760b31..1cb8e19c8 100644 --- a/foundation/application.go +++ b/foundation/application.go @@ -259,16 +259,19 @@ func (r *Application) Start() { log.Errorf("failed to run %s: %v\n", runner.signature, err) } - runner.doneOnce.Do(func() { - r.runnerWg.Done() - }) r.cancel() } // Run may be a blocking call, so don't write anything after it. }() go func() { + defer runner.doneOnce.Do(func() { + r.runnerWg.Done() + }) + <-r.ctx.Done() + + // Only call Shutdown if the runner is still running (Run didn't error) if !runner.running.Load() { return } @@ -278,10 +281,8 @@ func (r *Application) Start() { log.Errorf("failed to shutdown %s: %v\n", runner.signature, err) } } + runner.running.Store(false) - runner.doneOnce.Do(func() { - r.runnerWg.Done() - }) }() } diff --git a/foundation/application_test.go b/foundation/application_test.go index 5b28b65df..5ace73ac0 100644 --- a/foundation/application_test.go +++ b/foundation/application_test.go @@ -3,10 +3,8 @@ package foundation import ( "context" "os" - "os/signal" "path/filepath" "sync" - "syscall" "testing" "time" @@ -38,7 +36,7 @@ func TestApplicationTestSuite(t *testing.T) { } func (s *ApplicationTestSuite) SetupTest() { - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + ctx, cancel := context.WithCancel(context.Background()) s.app = &Application{ Container: NewContainer(), @@ -851,7 +849,10 @@ func (s *ApplicationTestSuite) TestStart() { runner2 := mocksfoundation.NewRunner(s.T()) runner2.EXPECT().Signature().Return("test-runner-2").Once() runner2.EXPECT().ShouldRun().Return(true).Once() - runner2.EXPECT().Run().Return(assert.AnError).Once() + runner2.EXPECT().Run().RunAndReturn(func() error { + time.Sleep(1 * time.Second) + return assert.AnError + }).Once() return []foundation.Runner{runner1, runner2} }, @@ -915,15 +916,16 @@ func (s *ApplicationTestSuite) TestStart() { select { case <-panicChan: // Panic occurred as expected - case <-time.After(1 * time.Second): + case <-time.After(5 * time.Second): s.Fail("expected panic but none occurred") } } else { // Only trigger cancel for non-panic cases // For panic cases, the error handling will call cancel automatically + cancel := s.cancel go func() { time.Sleep(100 * time.Millisecond) // Wait for goroutines to start - s.cancel() + cancel() }() s.NotPanics(func() { @@ -989,6 +991,7 @@ func (s *ApplicationTestSuite) TestShutdown() { s.Run(tt.name, func() { s.SetupTest() runners := tt.setup() + var err error if len(runners) > 0 { serviceProvider := mocksfoundation.NewServiceProviderWithRunners(s.T()) @@ -1005,14 +1008,24 @@ func (s *ApplicationTestSuite) TestShutdown() { s.app.configureRunners() // Start runners in the background - go s.app.Start() + startDone := make(chan struct{}) + go func() { + defer close(startDone) + s.app.Start() + }() // Wait a moment for runners to start time.Sleep(50 * time.Millisecond) - } - // Shutdown the application - err := s.app.Shutdown() + // Shutdown the application + err = s.app.Shutdown() + + // Wait for Start() to complete before mock cleanup + <-startDone + } else { + // Shutdown the application + err = s.app.Shutdown() + } if tt.expectError { s.Error(err) diff --git a/go.mod b/go.mod index c35a06618..490a2ab56 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/urfave/cli/v3 v3.6.2 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 go.opentelemetry.io/contrib/propagators/b3 v1.39.0 - go.opentelemetry.io/otel v1.39.0 + go.opentelemetry.io/otel v1.40.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.15.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.15.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 @@ -44,11 +44,11 @@ require ( go.opentelemetry.io/otel/exporters/zipkin v1.39.0 go.opentelemetry.io/otel/log v0.15.0 go.opentelemetry.io/otel/log/logtest v0.15.0 - go.opentelemetry.io/otel/metric v1.39.0 - go.opentelemetry.io/otel/sdk v1.39.0 + go.opentelemetry.io/otel/metric v1.40.0 + go.opentelemetry.io/otel/sdk v1.40.0 go.opentelemetry.io/otel/sdk/log v0.15.0 - go.opentelemetry.io/otel/sdk/metric v1.39.0 - go.opentelemetry.io/otel/trace v1.39.0 + go.opentelemetry.io/otel/sdk/metric v1.40.0 + go.opentelemetry.io/otel/trace v1.40.0 golang.org/x/crypto v0.47.0 golang.org/x/exp v0.0.0-20260112195511-716be5621a96 google.golang.org/grpc v1.78.0 diff --git a/go.sum b/go.sum index bc4097879..a9074d7a1 100644 --- a/go.sum +++ b/go.sum @@ -276,8 +276,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= go.opentelemetry.io/contrib/propagators/b3 v1.39.0 h1:PI7pt9pkSnimWcp5sQhUA9OzLbc3Ba4sL+VEUTNsxrk= go.opentelemetry.io/contrib/propagators/b3 v1.39.0/go.mod h1:5gV/EzPnfYIwjzj+6y8tbGW2PKWhcsz5e/7twptRVQY= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.15.0 h1:W+m0g+/6v3pa5PgVf2xoFMi5YtNR06WtS7ve5pcvLtM= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.15.0/go.mod h1:JM31r0GGZ/GU94mX8hN4D8v6e40aFlUECSQ48HaLgHM= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.15.0 h1:EKpiGphOYq3CYnIe2eX9ftUkyU+Y8Dtte8OaWyHJ4+I= @@ -304,18 +304,18 @@ go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypk go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= go.opentelemetry.io/otel/log/logtest v0.15.0 h1:porNFuxAjodl6LhePevOc3n7bo3Wi3JhGXNWe7KP8iU= go.opentelemetry.io/otel/log/logtest v0.15.0/go.mod h1:c8epqBXGHgS1LiNgmD+LuNYK9lSS3mqvtMdxLsfJgLg= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/log/writer_test.go b/log/writer_test.go index a6a1db50a..cd4761efd 100644 --- a/log/writer_test.go +++ b/log/writer_test.go @@ -769,15 +769,15 @@ func (h *CustomHandler) Handle(entry contractslog.Entry) error { var builder strings.Builder message := entry.Message() if len(message) > 0 { - builder.WriteString(fmt.Sprintf("%s: %v\n", entry.Level().String(), message)) + _, _ = fmt.Fprintf(&builder, "%s: %v\n", entry.Level().String(), message) } if len(code) > 0 { - builder.WriteString(fmt.Sprintf("custom_code: %v\n", code)) + _, _ = fmt.Fprintf(&builder, "custom_code: %v\n", code) } if user != nil { - builder.WriteString(fmt.Sprintf("custom_user: %v\n", user)) + _, _ = fmt.Fprintf(&builder, "custom_user: %v\n", user) } err := file.PutContent(filename, builder.String())