ronantakizawa/codereview-bench huggingface dataset:
Repository: caddyserver/caddy
File: admin.go (Go)

Code before:
	},
}

func handleConfig(w http.ResponseWriter, r *http.Request) error {
	switch r.Method {
	case http.MethodGet:
		w.Header().Set("Content-Type", "application/json")
		hash := etagHasher()

		// Read the config into a buffer instead of writing directly to
		// the response writer, as we want to set the ETag as the header,
		// not the trailer.
		buf := bufferPool.Get().(*bytes.Buffer)

		configWriter := io.MultiWriter(buf, hash)
		err := readConfig(r.URL.Path, configWriter)
		if err != nil {
			return APIError{HTTPStatus: http.StatusBadRequest, Err: err}
		}

		// we could consider setting up a sync.Pool for the summed
		// hashes to reduce GC pressure.
		w.Header().Set("Etag", makeEtag(r.URL.Path, hash))
		_, err = w.Write(buf.Bytes())
		if err != nil {
			return APIError{HTTPStatus: http.StatusBadRequest, Err: err}
		}

		buf.Reset()
		bufferPool.Put(buf)

		return nil

	case http.MethodPost,
		http.MethodPut,
		http.MethodPatch,
		http.MethodDelete:

		// DELETE does not use a body, but the others do
		var body []byte
		if r.Method != http.MethodDelete {
			if ct := r.Header.Get("Content-Type"); !strings.Contains(ct, "/json") {
				return APIError{
					HTTPStatus: http.StatusBadRequest,
					Err:        fmt.Errorf("unacceptable content-type: %v; 'application/json' required", ct),
				}
			}

			buf := bufPool.Get().(*bytes.Buffer)
			buf.Reset()
			defer bufPool.Put(buf)

Review comment (suggestion):
An `InternalServerError` would be a better error 🤔 . After all, there shouldn't be any error writing the generated response.

Code after:
	},
}

func handleConfig(w http.ResponseWriter, r *http.Request) error {
	switch r.Method {
	case http.MethodGet:
		w.Header().Set("Content-Type", "application/json")
		hash := etagHasher()

		// Read the config into a buffer instead of writing directly to
		// the response writer, as we want to set the ETag as the header,
		// not the trailer.
		buf := bufferPool.Get().(*bytes.Buffer)
		buf.Reset()
		defer bufferPool.Put(buf)

		configWriter := io.MultiWriter(buf, hash)
		err := readConfig(r.URL.Path, configWriter)
		if err != nil {
			return APIError{HTTPStatus: http.StatusBadRequest, Err: err}
		}

		// we could consider setting up a sync.Pool for the summed
		// hashes to reduce GC pressure.
		w.Header().Set("Etag", makeEtag(r.URL.Path, hash))
		_, err = w.Write(buf.Bytes())
		if err != nil {
			return APIError{HTTPStatus: http.StatusInternalServerError, Err: err}
		}

		return nil

	case http.MethodPost,
		http.MethodPut,
		http.MethodPatch,
		http.MethodDelete:

		// DELETE does not use a body, but the others do
		var body []byte
		if r.Method != http.MethodDelete {
			if ct := r.Header.Get("Content-Type"); !strings.Contains(ct, "/json") {
				return APIError{
					HTTPStatus: http.StatusBadRequest,
					Err:        fmt.Errorf("unacceptable content-type: %v; 'application/json' required", ct),
				}
			}

			buf := bufPool.Get().(*bytes.Buffer)
			buf.Reset()
			defer bufPool.Put(buf)
Repository: caddyserver/caddy
File: modules/caddyhttp/caddyhttp_test.go (Go)

Code before:
			inputPath: "/foo/bar",
			expect:    filepath.Join("/", "a", "foo", "bar"),
		},
		{
			inputPath: "/foo/../bar",
			expect:    "bar",
		},
		{
			inputRoot: "/a/b",
			inputPath: "/foo/../bar",
			expect:    filepath.Join("/", "a", "b", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/..%2fbar",
			expect:    filepath.Join("/", "a", "b", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/%2e%2e%2fbar",
			expect:    filepath.Join("/", "a", "b", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/%2e%2e%2f%2e%2e%2f",
			expect:    filepath.Join("/", "a", "b"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/foo%2fbar",
			expect:    filepath.Join("/", "a", "b", "foo", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/foo%252fbar",
			expect:    filepath.Join("/", "a", "b", "foo%2fbar"),
		},
		{
			inputRoot: "C:\\www",
			inputPath: "/foo/bar",
			expect:    filepath.Join("C:\\www", "foo", "bar"),
		},
		{
			inputRoot: "C:\\www",
			inputPath: "/D:\\foo\\bar",
			expect:    filepath.Join("C:\\www", "D:\\foo\\bar"),
		},
		{
			// https://github.com/golang/go/issues/56336#issuecomment-1416214885
			inputRoot: "root",
			inputPath: "/a/b/../../c",

Review comment (suggestion):
The `inputPath` is deemed by `IsLocal()` to be unsafe (that is, not local); which surprised me. In that case we just return the root without any request path appended... hence removing the `+ separator` here, but I don't fully 100% understand the reason for this.

Code after:
		{
			inputRoot: "/a",
			inputPath: "/foo/bar",
			expect:    filepath.Join("/", "a", "foo", "bar"),
		},
		{
			inputPath: "/foo/../bar",
			expect:    "bar",
		},
		{
			inputRoot: "/a/b",
			inputPath: "/foo/../bar",
			expect:    filepath.Join("/", "a", "b", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/..%2fbar",
			expect:    filepath.Join("/", "a", "b", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/%2e%2e%2fbar",
			expect:    filepath.Join("/", "a", "b", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/%2e%2e%2f%2e%2e%2f",
			expect:    "/a/b", // inputPath fails the IsLocal test so only the root is returned
		},
		{
			inputRoot: "/a/b",
			inputPath: "/foo%2fbar",
			expect:    filepath.Join("/", "a", "b", "foo", "bar"),
		},
		{
			inputRoot: "/a/b",
			inputPath: "/foo%252fbar",
			expect:    filepath.Join("/", "a", "b", "foo%2fbar"),
		},
		{
			inputRoot: "C:\\www",
			inputPath: "/foo/bar",
			expect:    filepath.Join("C:\\www", "foo", "bar"),
		},
		{
			inputRoot:     "C:\\www",
			inputPath:     "/D:\\foo\\bar",
			expect:        filepath.Join("C:\\www", "D:\\foo\\bar"),
			expectWindows: filepath.Join("C:\\www"), // inputPath fails IsLocal on Windows
		},
		{
Repository: caddyserver/caddy
File: modules/caddytls/acmeissuer.go (Go)

Code before:
//	        root_common_name <common_names...>
//	        any_common_name  <common_names...>
//	    }
//	}
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	d.Next() // consume issuer name

	if d.NextArg() {
		iss.CA = d.Val()
		if d.NextArg() {
			return d.ArgErr()
		}
	}

	for d.NextBlock(0) {
		switch d.Val() {
		case "lifetime":
			var lifetimeStr string
			if !d.AllArgs(&lifetimeStr) {
				return d.ArgErr()
			}
			lifetime, err := caddy.ParseDuration(lifetimeStr)
			if err != nil {
				return d.Errf("invalid lifetime %s: %v", lifetimeStr, err)
			}
			// TODO: Add check that valid lifetime must be >= 0
			iss.CertificateLifetime = caddy.Duration(lifetime)
		case "dir":
			if iss.CA != "" {
				return d.Errf("directory is already specified: %s", iss.CA)
			}
			if !d.AllArgs(&iss.CA) {
				return d.ArgErr()
			}

		case "test_dir":
			if !d.AllArgs(&iss.TestCA) {
				return d.ArgErr()
			}

		case "email":
			if !d.AllArgs(&iss.Email) {
				return d.ArgErr()
			}

		case "timeout":
			var timeoutStr string
			if !d.AllArgs(&timeoutStr) {
				return d.ArgErr()
			}
			timeout, err := caddy.ParseDuration(timeoutStr)

Review comment (suggestion):
```suggestion
			if lifetime < 0 {
				return d.Errf("lifetime must be > 0: %s", lifetime)
			}
```

Code after:
//	        any_common_name  <common_names...>
//	    }
//	}
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	d.Next() // consume issuer name

	if d.NextArg() {
		iss.CA = d.Val()
		if d.NextArg() {
			return d.ArgErr()
		}
	}

	for d.NextBlock(0) {
		switch d.Val() {
		case "lifetime":
			var lifetimeStr string
			if !d.AllArgs(&lifetimeStr) {
				return d.ArgErr()
			}
			lifetime, err := caddy.ParseDuration(lifetimeStr)
			if err != nil {
				return d.Errf("invalid lifetime %s: %v", lifetimeStr, err)
			}
			if lifetime < 0 {
				return d.Errf("lifetime must be >= 0: %s", lifetime)
			}
			iss.CertificateLifetime = caddy.Duration(lifetime)
			
		case "dir":
			if iss.CA != "" {
				return d.Errf("directory is already specified: %s", iss.CA)
			}
			if !d.AllArgs(&iss.CA) {
				return d.ArgErr()
			}

		case "test_dir":
			if !d.AllArgs(&iss.TestCA) {
				return d.ArgErr()
			}

		case "email":
			if !d.AllArgs(&iss.Email) {
				return d.ArgErr()
			}

		case "timeout":
			var timeoutStr string
			if !d.AllArgs(&timeoutStr) {
				return d.ArgErr()
Repository: caddyserver/caddy
File: caddyconfig/caddyfile/lexer.go (Go)

Code before:
	}

	// return the final value
	return []rune(out), nil
}

func (t Token) Quoted() bool {
	return t.wasQuoted > 0
}

// NumLineBreaks counts how many line breaks are in the token text.
func (t Token) NumLineBreaks() int {
	lineBreaks := strings.Count(t.Text, "\n")
	if t.wasQuoted == '<' {
		// heredocs have an extra linebreak because the opening
		// delimiter is on its own line and is not included in the
		// token Text itself, and the trailing newline is removed.
		lineBreaks += 2
	}
	return lineBreaks
}

var heredocMarkerRegexp = regexp.MustCompile("^[A-Za-z0-9_-]+$")

// isNextOnNewLine tests whether t2 is on a different line from t1
func isNextOnNewLine(t1, t2 Token) bool {
	// If the second token is from a different file,
	// we can assume it's from a different line
	if t1.File != t2.File {
		return true
	}

	// If the second token is from a different import chain,
	// we can assume it's from a different line
	if len(t1.imports) != len(t2.imports) {
		return true
	}
	for i, im := range t1.imports {
		if im != t2.imports[i] {
			return true
		}
	}

	// If the first token (incl line breaks) ends
	// on a line earlier than the next token,
	// then the second token is on a new line
	return t1.Line+t1.NumLineBreaks() < t2.Line
}


Review comment (suggestion):
I needed to make a `Clone()` because the token has some unexported fields that can't be copied from inside the `httpcaddyfile` package.

Code after:
	}

	// return the final value
	return []rune(out), nil
}

// Quoted returns true if the token was enclosed in quotes
// (i.e. double quotes, backticks, or heredoc).
func (t Token) Quoted() bool {
	return t.wasQuoted > 0
}

// NumLineBreaks counts how many line breaks are in the token text.
func (t Token) NumLineBreaks() int {
	lineBreaks := strings.Count(t.Text, "\n")
	if t.wasQuoted == '<' {
		// heredocs have an extra linebreak because the opening
		// delimiter is on its own line and is not included in the
		// token Text itself, and the trailing newline is removed.
		lineBreaks += 2
	}
	return lineBreaks
}

// Clone returns a deep copy of the token.
func (t Token) Clone() Token {
	return Token{
		File:          t.File,
		imports:       append([]string{}, t.imports...),
		Line:          t.Line,
		Text:          t.Text,
		wasQuoted:     t.wasQuoted,
		heredocMarker: t.heredocMarker,
		snippetName:   t.snippetName,
	}
}

var heredocMarkerRegexp = regexp.MustCompile("^[A-Za-z0-9_-]+$")

// isNextOnNewLine tests whether t2 is on a different line from t1
func isNextOnNewLine(t1, t2 Token) bool {
	// If the second token is from a different file,
	// we can assume it's from a different line
	if t1.File != t2.File {
		return true
	}

	// If the second token is from a different import chain,
	// we can assume it's from a different line
	if len(t1.imports) != len(t2.imports) {
		return true
Repository: caddyserver/caddy
File: modules/caddyhttp/reverseproxy/healthchecks.go (Go)

Code before:
		timeout = 5 * time.Second
	}

	if a.Path != "" {
		a.logger.Warn("the 'path' option is deprecated, please use 'uri' instead!")
	}

	// parse the URI string (supports path and query)
	if a.URI != "" {
		parsedURI, err := url.Parse(a.URI)
		if err != nil {
			return err
		}
		a.uri = parsedURI
	}

	a.httpClient = &http.Client{
		Timeout:   timeout,
		Transport: h.Transport,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			if !a.HealthFollowRedirects {
				return errors.New(
					"Redirects are disabled in health check, set health_follow_redirects flag in config to avoid this error")
			} else {
				return nil
			}
		},
	}

	for _, upstream := range h.Upstreams {
		// if there's an alternative port for health-check provided in the config,
		// then use it, otherwise use the port of upstream.
		if a.Port != 0 {
			upstream.activeHealthCheckPort = a.Port
		}
	}

	if a.Interval == 0 {
		a.Interval = caddy.Duration(30 * time.Second)
	}

	if a.ExpectBody != "" {
		var err error
		a.bodyRegexp, err = regexp.Compile(a.ExpectBody)
		if err != nil {
			return fmt.Errorf("expect_body: compiling regular expression: %v", err)
		}
	}

	if a.Passes < 1 {
		a.Passes = 1

Review comment (suggestion):
The `else` is unnecessary

```suggestion
			}
			return nil
```

Code after:
	}

	if a.Path != "" {
		a.logger.Warn("the 'path' option is deprecated, please use 'uri' instead!")
	}

	// parse the URI string (supports path and query)
	if a.URI != "" {
		parsedURI, err := url.Parse(a.URI)
		if err != nil {
			return err
		}
		a.uri = parsedURI
	}

	a.httpClient = &http.Client{
		Timeout:   timeout,
		Transport: h.Transport,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			if !a.FollowRedirects {
				return http.ErrUseLastResponse
			}
			return nil
		},
	}

	for _, upstream := range h.Upstreams {
		// if there's an alternative port for health-check provided in the config,
		// then use it, otherwise use the port of upstream.
		if a.Port != 0 {
			upstream.activeHealthCheckPort = a.Port
		}
	}

	if a.Interval == 0 {
		a.Interval = caddy.Duration(30 * time.Second)
	}

	if a.ExpectBody != "" {
		var err error
		a.bodyRegexp, err = regexp.Compile(a.ExpectBody)
		if err != nil {
			return fmt.Errorf("expect_body: compiling regular expression: %v", err)
		}
	}

	if a.Passes < 1 {
		a.Passes = 1
	}

	if a.Fails < 1 {
Repository: caddyserver/caddy
File: modules/logging/filewriter_test.go (Go)

Code before:
				Roll: &on,
			},
			wantMode: 0o600,
		},
		{
			name: "custom mode no roll",
			fw: FileWriter{
				Roll: &off,
				Mode: 0o666,
			},
			wantMode: 0o666,
		},
		{
			name: "custom mode roll",
			fw: FileWriter{
				Roll: &on,
				Mode: 0o666,
			},
			wantMode: 0o666,
		},
	}

	m := syscall.Umask(0o000)
	defer func() {
		syscall.Umask(m)
	}()

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dir, err := os.MkdirTemp("", "caddytest")
			if err != nil {
				t.Fatalf("failed to create tempdir: %v", err)
			}
			defer os.RemoveAll(dir)
			fpath := path.Join(dir, "test.log")
			tt.fw.Filename = fpath

			logger, err := tt.fw.OpenWriter()
			if err != nil {
				t.Fatalf("failed to create file: %v", err)
			}
			defer logger.Close()

			st, err := os.Stat(fpath)
			if err != nil {
				t.Fatalf("failed to check file permissions: %v", err)
			}

			if st.Mode() != tt.wantMode {
				t.Errorf("file mode is %v, want %v", st.Mode(), tt.wantMode)
			}

Review comment (bug):
Tests are failing on windows due to the umask syscall not defined (God bless CI/CD !).
What would your recommendation be to manage this : having a helper mock that does nothing on windows ? That would imply having additional filewriter_test_unix.go and filewriter_test_windows.go  files to define the mock ? 

Code after:
		{
			name: "default mode roll",
			fw: FileWriter{
				Roll: &on,
			},
			wantMode: 0o600,
		},
		{
			name: "custom mode no roll",
			fw: FileWriter{
				Roll: &off,
				Mode: 0o666,
			},
			wantMode: 0o666,
		},
		{
			name: "custom mode roll",
			fw: FileWriter{
				Roll: &on,
				Mode: 0o666,
			},
			wantMode: 0o666,
		},
	}

	m := syscall.Umask(0o000)
	defer syscall.Umask(m)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dir, err := os.MkdirTemp("", "caddytest")
			if err != nil {
				t.Fatalf("failed to create tempdir: %v", err)
			}
			defer os.RemoveAll(dir)
			fpath := path.Join(dir, "test.log")
			tt.fw.Filename = fpath

			logger, err := tt.fw.OpenWriter()
			if err != nil {
				t.Fatalf("failed to create file: %v", err)
			}
			defer logger.Close()

			st, err := os.Stat(fpath)
			if err != nil {
				t.Fatalf("failed to check file permissions: %v", err)
			}

			if st.Mode() != tt.wantMode {
				t.Errorf("file mode is %v, want %v", st.Mode(), tt.wantMode)
Repository: caddyserver/caddy
File: modules/logging/filewriter.go (Go)

Code before:

package logging

import (
	"encoding/json"
	"fmt"
	"io"
	"math"
	"os"
	"path/filepath"
	"strconv"

	"github.com/dustin/go-humanize"
	"gopkg.in/natefinch/lumberjack.v2"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)

func init() {
	caddy.RegisterModule(FileWriter{})
}

// FileMode is a string made of 1 to 4 octal digits representing
// a numeric mode as specified with the `chmod` unix command.
// `"0777"` and `"777"` are thus equivalent values.
type FileMode os.FileMode

// UnmarshalJSON satisfies json.Unmarshaler.
func (m *FileMode) UnmarshalJSON(b []byte) error {
	if len(b) == 0 {
		return io.EOF
	}

	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	mode, err := ParseFileMode(s)
	if err != nil {
		return err
	}

	*m = FileMode(mode)
	return err
}

// ParseFileMode parses a file mode string,
// adding support for `chmod` unix command like
// 1 to 4 digital octal values.

Review comment (security):
Will this ever be a limitation or problematic? (I guess, do people ever represent permissions using any other than the octal representation?) I just don't want to assume something we shouldn't be.

Code after:

package logging

import (
	"encoding/json"
	"fmt"
	"io"
	"math"
	"os"
	"path/filepath"
	"strconv"

	"github.com/dustin/go-humanize"
	"gopkg.in/natefinch/lumberjack.v2"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)

func init() {
	caddy.RegisterModule(FileWriter{})
}

// fileMode is a string made of 1 to 4 octal digits representing
// a numeric mode as specified with the `chmod` unix command.
// `"0777"` and `"777"` are thus equivalent values.
type fileMode os.FileMode

// UnmarshalJSON satisfies json.Unmarshaler.
func (m *fileMode) UnmarshalJSON(b []byte) error {
	if len(b) == 0 {
		return io.EOF
	}

	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	mode, err := parseFileMode(s)
	if err != nil {
		return err
	}

	*m = fileMode(mode)
	return err
}

// parseFileMode parses a file mode string,
// adding support for `chmod` unix command like
// 1 to 4 digital octal values.
Repository: caddyserver/caddy
File: modules/caddytls/certmanagers.go (Go)

Code before:
package caddytls

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/caddyserver/certmagic"
	"github.com/tailscale/tscert"
	"go.uber.org/zap"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)

func init() {
	caddy.RegisterModule(Tailscale{})
	caddy.RegisterModule(HTTPCertGetter{})
}

// For referencing the requested SNI server name.
var SNIServerNameCtxKey caddy.CtxKey = "sni_server_name"

// Tailscale is a module that can get certificates from the local Tailscale process.
type Tailscale struct {
	logger *zap.Logger
}

// CaddyModule returns the Caddy module information.
func (Tailscale) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "tls.get_certificate.tailscale",
		New: func() caddy.Module { return new(Tailscale) },
	}
}

func (ts *Tailscale) Provision(ctx caddy.Context) error {
	ts.logger = ctx.Logger()
	return nil
}

func (ts Tailscale) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	ctx = context.WithValue(ctx, SNIServerNameCtxKey, hello.ServerName)
	canGetCert, err := ts.canHazCertificate(ctx, hello)
	if err == nil && !canGetCert {
		return nil, nil // pass-thru: Tailscale can't offer a cert for this name
	}

Review comment (suggestion):
```suggestion
const ClientHelloSNICtxKey caddy.CtxKey = "client_hello_sni"
```

This is potentially bikeshedding but hear me out

Code after:
package caddytls

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/caddyserver/certmagic"
	"github.com/tailscale/tscert"
	"go.uber.org/zap"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)

func init() {
	caddy.RegisterModule(Tailscale{})
	caddy.RegisterModule(HTTPCertGetter{})
}

// For referencing the requested SNI server name.
const ClientHelloSNICtxKey caddy.CtxKey = "client_hello_sni"

// Tailscale is a module that can get certificates from the local Tailscale process.
type Tailscale struct {
	logger *zap.Logger
}

// CaddyModule returns the Caddy module information.
func (Tailscale) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "tls.get_certificate.tailscale",
		New: func() caddy.Module { return new(Tailscale) },
	}
}

func (ts *Tailscale) Provision(ctx caddy.Context) error {
	ts.logger = ctx.Logger()
	return nil
}

func (ts Tailscale) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	ctx = context.WithValue(ctx, ClientHelloSNICtxKey, hello.ServerName)
	canGetCert, err := ts.canHazCertificate(ctx, hello)
	if err == nil && !canGetCert {
		return nil, nil // pass-thru: Tailscale can't offer a cert for this name
	}
Repository: caddyserver/caddy
File: cmd/main_test.go (Go)

Code before:
				configFile:  "./conf.caddyfile",
				adapterName: "",
			},
			want:    true,
			wantErr: false,
		},
		{
			name: "config is Caddyfile.yaml without adapter",
			args: args{
				configFile:  "./Caddyfile.yaml",
				adapterName: "",
			},
			want:    false,
			wantErr: true,
		},
		{
			name: "json is not caddyfile but not error",
			args: args{
				configFile:  "./Caddyfile.json",
				adapterName: "",
			},
			want:    false,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := isCaddyfile(tt.args.configFile, tt.args.adapterName)
			if (err != nil) != tt.wantErr {
				t.Errorf("isCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("isCaddyfile() = %v, want %v", got, tt.want)
			}
		})
	}
}


Review comment (suggestion):
This case is still confusing to me 😅 and I almost wonder if it should be an error.

Code after:
				configFile:  "./conf.caddyfile",
				adapterName: "caddyfile",
			},
			want:    true,
			wantErr: false,
		},
		{
			name: "ends with .caddyfile without adapter",
			args: args{
				configFile:  "./conf.caddyfile",
				adapterName: "",
			},
			want:    true,
			wantErr: false,
		},
		{
			name: "config is Caddyfile.yaml without adapter",
			args: args{
				configFile:  "./Caddyfile.yaml",
				adapterName: "",
			},
			want:    false,
			wantErr: true,
		},
		{
			name: "json is not caddyfile but not error",
			args: args{
				configFile:  "./Caddyfile.json",
				adapterName: "",
			},
			want:    false,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := isCaddyfile(tt.args.configFile, tt.args.adapterName)
			if (err != nil) != tt.wantErr {
				t.Errorf("isCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("isCaddyfile() = %v, want %v", got, tt.want)
			}
		})
	}
}