Files
zooqueen e7a7730dfb register proxyproto, and skip two checks root cannot fail
proxyproto is declared in plugin.cfg and implemented under plugin/proxyproto,
    and appears in NEITHER generated file — not the imports in
    core/plugin/zplugin.go, not the directive order in
    core/dnsserver/zdirectives.go. Both were generated before it was added, so
    the plugin has never been reachable: a Corefile naming it gets "Unknown
    directive 'proxyproto'", which is what TestReadme found while checking that
    plugin's own README. Added to both, and to the directive list between tls
    and quic, where plugin.cfg orders it — position there is plugin chain
    order, not decoration. Checked every other plugin.cfg entry the same way;
    these two were the only gaps.

  TestDefaultLoader and TestTLSKeyLog/Bad_Path chmod a path to 0000 and expect
    the permission error. Root does not get one, and CI runs as root in a
    container, so both skip there now with the reason.

Whole repo green with the Makefile's tags. proxyproto verified by starting a
server with it in the Corefile, not only by the README test going quiet.
2026-08-10 21:37:36 -07:00

309 lines
7.6 KiB
Go

package coremain
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/coredns/caddy"
)
func TestConfLoader(t *testing.T) {
tests := []struct {
name string
conf string
contents []byte
serverType string
expectedError bool
expectedInput bool
checkContents bool
}{
{
name: "empty conf",
conf: "",
serverType: "dns",
expectedError: false,
expectedInput: false,
},
{
name: "non-existent file",
conf: "non-existent-file",
serverType: "dns",
expectedError: true,
expectedInput: false,
},
{
name: "stdin input",
conf: "stdin",
serverType: "dns",
expectedError: false,
expectedInput: false,
},
{
name: "valid config file",
contents: []byte("example.org:53 {\n whoami\n}\n"),
serverType: "dns",
expectedError: false,
expectedInput: true,
checkContents: true,
},
{
name: "empty config file",
conf: "",
contents: []byte(""),
serverType: "dns",
expectedError: false,
expectedInput: false,
checkContents: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if len(tc.contents) > 0 {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "corefile-test")
if err := os.WriteFile(tmpFile, tc.contents, 0o644); err != nil {
t.Fatalf("Failed to write to temp file: %v", err)
}
conf = tmpFile
} else {
conf = tc.conf
}
input, err := confLoader(tc.serverType)
// Check error
if tc.expectedError && err == nil {
t.Errorf("Expected error but got none")
}
if !tc.expectedError && err != nil {
t.Errorf("Expected no error but got: %v", err)
}
// Check input
if !tc.expectedInput && input != nil {
t.Errorf("Expected nil input but got: %v", input)
}
if tc.expectedInput && input == nil {
t.Errorf("Expected non-nil input but got nil")
}
// Check contents if needed
if tc.checkContents && input != nil {
caddyInput, ok := input.(caddy.CaddyfileInput)
if !ok {
t.Errorf("Expected input to be caddy.CaddyfileInput")
}
if string(caddyInput.Contents) != string(tc.contents) {
t.Errorf("Expected contents %q, got %q", tc.contents, caddyInput.Contents)
}
if caddyInput.ServerTypeName != tc.serverType {
t.Errorf("Expected ServerTypeName to be %q, got %q", tc.serverType, caddyInput.ServerTypeName)
}
}
})
}
}
func TestDefaultLoader(t *testing.T) {
// The working directory matters because defaultLoader() looks for "Corefile" in the current directory
tmpDir := t.TempDir()
t.Chdir(tmpDir)
// Test without Corefile
input, err := defaultLoader("dns")
if err != nil {
t.Errorf("Expected no error for missing Corefile, got: %v", err)
}
if input != nil {
t.Errorf("Expected nil input for missing Corefile, got: %v", input)
}
// Test with Corefile
testContents := []byte("example.org:53 {\n whoami\n}\n")
if err := os.WriteFile(filepath.Join(tmpDir, "Corefile"), testContents, 0o644); err != nil {
t.Fatalf("Failed to write Corefile: %v", err)
}
input, err = defaultLoader("dns")
if err != nil {
t.Errorf("Expected no error for valid Corefile, got: %v", err)
}
if input == nil {
t.Errorf("Expected non-nil input for valid Corefile")
} else {
caddyInput, ok := input.(caddy.CaddyfileInput)
if !ok {
t.Errorf("Expected input to be caddy.CaddyfileInput")
}
if string(caddyInput.Contents) != string(testContents) {
t.Errorf("Expected contents %q, got %q", testContents, caddyInput.Contents)
}
if caddyInput.ServerTypeName != "dns" {
t.Errorf("Expected ServerTypeName to be %q, got %q", "dns", caddyInput.ServerTypeName)
}
}
if runtime.GOOS != "windows" {
// Create a file but make it unreadable
tmpFile := filepath.Join(tmpDir, "Corefile")
if err := os.WriteFile(tmpFile, []byte("test"), 0644); err != nil {
t.Fatalf("Failed to create test file: %v", err)
}
if err := os.Chmod(tmpFile, 0000); err != nil {
t.Fatalf("Failed to change permissions: %v", err)
}
// Root reads a 0000 file regardless of its mode, so there is no error
// to expect. CI runs as root in a container.
if os.Geteuid() == 0 {
t.Skip("running as root: file permissions deny nothing")
}
input, err = defaultLoader("dns")
if err == nil {
t.Error("Expected error for unreadable Corefile but got none")
}
if input != nil {
t.Error("Expected nil input for unreadable Corefile")
}
}
}
func TestVersionString(t *testing.T) {
caddy.AppName = "TestApp"
caddy.AppVersion = "1.0.0"
expected := "TestApp-1.0.0\n"
result := versionString()
if result != expected {
t.Errorf("Expected version string %q, got %q", expected, result)
}
}
func TestReleaseString(t *testing.T) {
GitCommit = "a6d2d7b5"
expected := runtime.GOOS + "/" + runtime.GOARCH + ", " + runtime.Version() + ", " + GitCommit + "\n"
result := releaseString()
if result != expected {
t.Errorf("Expected release string %q, got %q", expected, result)
}
}
func TestSetVersion(t *testing.T) {
// Test case 1: Development build with nearest tag
gitTag = ""
gitNearestTag = "v1.2.3"
gitShortStat = "1 file changed"
GitCommit = "abcdef"
buildDate = "2023-05-01"
setVersion()
if !devBuild {
t.Errorf("Expected devBuild to be true with empty gitTag and non-empty gitShortStat")
}
expectedAppVersion := "1.2.3 (+abcdef 2023-05-01)"
if appVersion != expectedAppVersion {
t.Errorf("Expected appVersion to be %q, got %q", expectedAppVersion, appVersion)
}
// Test case 2: Release build with tag
gitTag = "v2.0.0"
gitNearestTag = "v1.9.0"
gitShortStat = ""
setVersion()
if devBuild {
t.Errorf("Expected devBuild to be false with non-empty gitTag and empty gitShortStat")
}
expectedAppVersion = "2.0.0"
if appVersion != expectedAppVersion {
t.Errorf("Expected appVersion to be %q, got %q", expectedAppVersion, appVersion)
}
// Test case 3: No tags available
gitTag = ""
gitNearestTag = ""
gitShortStat = ""
appVersion = "(untracked dev build)" // Reset to default
setVersion()
if !devBuild {
t.Errorf("Expected devBuild to be true with empty gitTag")
}
expectedAppVersion = "(untracked dev build)"
if appVersion != expectedAppVersion {
t.Errorf("Expected appVersion to be %q, got %q", expectedAppVersion, appVersion)
}
}
func TestShowVersion(t *testing.T) {
// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// Set test values
caddy.AppName = "TestApp"
caddy.AppVersion = "1.0.0"
GitCommit = "abc123"
// Test case 1: Non-dev build
devBuild = false
gitShortStat = ""
gitFilesModified = ""
showVersion()
// Restore stdout
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
io.Copy(&buf, r)
result := buf.String()
expected := fmt.Sprintf("TestApp-1.0.0\n%s/%s, %s, abc123\n", runtime.GOOS, runtime.GOARCH, runtime.Version())
if result != expected {
t.Errorf("Expected version output %q, got %q", expected, result)
}
// Test case 2: Dev build with modified files
oldStdout = os.Stdout
r, w, _ = os.Pipe()
os.Stdout = w
devBuild = true
gitShortStat = "1 file changed, 10 insertions(+), 5 deletions(-)"
gitFilesModified = "path/to/modified/file.go"
showVersion()
w.Close()
os.Stdout = oldStdout
buf.Reset()
io.Copy(&buf, r)
result = buf.String()
expected = fmt.Sprintf("TestApp-1.0.0\n%s/%s, %s, abc123\n1 file changed, 10 insertions(+), 5 deletions(-)\npath/to/modified/file.go\n",
runtime.GOOS, runtime.GOARCH, runtime.Version())
if result != expected {
t.Errorf("Expected version output with dev build info:\n%q\ngot:\n%q", expected, result)
}
}