diff --git a/service/runway/server/BUILD.bazel b/service/runway/server/BUILD.bazel index 113cd6e23..97ba9bc1e 100644 --- a/service/runway/server/BUILD.bazel +++ b/service/runway/server/BUILD.bazel @@ -79,6 +79,7 @@ go_test( srcs = [ "checkout_test.go", "config_test.go", + "main_test.go", ], # Checkout provisioning runs real git, so the test uses the same pinned # runtime the merger does rather than whatever git the host happens to have. @@ -101,6 +102,8 @@ go_test( "//runway/extension/merger/git:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", "@com_github_stretchr_testify//require:go_default_library", + "@org_uber_go_zap//:go_default_library", "@org_uber_go_zap//zaptest:go_default_library", + "@org_uber_go_zap//zaptest/observer:go_default_library", ], ) diff --git a/service/runway/server/main.go b/service/runway/server/main.go index dc9985001..f95a9bf97 100644 --- a/service/runway/server/main.go +++ b/service/runway/server/main.go @@ -146,7 +146,7 @@ func run() error { } defer mysqlQueue.Close() - logger.Info("initialized queue", zap.String("dsn", queueDSN)) + logQueueInitialized(logger) subscriberName := os.Getenv("HOSTNAME") if subscriberName == "" { @@ -305,6 +305,12 @@ func run() error { return err } +const queueBackendMySQL = "mysql" + +func logQueueInitialized(logger *zap.Logger) { + logger.Info("initialized queue", zap.String("backend", queueBackendMySQL)) +} + // newMergerFactory builds the mergers for the server. // // MERGER pins every queue to one implementation explicitly, which is how a test diff --git a/service/runway/server/main_test.go b/service/runway/server/main_test.go new file mode 100644 index 000000000..df7b386a7 --- /dev/null +++ b/service/runway/server/main_test.go @@ -0,0 +1,80 @@ +// Copyright (c) 2026 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +const sentinelQueueDSN = "sentinel-user:super-secret@tcp(mysql:3306)/submitqueue?tls=sentinel-query-secret" + +type queueInitializationLogFixture struct { + logger *zap.Logger + logs *observer.ObservedLogs +} + +func newQueueInitializationLogFixture(t *testing.T) *queueInitializationLogFixture { + t.Helper() + + core, logs := observer.New(zap.InfoLevel) + return &queueInitializationLogFixture{ + logger: zap.New(core), + logs: logs, + } +} + +func TestLogQueueInitialized(t *testing.T) { + tests := []struct { + name string + queueDSN string + forbiddenSecrets []string + }{ + { + name: "omits credentials and retains backend", + queueDSN: sentinelQueueDSN, + forbiddenSecrets: []string{ + "sentinel-user", + "super-secret", + "sentinel-query-secret", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("QUEUE_MYSQL_DSN", tt.queueDSN) + fixture := newQueueInitializationLogFixture(t) + + logQueueInitialized(fixture.logger) + + entries := fixture.logs.All() + require.Len(t, entries, 1) + assert.Equal(t, "initialized queue", entries[0].Message) + assert.Equal(t, queueBackendMySQL, entries[0].ContextMap()["backend"]) + assert.NotContains(t, entries[0].ContextMap(), "dsn") + + renderedEntry := fmt.Sprintf("%s %v", entries[0].Message, entries[0].ContextMap()) + for _, secret := range tt.forbiddenSecrets { + assert.NotContains(t, renderedEntry, secret) + } + }) + } +}