From b523a21fde4c04207dc53c451c6bfe92321a60af Mon Sep 17 00:00:00 2001 From: Leif Johansson Date: Fri, 4 Sep 2026 22:37:43 +0200 Subject: [PATCH] Add the ETSI config test that .gitignore swallowed #159 described a test asserting every ETSI field is carried into the registry config. The test was written and passing locally, and it is not in the repository: .gitignore's third line was an unanchored `gt`, which matches the cmd/gt *directory* as well as the built binary, so `git add -A` skipped the new file without a word and the commit looked complete. Anchors those patterns to the repository root so cmd/gt is tracked normally, and adds the missing test. Reverting #159's mapping fails it on all five fields it restored. The same shape as the bug #159 fixed, one level up: a dropped thing produces silence rather than an error, so nothing looks wrong until something downstream is missing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DWj5aAxqWjEm5ESCX4P1hk --- .gitignore | 11 +++-- cmd/gt/etsi_config_test.go | 94 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 cmd/gt/etsi_config_test.go diff --git a/.gitignore b/.gitignore index 39581b2..13955c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ # Binaries for programs and plugins -output -gt -gt-test -go-trust +# Anchored to the repository root: an unanchored "gt" also matches the cmd/gt +# *directory*, which silently excluded every new file under it - a test added +# there was ignored by `git add -A` without a word, and the commit looked fine. +/output +/gt +/gt-test +/go-trust *.exe *.exe~ *.dll diff --git a/cmd/gt/etsi_config_test.go b/cmd/gt/etsi_config_test.go new file mode 100644 index 0000000..a57e4c5 --- /dev/null +++ b/cmd/gt/etsi_config_test.go @@ -0,0 +1,94 @@ +package main + +import ( + "testing" + "time" + + "github.com/sirosfoundation/go-trust/pkg/config" +) + +// TestETSITSLConfigCarriesEveryConfiguredField is the test whose absence let +// six fields be dropped silently. +// +// The failure mode is what makes it worth pinning: a dropped field does not +// produce a wrong value, it produces the zero value, and the registry then +// fails with "no trust data loaded - configure CertBundle, TSLFiles, or +// TSLURLs" while the operator is looking at a config file that plainly +// configures TSLURLs. +func TestETSITSLConfigCarriesEveryConfiguredField(t *testing.T) { + cfg := &config.ETSIRegistryConfig{ + Enabled: true, + Name: "wrpac-tsl", + Description: "test anchors", + CertBundle: "/etc/trust/bundle.pem", + TSLFiles: []string{"/etc/trust/local.xml"}, + TSLURLs: []string{"https://registrar.example.org/tsl.xml"}, + FollowRefs: true, + MaxRefDepth: 4, + AllowNetworkAccess: true, + FetchTimeout: "17s", + UserAgent: "test-agent/1.0", + LOTLSignerBundle: "/etc/trust/lotl-signers.pem", + RequireSignature: true, + FollowPivots: true, + } + + got := etsiTSLConfig(cfg, nil, nil) + + if len(got.TSLURLs) != 1 || got.TSLURLs[0] != cfg.TSLURLs[0] { + t.Errorf("TSLURLs = %v, want %v", got.TSLURLs, cfg.TSLURLs) + } + if len(got.TSLFiles) != 1 || got.TSLFiles[0] != cfg.TSLFiles[0] { + t.Errorf("TSLFiles = %v, want %v", got.TSLFiles, cfg.TSLFiles) + } + if got.CertBundle != cfg.CertBundle { + t.Errorf("CertBundle = %q, want %q", got.CertBundle, cfg.CertBundle) + } + if !got.FollowRefs { + t.Error("FollowRefs was not carried across") + } + if got.MaxRefDepth != cfg.MaxRefDepth { + t.Errorf("MaxRefDepth = %d, want %d", got.MaxRefDepth, cfg.MaxRefDepth) + } + if !got.AllowNetworkAccess { + // Without this a URL source is refused even when one is configured. + t.Error("AllowNetworkAccess was not carried across") + } + if got.FetchTimeout != 17*time.Second { + t.Errorf("FetchTimeout = %v, want 17s", got.FetchTimeout) + } + if got.UserAgent != cfg.UserAgent { + t.Errorf("UserAgent = %q, want %q", got.UserAgent, cfg.UserAgent) + } + if got.LOTLSignerBundle != cfg.LOTLSignerBundle { + t.Errorf("LOTLSignerBundle = %q, want %q", got.LOTLSignerBundle, cfg.LOTLSignerBundle) + } + if !got.RequireSignature { + t.Error("RequireSignature was not carried across") + } + if !got.FollowPivots { + t.Error("FollowPivots was not carried across") + } + if got.Name != cfg.Name || got.Description != cfg.Description { + t.Errorf("Name/Description = %q/%q, want %q/%q", got.Name, got.Description, cfg.Name, cfg.Description) + } +} + +func TestETSITSLConfigDefaultsNameAndDescription(t *testing.T) { + got := etsiTSLConfig(&config.ETSIRegistryConfig{Enabled: true}, nil, nil) + if got.Name != "ETSI-TSL" { + t.Errorf("Name = %q, want the ETSI-TSL default", got.Name) + } + if got.Description == "" { + t.Error("Description was left empty") + } +} + +func TestETSITSLConfigIgnoresAnUnparseableFetchTimeout(t *testing.T) { + // A bad duration must leave the default in place rather than propagate a + // zero timeout, which would be "no timeout" rather than "the default". + got := etsiTSLConfig(&config.ETSIRegistryConfig{Enabled: true, FetchTimeout: "soon"}, nil, nil) + if got.FetchTimeout != 0 { + t.Errorf("FetchTimeout = %v, want the zero value so the registry applies its own default", got.FetchTimeout) + } +}