Files
godoxy-yusing/internal/logging/accesslog/multi_access_logger_test.go
yusing 898002a38e feat(acl): add reason field to ACL logging for decision tracking
Add a reason parameter throughout the ACL system to track and log why
each IP was allowed or denied. This provides better visibility into
ACL decisions by recording specific reasons such as "allowed by
allow_local rule", "blocked by deny rule: [rule]", or "deny by default".

Changes include:
- Add reason field to checkCache and ipLog structs
- Update LogACL interface and implementations to accept reason
- Generate descriptive reasons for all ACL decision paths
- Include reason in console log output
2026-02-15 17:20:54 +08:00

262 lines
5.8 KiB
Go

package accesslog
import (
"errors"
"net"
"net/http"
"net/url"
"testing"
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
"github.com/yusing/goutils/task"
expect "github.com/yusing/goutils/testing"
)
func TestNewMultiAccessLogger(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writers := []File{
NewMockFile(true),
NewMockFile(true),
}
logger := NewMultiAccessLogger(testTask, cfg, writers)
expect.NotNil(t, logger)
}
func TestMultiAccessLoggerConfig(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
cfg.Format = FormatCommon
writers := []File{
NewMockFile(true),
NewMockFile(true),
}
logger := NewMultiAccessLogger(testTask, cfg, writers)
retrievedCfg := logger.Config()
expect.Equal(t, retrievedCfg.req.Format, FormatCommon)
}
func TestMultiAccessLoggerLog(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
cfg.Format = FormatCommon
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
testURL, _ := url.Parse("http://example.com/test")
req := &http.Request{
RemoteAddr: "192.168.1.1",
Method: http.MethodGet,
Proto: "HTTP/1.1",
Host: "example.com",
URL: testURL,
Header: http.Header{
"User-Agent": []string{"test-agent"},
},
}
resp := &http.Response{
StatusCode: http.StatusOK,
ContentLength: 100,
}
logger.LogRequest(req, resp)
logger.Flush()
expect.Equal(t, writer1.NumLines(), 1)
expect.Equal(t, writer2.NumLines(), 1)
}
func TestMultiAccessLoggerLogError(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
testURL, _ := url.Parse("http://example.com/test")
req := &http.Request{
RemoteAddr: "192.168.1.1",
Method: http.MethodGet,
URL: testURL,
}
testErr := errors.New("test error")
logger.LogError(req, testErr)
logger.Flush()
expect.Equal(t, writer1.NumLines(), 1)
expect.Equal(t, writer2.NumLines(), 1)
}
func TestMultiAccessLoggerLogACL(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultACLLoggerConfig()
cfg.LogAllowed = true
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
info := &maxmind.IPInfo{
IP: net.ParseIP("192.168.1.1"),
Str: "192.168.1.1",
}
logger.LogACL(info, false, "test reason")
logger.Flush()
expect.Equal(t, writer1.NumLines(), 1)
expect.Equal(t, writer2.NumLines(), 1)
}
func TestMultiAccessLoggerFlush(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
testURL, _ := url.Parse("http://example.com/test")
req := &http.Request{
RemoteAddr: "192.168.1.1",
Method: http.MethodGet,
URL: testURL,
}
resp := &http.Response{
StatusCode: http.StatusOK,
}
logger.LogRequest(req, resp)
logger.Flush()
expect.Equal(t, writer1.NumLines(), 1)
expect.Equal(t, writer2.NumLines(), 1)
}
func TestMultiAccessLoggerClose(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
err := logger.Close()
expect.Nil(t, err)
}
func TestMultiAccessLoggerMultipleLogs(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
testURL, _ := url.Parse("http://example.com/test")
for range 3 {
req := &http.Request{
RemoteAddr: "192.168.1.1",
Method: http.MethodGet,
URL: testURL,
}
resp := &http.Response{
StatusCode: http.StatusOK,
}
logger.LogRequest(req, resp)
}
logger.Flush()
expect.Equal(t, writer1.NumLines(), 3)
expect.Equal(t, writer2.NumLines(), 3)
}
func TestMultiAccessLoggerSingleWriter(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writer := NewMockFile(true)
writers := []File{writer}
logger := NewMultiAccessLogger(testTask, cfg, writers)
expect.NotNil(t, logger)
testURL, _ := url.Parse("http://example.com/test")
req := &http.Request{
RemoteAddr: "192.168.1.1",
Method: http.MethodGet,
URL: testURL,
}
resp := &http.Response{
StatusCode: http.StatusOK,
}
logger.LogRequest(req, resp)
logger.Flush()
expect.Equal(t, writer.NumLines(), 1)
}
func TestMultiAccessLoggerMixedOperations(t *testing.T) {
testTask := task.RootTask("test", false)
cfg := DefaultRequestLoggerConfig()
writer1 := NewMockFile(true)
writer2 := NewMockFile(true)
writers := []File{writer1, writer2}
logger := NewMultiAccessLogger(testTask, cfg, writers)
testURL, _ := url.Parse("http://example.com/test")
req := &http.Request{
RemoteAddr: "192.168.1.1",
Method: http.MethodGet,
URL: testURL,
}
resp := &http.Response{
StatusCode: http.StatusOK,
}
logger.LogRequest(req, resp)
logger.Flush()
info := &maxmind.IPInfo{
IP: net.ParseIP("192.168.1.1"),
Str: "192.168.1.1",
}
cfg2 := DefaultACLLoggerConfig()
cfg2.LogAllowed = true
aclLogger := NewMultiAccessLogger(testTask, cfg2, writers)
aclLogger.LogACL(info, false, "test reason")
logger.Flush()
expect.Equal(t, writer1.NumLines(), 1)
expect.Equal(t, writer2.NumLines(), 1)
}