Skip to content

Commit 01ecab5

Browse files
committed
Refactor, add tests for cli package
1 parent 5adcfa3 commit 01ecab5

File tree

6 files changed

+435
-4
lines changed

6 files changed

+435
-4
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ vendor
33
.idea
44
config/config.go.bak
55
scripts/bak.run_yc_360.ps1
6+
yc-*
7+
yc

internal/agent/agent_test.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package agent
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"yc-agent/internal/config"
8+
"yc-agent/internal/logger"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestRun(t *testing.T) {
14+
// Save original config and restore after tests
15+
originalConfig := config.GlobalConfig
16+
defer func() {
17+
config.GlobalConfig = originalConfig
18+
}()
19+
20+
// Initialize logger for tests
21+
logger.Init("", 0, 0, "info")
22+
23+
t.Run("no mode specified returns ErrNothingCanBeDone", func(t *testing.T) {
24+
config.GlobalConfig = config.Config{
25+
Options: config.Options{
26+
Pid: "", // No PID (ondemand mode disabled)
27+
M3: false,
28+
Port: 0, // API mode disabled
29+
},
30+
}
31+
32+
err := Run()
33+
assert.Equal(t, ErrNothingCanBeDone, err)
34+
})
35+
36+
t.Run("ondemand and m3 mode conflict", func(t *testing.T) {
37+
config.GlobalConfig = config.Config{
38+
Options: config.Options{
39+
Pid: "12345", // OnDemand mode enabled
40+
M3: true, // M3 mode enabled
41+
Port: 0,
42+
},
43+
}
44+
45+
err := Run()
46+
assert.Equal(t, ErrConflictingMode, err)
47+
})
48+
49+
t.Run("ondemand mode with process token and m3 mode conflict", func(t *testing.T) {
50+
config.GlobalConfig = config.Config{
51+
Options: config.Options{
52+
Pid: "buggyApp", // OnDemand mode with token
53+
M3: true, // M3 mode enabled
54+
Port: 0,
55+
},
56+
}
57+
58+
err := Run()
59+
assert.Equal(t, ErrConflictingMode, err)
60+
})
61+
}
62+
63+
func TestModeValidation(t *testing.T) {
64+
// Save original config and restore after tests
65+
originalConfig := config.GlobalConfig
66+
defer func() {
67+
config.GlobalConfig = originalConfig
68+
}()
69+
70+
// Initialize logger for tests
71+
logger.Init("", 0, 0, "info")
72+
73+
// Create temp directory for test artifacts
74+
tempDir := t.TempDir()
75+
76+
tests := []struct {
77+
name string
78+
pid string
79+
m3 bool
80+
port int
81+
expectedErr error
82+
description string
83+
}{
84+
{
85+
name: "no mode specified",
86+
pid: "",
87+
m3: false,
88+
port: 0,
89+
expectedErr: ErrNothingCanBeDone,
90+
description: "neither ondemand, m3, nor api mode is enabled",
91+
},
92+
{
93+
name: "ondemand only",
94+
pid: "12345",
95+
m3: false,
96+
port: 0,
97+
expectedErr: nil,
98+
description: "valid ondemand mode",
99+
},
100+
{
101+
name: "m3 only",
102+
pid: "",
103+
m3: true,
104+
port: 0,
105+
expectedErr: nil,
106+
description: "valid m3 mode",
107+
},
108+
{
109+
name: "api only",
110+
pid: "",
111+
m3: false,
112+
port: 8080,
113+
expectedErr: nil,
114+
description: "valid api mode",
115+
},
116+
{
117+
name: "ondemand and m3 conflict",
118+
pid: "12345",
119+
m3: true,
120+
port: 0,
121+
expectedErr: ErrConflictingMode,
122+
description: "ondemand and m3 cannot run together",
123+
},
124+
{
125+
name: "m3 and api together",
126+
pid: "",
127+
m3: true,
128+
port: 8080,
129+
expectedErr: nil,
130+
description: "m3 and api can run together",
131+
},
132+
{
133+
name: "ondemand and api together",
134+
pid: "12345",
135+
m3: false,
136+
port: 8080,
137+
expectedErr: nil,
138+
description: "ondemand and api can run together (backward compatibility)",
139+
},
140+
{
141+
name: "all three modes",
142+
pid: "12345",
143+
m3: true,
144+
port: 8080,
145+
expectedErr: ErrConflictingMode,
146+
description: "ondemand conflicts with m3 even with api mode",
147+
},
148+
}
149+
150+
for _, tt := range tests {
151+
t.Run(tt.name, func(t *testing.T) {
152+
config.GlobalConfig = config.Config{
153+
Options: config.Options{
154+
Pid: tt.pid,
155+
M3: tt.m3,
156+
Port: tt.port,
157+
Address: "localhost",
158+
OnlyCapture: true, // Required to avoid upload logic in ondemand mode
159+
JavaHomePath: "/usr/lib/jvm/java-11",
160+
StoragePath: tempDir, // Use temp directory for test artifacts
161+
},
162+
}
163+
164+
// Create a channel to catch the error or timeout
165+
errChan := make(chan error, 1)
166+
167+
go func() {
168+
err := Run()
169+
errChan <- err
170+
}()
171+
172+
// Wait for error or timeout
173+
select {
174+
case err := <-errChan:
175+
assert.Equal(t, tt.expectedErr, err, tt.description)
176+
case <-time.After(100 * time.Millisecond):
177+
// If we timeout, it means Run() is blocking (which is expected for m3/api modes)
178+
assert.Nil(t, tt.expectedErr, "%s: expected error %v, but Run() is blocking", tt.description, tt.expectedErr)
179+
// This is expected behavior for valid m3/api modes - they block indefinitely
180+
}
181+
})
182+
}
183+
}
184+
185+
func TestResolvePidsFromToken(t *testing.T) {
186+
// Save original config and restore after tests
187+
originalConfig := config.GlobalConfig
188+
defer func() {
189+
config.GlobalConfig = originalConfig
190+
}()
191+
192+
// Initialize logger for tests
193+
logger.Init("", 0, 0, "info")
194+
195+
t.Run("resolve pids from token with no matches", func(t *testing.T) {
196+
pids := resolvePidsFromToken("nonexistent-token")
197+
198+
assert.Empty(t, pids, "expected empty pids slice for nonexistent token")
199+
})
200+
}
201+
202+
func TestModeLogic(t *testing.T) {
203+
// Save original config and restore after tests
204+
originalConfig := config.GlobalConfig
205+
defer func() {
206+
config.GlobalConfig = originalConfig
207+
}()
208+
209+
// Initialize logger for tests
210+
logger.Init("", 0, 0, "info")
211+
}

internal/cli/cli.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func Run() {
2929

3030
logCLIArguments()
3131

32-
runCaptureModeIfConditionSatisfied()
32+
runJattachCaptureModes()
3333

3434
if config.GlobalConfig.ShowVersion {
3535
logger.Log("yc-360 script version: " + executils.SCRIPT_VERSION)

internal/cli/custom_mode.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ func runRawCaptureModeIfConditionSatisfied() {
2626
}
2727
}
2828

29-
// runCaptureModeIfConditionSatisfied runs capture mode depending on the config.
30-
// Capture mode can be run after parsing the config flags.
31-
func runCaptureModeIfConditionSatisfied() {
29+
// runJattachCaptureModes runs jattach-based capture modes depending on the config.
30+
// These modes (GC, thread dump, heap dump, jcmd) use jattach for direct Java diagnostics.
31+
// This function can be run after parsing the config flags and exits immediately upon execution.
32+
func runJattachCaptureModes() {
3233
if config.GlobalConfig.GCCaptureMode {
3334
pid, err := strconv.Atoi(config.GlobalConfig.Pid)
3435
if err != nil {

internal/cli/validation.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
var ErrInvalidArgumentCantContinue = errors.New("cli: invalid argument")
1414

1515
func validate() error {
16+
// Server URL and API Key
1617
if !config.GlobalConfig.OnlyCapture {
1718
if len(config.GlobalConfig.Server) < 1 {
1819
logger.Log("'-s' yCrash server URL argument not passed.")
@@ -24,6 +25,7 @@ func validate() error {
2425
}
2526
}
2627

28+
// JAVA_HOME
2729
if len(config.GlobalConfig.JavaHomePath) < 1 {
2830
config.GlobalConfig.JavaHomePath = os.Getenv("JAVA_HOME")
2931
}
@@ -32,6 +34,7 @@ func validate() error {
3234
return ErrInvalidArgumentCantContinue
3335
}
3436

37+
// M3
3538
if config.GlobalConfig.M3 && config.GlobalConfig.OnlyCapture {
3639
logger.Warn().Msg("-onlyCapture will be ignored in m3 mode.")
3740
config.GlobalConfig.OnlyCapture = false
@@ -41,6 +44,7 @@ func validate() error {
4144
logger.Warn().Msg("ProcessTokens is passed without M3 mode. See https://docs.ycrash.io/yc-360/launch-modes/m3-mode.html")
4245
}
4346

47+
// AppLog
4448
if config.GlobalConfig.AppLogLineCount < -1 {
4549
logger.Log("%d is not a valid value for 'appLogLineCount' argument. It should be -1 (all lines), 0 (no logs), or a positive number.", config.GlobalConfig.AppLogLineCount)
4650
return ErrInvalidArgumentCantContinue

0 commit comments

Comments
 (0)