Fix every finding surfaced by the canonical .golangci.yml with golangci-lint v2.12.2 (refs #61), behavior-preserving throughout: - err113: dynamic errors replaced with package-level sentinels and %w wrapping; direct comparisons converted to errors.Is - goprintffuncname: printf-style helpers renamed with an f suffix (ui.Writer message methods, cli.ReportErrorf, database.Fatalf, vaultik stdoutf) and all call sites updated - revive: stuttering type names renamed (blob.Handler, blob.WithReader, blob.ChunkPosition, storage.URL, storage.Info), doc comments added, unused parameters blanked, package comments added - contextcheck/noctx: ctx threaded through blob.Packer (AddChunk/Flush/FinalizeBlob/PackChunks) and scanner call sites; context-aware exec and sql variants used - funlen/cyclop/gocognit/nestif/dupl: oversized or duplicated functions split into focused helpers across production and test code - paralleltest/tparallel/thelper/usetesting/testpackage: tests parallelized where safe (global log.Initialize kept in the serial phase), helpers marked, t.TempDir adopted, external test packages where only exported API is used - gosec: integer conversions clamped or justified, header timeouts added, remaining findings suppressed with per-site justifications - mnd/goconst/lll/wsl_v5/nlreturn/noinlineerr/errcheck and other mechanical findings fixed directly Remove the deprecated log.LogOptions alias (callers migrated to log.Options). make check is green.
166 lines
4.2 KiB
Go
166 lines
4.2 KiB
Go
package config //nolint:testpackage // exercises unexported extractAgeSecretKey
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
const (
|
|
testSneakAgePublicKey = "age1278m9q7dp3chsh2dcy82qk27v047zywyvt" +
|
|
"xwnj4cvt0z65jw6a7q5dqhfj"
|
|
testIntegrationAgePublicKey = "age1ezrjmfpwsc95svdg0y54mums3zevgzu" +
|
|
"0x0ecq2f7tp8a05gl0sjq9q9wjg"
|
|
testIntegrationAgePrivateKey = "AGE-SECRET-KEY-19CR5YSFW59HM4TLD6GX" +
|
|
"VEDMZFTVVF7PPHKUT68TXSFPK7APHXA2QS2NJA5"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Set up test environment
|
|
testConfigPath := filepath.Join("..", "..", "test", "config.yaml")
|
|
|
|
absPath, err := filepath.Abs(testConfigPath)
|
|
if err == nil {
|
|
_ = os.Setenv("VAULTIK_CONFIG", absPath)
|
|
}
|
|
|
|
code := m.Run()
|
|
os.Exit(code)
|
|
}
|
|
|
|
// TestConfigLoad ensures the config package can be imported and basic
|
|
// functionality works.
|
|
func TestConfigLoad(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Use the test config file
|
|
configPath := os.Getenv("VAULTIK_CONFIG")
|
|
if configPath == "" {
|
|
t.Fatal("VAULTIK_CONFIG environment variable not set")
|
|
}
|
|
|
|
// Test loading the config
|
|
cfg, err := Load(configPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
// Basic validation
|
|
if len(cfg.AgeRecipients) != 2 {
|
|
t.Errorf("Expected 2 age recipients, got %d", len(cfg.AgeRecipients))
|
|
}
|
|
|
|
if cfg.AgeRecipients[0] != testSneakAgePublicKey {
|
|
t.Errorf("Expected first age recipient to be %s, got '%s'",
|
|
testSneakAgePublicKey, cfg.AgeRecipients[0])
|
|
}
|
|
|
|
if len(cfg.Snapshots) != 1 {
|
|
t.Errorf("Expected 1 snapshot, got %d", len(cfg.Snapshots))
|
|
}
|
|
|
|
testSnap, ok := cfg.Snapshots["test"]
|
|
if !ok {
|
|
t.Fatal("Expected 'test' snapshot to exist")
|
|
}
|
|
|
|
if len(testSnap.Paths) != 2 {
|
|
t.Errorf("Expected 2 paths in test snapshot, got %d", len(testSnap.Paths))
|
|
}
|
|
|
|
if testSnap.Paths[0] != "/tmp/vaultik-test-source" {
|
|
t.Errorf("Expected first path to be '/tmp/vaultik-test-source', got '%s'",
|
|
testSnap.Paths[0])
|
|
}
|
|
|
|
if cfg.S3.Bucket != "vaultik-test-bucket" {
|
|
t.Errorf("Expected S3 bucket to be 'vaultik-test-bucket', got '%s'",
|
|
cfg.S3.Bucket)
|
|
}
|
|
|
|
if cfg.Hostname != "test-host" {
|
|
t.Errorf("Expected hostname to be 'test-host', got '%s'", cfg.Hostname)
|
|
}
|
|
}
|
|
|
|
// TestConfigFromEnv tests loading config path from environment variable
|
|
func TestConfigFromEnv(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
configPath := os.Getenv("VAULTIK_CONFIG")
|
|
if configPath == "" {
|
|
t.Skip("VAULTIK_CONFIG not set")
|
|
}
|
|
|
|
// Verify the file exists
|
|
//nolint:gosec // G703: test config path comes from the test environment
|
|
_, err := os.Stat(configPath)
|
|
if os.IsNotExist(err) {
|
|
t.Errorf("Config file does not exist at path from VAULTIK_CONFIG: %s",
|
|
configPath)
|
|
}
|
|
}
|
|
|
|
// TestExtractAgeSecretKey tests extraction of AGE-SECRET-KEY from various inputs
|
|
func TestExtractAgeSecretKey(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "plain key",
|
|
input: testIntegrationAgePrivateKey,
|
|
expected: testIntegrationAgePrivateKey,
|
|
},
|
|
{
|
|
name: "key with trailing newline",
|
|
input: testIntegrationAgePrivateKey + "\n",
|
|
expected: testIntegrationAgePrivateKey,
|
|
},
|
|
{
|
|
name: "full age-keygen output",
|
|
input: "# created: 2025-01-14T12:00:00Z\n" +
|
|
"# public key: " + testIntegrationAgePublicKey + "\n" +
|
|
testIntegrationAgePrivateKey + "\n",
|
|
expected: testIntegrationAgePrivateKey,
|
|
},
|
|
{
|
|
name: "age-keygen output with extra blank lines",
|
|
input: "# created: 2025-01-14T12:00:00Z\n" +
|
|
"# public key: " + testIntegrationAgePublicKey + "\n\n" +
|
|
testIntegrationAgePrivateKey + "\n\n",
|
|
expected: testIntegrationAgePrivateKey,
|
|
},
|
|
{
|
|
name: "key with leading whitespace",
|
|
input: " " + testIntegrationAgePrivateKey + " ",
|
|
expected: testIntegrationAgePrivateKey,
|
|
},
|
|
{
|
|
name: "empty input",
|
|
input: "",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "only comments",
|
|
input: "# this is a comment\n# another comment",
|
|
expected: "# this is a comment\n# another comment",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := extractAgeSecretKey(tt.input)
|
|
if result != tt.expected {
|
|
t.Errorf("extractAgeSecretKey(%q) = %q, want %q",
|
|
tt.input, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|