Skip to content

Commit 321e07b

Browse files
committed
Add unit tests for har logger
- Add har_test.go - Add argument to NewLogger to make DI possible and change flush interval (for testing purposes) Signed-off-by: MJ Kim <mjkim610@gmail.com>
1 parent fc63f04 commit 321e07b

4 files changed

Lines changed: 167 additions & 3 deletions

File tree

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/projectdiscovery/roundrobin v0.0.6
2020
github.com/projectdiscovery/tinydns v0.0.77
2121
github.com/projectdiscovery/utils v0.4.13
22+
github.com/stretchr/testify v1.9.0
2223
github.com/things-go/go-socks5 v0.0.5
2324
golang.org/x/net v0.33.0
2425
gopkg.in/yaml.v3 v3.0.1
@@ -37,6 +38,7 @@ require (
3738
github.com/klauspost/pgzip v1.2.6 // indirect
3839
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
3940
github.com/mholt/archiver/v3 v3.5.1 // indirect
41+
github.com/pmezard/go-difflib v1.0.0 // indirect
4042
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
4143
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
4244
github.com/shirou/gopsutil/v3 v3.23.7 // indirect

pkg/logger/har/har.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/projectdiscovery/martian/v3/har"
1313
)
1414

15-
const flushInterval = 10 * time.Second
15+
const FlushInterval = 10 * time.Second
1616

1717
type Writer struct {
1818
f *os.File
@@ -26,7 +26,7 @@ type Logger struct {
2626
wg sync.WaitGroup
2727
}
2828

29-
func NewLogger(filePath string) (*Logger, error) {
29+
func NewLogger(filePath string, flushInterval time.Duration) (*Logger, error) {
3030
martianHarLogger := har.NewLogger()
3131
writer, err := newWriter(filePath)
3232
if err != nil {

pkg/logger/har/har_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package har
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"net/http/httptest"
7+
"os"
8+
"path/filepath"
9+
"testing"
10+
"time"
11+
12+
"github.com/projectdiscovery/martian/v3/har"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func TestNewLogger(t *testing.T) {
17+
tempDir := t.TempDir()
18+
tempFilePath := filepath.Join(tempDir, "test.har")
19+
20+
logger, err := NewLogger(tempFilePath, FlushInterval)
21+
require.NoError(t, err)
22+
require.NotNil(t, logger)
23+
defer func() {
24+
require.NoError(t, logger.Close())
25+
}()
26+
27+
// Check that the logger and its components are initialized
28+
require.NotNil(t, logger.martianHarLogger)
29+
require.NotNil(t, logger.writer)
30+
require.NotNil(t, logger.done)
31+
32+
// Check that the file was created
33+
_, err = os.Stat(tempFilePath)
34+
require.NoError(t, err)
35+
}
36+
37+
func TestAppend(t *testing.T) {
38+
tempDir := t.TempDir()
39+
tempFilePath := filepath.Join(tempDir, "test.har")
40+
41+
writer, err := newWriter(tempFilePath)
42+
require.NoError(t, err)
43+
require.NotNil(t, writer)
44+
defer func() {
45+
require.NoError(t, writer.f.Close())
46+
}()
47+
48+
// First append
49+
har1 := &har.HAR{
50+
Log: &har.Log{
51+
Entries: []*har.Entry{
52+
{Request: &har.Request{URL: "https://example.com/1"}},
53+
},
54+
},
55+
}
56+
err = writer.append(har1)
57+
require.NoError(t, err)
58+
59+
// Second append
60+
har2 := &har.HAR{
61+
Log: &har.Log{
62+
Entries: []*har.Entry{
63+
{Request: &har.Request{URL: "https://example.com/2"}},
64+
},
65+
},
66+
}
67+
err = writer.append(har2)
68+
require.NoError(t, err)
69+
70+
// Read the file and verify its content
71+
fileContent, err := os.ReadFile(tempFilePath)
72+
require.NoError(t, err)
73+
74+
var resultHar har.HAR
75+
err = json.Unmarshal(fileContent, &resultHar)
76+
require.NoError(t, err)
77+
78+
// Verify the entries
79+
require.Len(t, resultHar.Log.Entries, 2)
80+
require.Equal(t, "https://example.com/1", resultHar.Log.Entries[0].Request.URL)
81+
require.Equal(t, "https://example.com/2", resultHar.Log.Entries[1].Request.URL)
82+
}
83+
84+
func TestLoggerLifecycle(t *testing.T) {
85+
tempDir := t.TempDir()
86+
tempFilePath := filepath.Join(tempDir, "test.har")
87+
88+
// Use a shorter flush period for testing
89+
flushInterval := 200 * time.Millisecond
90+
91+
logger, err := NewLogger(tempFilePath, flushInterval)
92+
require.NoError(t, err)
93+
require.NotNil(t, logger)
94+
95+
// Create a test server
96+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
97+
w.WriteHeader(http.StatusOK)
98+
}))
99+
defer server.Close()
100+
101+
var harResult har.HAR
102+
103+
// Simulate some requests and responses by calling the logger directly
104+
// This is not ideal, but it's the simplest way to test the flushing
105+
// without setting up a full martian proxy.
106+
request1 := mustNewRequest(t, "GET", server.URL+"/req1")
107+
response1 := &http.Response{Request: request1}
108+
request2 := mustNewRequest(t, "POST", server.URL+"/req2")
109+
response2 := &http.Response{Request: request2}
110+
require.NoError(t, logger.martianHarLogger.RecordRequest("1", request1))
111+
require.NoError(t, logger.martianHarLogger.RecordResponse("1", response1))
112+
require.NoError(t, logger.martianHarLogger.RecordRequest("2", request2))
113+
require.NoError(t, logger.martianHarLogger.RecordResponse("2", response2))
114+
115+
// Wait for the background goroutine to flush
116+
require.Eventually(t, func() bool {
117+
fileContent, err := os.ReadFile(tempFilePath)
118+
if err != nil {
119+
return false
120+
}
121+
var harResult har.HAR
122+
if err := json.Unmarshal(fileContent, &harResult); err != nil {
123+
return false
124+
}
125+
return len(harResult.Log.Entries) == 2
126+
}, time.Second, 50*time.Millisecond)
127+
128+
// Check the content after the periodic flush
129+
fileContent, err := os.ReadFile(tempFilePath)
130+
require.NoError(t, err)
131+
132+
err = json.Unmarshal(fileContent, &harResult)
133+
require.NoError(t, err)
134+
require.Len(t, harResult.Log.Entries, 2)
135+
require.Equal(t, server.URL+"/req1", harResult.Log.Entries[0].Request.URL)
136+
require.Equal(t, server.URL+"/req2", harResult.Log.Entries[1].Request.URL)
137+
138+
// Simulate more requests and then close the logger
139+
request3 := mustNewRequest(t, "PUT", server.URL+"/req3")
140+
response3 := &http.Response{Request: request3}
141+
require.NoError(t, logger.martianHarLogger.RecordRequest("3", request3))
142+
require.NoError(t, logger.martianHarLogger.RecordResponse("3", response3))
143+
144+
require.NoError(t, logger.Close())
145+
146+
// Check the final content after Close calls Flush
147+
fileContent, err = os.ReadFile(tempFilePath)
148+
require.NoError(t, err)
149+
150+
err = json.Unmarshal(fileContent, &harResult)
151+
require.NoError(t, err)
152+
require.Len(t, harResult.Log.Entries, 3)
153+
require.Equal(t, server.URL+"/req1", harResult.Log.Entries[0].Request.URL)
154+
require.Equal(t, server.URL+"/req2", harResult.Log.Entries[1].Request.URL)
155+
require.Equal(t, server.URL+"/req3", harResult.Log.Entries[2].Request.URL)
156+
}
157+
158+
func mustNewRequest(t *testing.T, method, url string) *http.Request {
159+
req, err := http.NewRequest(method, url, nil)
160+
require.NoError(t, err)
161+
return req
162+
}

pkg/logger/logger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func NewLogger(options *OptionsLogger) *Logger {
9999
}
100100

101101
if options.OutputHar != "" {
102-
harLogger, err := har.NewLogger(options.OutputHar)
102+
harLogger, err := har.NewLogger(options.OutputHar, har.FlushInterval)
103103
if err != nil {
104104
gologger.Error().Msgf("Could not create HAR logger: %s", err)
105105
} else {

0 commit comments

Comments
 (0)