Files
MyGoNavi/internal/app/methods_jvm_diagnostic_test.go
Syngnat ec2eefc9d2 🐛 fix(jvm): 加固诊断命令策略与输出脱敏
在服务端阻断只读连接中的高风险和多行诊断命令,并对诊断事件与错误消息统一脱敏,避免凭证、Authorization 和 PEM 片段泄漏。
2026-04-28 09:42:41 +08:00

721 lines
23 KiB
Go

package app
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/jvm"
)
type fakeDiagnosticTransport struct {
testErr error
caps []jvm.DiagnosticCapability
capsErr error
handle jvm.DiagnosticSessionHandle
startErr error
executeReq jvm.DiagnosticCommandRequest
executeErr error
executeCalls int
cancelSession string
cancelCommand string
cancelErr error
}
func (f fakeDiagnosticTransport) Mode() string { return jvm.DiagnosticTransportAgentBridge }
func (f fakeDiagnosticTransport) TestConnection(context.Context, connection.ConnectionConfig) error {
return f.testErr
}
func (f fakeDiagnosticTransport) ProbeCapabilities(context.Context, connection.ConnectionConfig) ([]jvm.DiagnosticCapability, error) {
return f.caps, f.capsErr
}
func (f fakeDiagnosticTransport) StartSession(context.Context, connection.ConnectionConfig, jvm.DiagnosticSessionRequest) (jvm.DiagnosticSessionHandle, error) {
return f.handle, f.startErr
}
func (f fakeDiagnosticTransport) ExecuteCommand(context.Context, connection.ConnectionConfig, jvm.DiagnosticCommandRequest) error {
return f.executeErr
}
func (f fakeDiagnosticTransport) CancelCommand(context.Context, connection.ConnectionConfig, string, string) error {
return f.cancelErr
}
func (f fakeDiagnosticTransport) CloseSession(context.Context, connection.ConnectionConfig, string) error {
return nil
}
type fakeStreamingDiagnosticTransport struct {
sink jvm.DiagnosticEventSink
chunks []jvm.DiagnosticEventChunk
executeErr error
}
func (f *fakeStreamingDiagnosticTransport) Mode() string { return jvm.DiagnosticTransportAgentBridge }
func (f *fakeStreamingDiagnosticTransport) TestConnection(context.Context, connection.ConnectionConfig) error {
return nil
}
func (f *fakeStreamingDiagnosticTransport) ProbeCapabilities(context.Context, connection.ConnectionConfig) ([]jvm.DiagnosticCapability, error) {
return nil, nil
}
func (f *fakeStreamingDiagnosticTransport) StartSession(context.Context, connection.ConnectionConfig, jvm.DiagnosticSessionRequest) (jvm.DiagnosticSessionHandle, error) {
return jvm.DiagnosticSessionHandle{}, nil
}
func (f *fakeStreamingDiagnosticTransport) SetEventSink(sink jvm.DiagnosticEventSink) {
f.sink = sink
}
func (f *fakeStreamingDiagnosticTransport) ExecuteCommand(context.Context, connection.ConnectionConfig, jvm.DiagnosticCommandRequest) error {
if f.sink != nil {
chunks := f.chunks
if len(chunks) == 0 {
chunks = []jvm.DiagnosticEventChunk{{
Event: "diagnostic",
Phase: "running",
Content: "PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nabc123",
}}
}
for _, chunk := range chunks {
f.sink(chunk)
}
}
return f.executeErr
}
func (f *fakeStreamingDiagnosticTransport) CancelCommand(context.Context, connection.ConnectionConfig, string, string) error {
return nil
}
func (f *fakeStreamingDiagnosticTransport) CloseSession(context.Context, connection.ConnectionConfig, string) error {
return nil
}
func TestJVMProbeDiagnosticCapabilitiesReturnsTransportPayload(t *testing.T) {
app := NewAppWithSecretStore(nil)
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return fakeDiagnosticTransport{
caps: []jvm.DiagnosticCapability{{
Transport: jvm.DiagnosticTransportAgentBridge,
CanOpenSession: true,
CanStream: true,
}},
}, nil
})
defer restore()
res := app.JVMProbeDiagnosticCapabilities(connection.ConnectionConfig{
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
},
},
})
if !res.Success {
t.Fatalf("expected success, got %+v", res)
}
items, ok := res.Data.([]jvm.DiagnosticCapability)
if !ok {
t.Fatalf("expected diagnostic capability payload, got %#v", res.Data)
}
if len(items) != 1 || items[0].Transport != jvm.DiagnosticTransportAgentBridge {
t.Fatalf("unexpected diagnostic capabilities: %#v", items)
}
}
func TestJVMStartDiagnosticSessionReturnsHandle(t *testing.T) {
app := NewAppWithSecretStore(nil)
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return fakeDiagnosticTransport{
handle: jvm.DiagnosticSessionHandle{
SessionID: "sess-1",
Transport: jvm.DiagnosticTransportAgentBridge,
StartedAt: 1713945600000,
},
}, nil
})
defer restore()
res := app.JVMStartDiagnosticSession(connection.ConnectionConfig{
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
},
},
}, jvm.DiagnosticSessionRequest{
Title: "排查线程堆积",
Reason: "先建立诊断会话",
})
if !res.Success {
t.Fatalf("expected success, got %+v", res)
}
handle, ok := res.Data.(jvm.DiagnosticSessionHandle)
if !ok {
t.Fatalf("expected diagnostic session handle, got %#v", res.Data)
}
if handle.SessionID != "sess-1" || handle.Transport != jvm.DiagnosticTransportAgentBridge {
t.Fatalf("unexpected diagnostic session handle: %#v", handle)
}
}
func TestJVMExecuteDiagnosticCommandReturnsAccepted(t *testing.T) {
app := NewAppWithSecretStore(nil)
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-1",
Command: "thread -n 5",
Source: "manual",
Reason: "定位线程堆积",
})
if !res.Success {
t.Fatalf("expected success, got %+v", res)
}
if recorder.executeReq.Command != "thread -n 5" || recorder.executeReq.SessionID != "sess-1" {
t.Fatalf("unexpected execute request: %#v", recorder.executeReq)
}
}
func TestJVMExecuteDiagnosticCommandBlocksTraceWhenConnectionReadOnly(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
readOnly := true
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
ReadOnly: &readOnly,
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowTraceCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-trace-1",
Command: "watch com.foo.OrderService submitOrder '{params,returnObj}' -x 2",
Source: "manual",
Reason: "定位慢调用",
})
if res.Success {
t.Fatalf("expected trace command to be blocked in read-only mode, got %+v", res)
}
if !strings.Contains(res.Message, "只读") {
t.Fatalf("expected read-only message, got %+v", res)
}
if recorder.executeCalls != 0 {
t.Fatalf("expected transport ExecuteCommand not called, got %d", recorder.executeCalls)
}
}
func TestJVMExecuteDiagnosticCommandBlocksMutatingWhenConnectionReadOnly(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
readOnly := true
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
ReadOnly: &readOnly,
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowMutatingCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-mutating-1",
Command: "ognl '@java.lang.System@getProperty(\"user.dir\")'",
Source: "manual",
Reason: "读取系统属性",
})
if res.Success {
t.Fatalf("expected mutating command to be blocked in read-only mode, got %+v", res)
}
if !strings.Contains(res.Message, "只读") {
t.Fatalf("expected read-only message, got %+v", res)
}
if recorder.executeCalls != 0 {
t.Fatalf("expected transport ExecuteCommand not called, got %d", recorder.executeCalls)
}
}
func TestJVMExecuteDiagnosticCommandBlocksMultilineCommandWhenConnectionReadOnly(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
readOnly := true
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
ReadOnly: &readOnly,
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
AllowTraceCommands: true,
AllowMutatingCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-multiline-1",
Command: "thread -n 1\nognl '@java.lang.System@setProperty(\"x\",\"y\")'",
Source: "manual",
Reason: "观察线程",
})
if res.Success {
t.Fatalf("expected multiline command to be blocked in read-only mode, got %+v", res)
}
if recorder.executeCalls != 0 {
t.Fatalf("expected transport ExecuteCommand not called, got %d", recorder.executeCalls)
}
}
func TestJVMExecuteDiagnosticCommandAllowsObserveWhenConnectionReadOnly(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
readOnly := true
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
ReadOnly: &readOnly,
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-observe-1",
Command: "thread -n 5",
Source: "manual",
Reason: "观察线程",
})
if !res.Success {
t.Fatalf("expected observe command to be allowed in read-only mode, got %+v", res)
}
if recorder.executeCalls != 1 {
t.Fatalf("expected transport ExecuteCommand called once, got %d", recorder.executeCalls)
}
}
func TestJVMExecuteDiagnosticCommandRedactsExecuteErrorMessage(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return fakeDiagnosticTransport{executeErr: errors.New("Authorization: Bearer header-secret")}, nil
})
defer restore()
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-observe-secret",
Command: "thread -n 5",
Source: "manual",
Reason: "观察线程",
})
if res.Success {
t.Fatalf("expected execute failure, got %+v", res)
}
if strings.Contains(res.Message, "header-secret") {
t.Fatalf("expected execute error message to be redacted, got %q", res.Message)
}
if !strings.Contains(res.Message, "Authorization: ********") {
t.Fatalf("expected redacted authorization message, got %q", res.Message)
}
}
func TestJVMExecuteDiagnosticCommandRedactsExecuteErrorWithStreamingPEMState(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return &fakeStreamingDiagnosticTransport{executeErr: errors.New("def456\n-----END PRIVATE KEY-----")}, nil
})
defer restore()
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-observe-pem",
Command: "thread -n 5",
Source: "manual",
Reason: "观察线程",
})
if res.Success {
t.Fatalf("expected execute failure, got %+v", res)
}
if strings.Contains(res.Message, "def456") || strings.Contains(res.Message, "PRIVATE KEY") {
t.Fatalf("expected execute error PEM continuation to be redacted, got %q", res.Message)
}
}
func TestJVMExecuteDiagnosticCommandRedactsPolicyErrorMessage(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-policy-secret",
Command: "watch com.foo.OrderService submitOrder '{params}' password=plain-secret",
Source: "manual",
Reason: "观察线程",
})
if res.Success {
t.Fatalf("expected policy failure, got %+v", res)
}
if strings.Contains(res.Message, "plain-secret") {
t.Fatalf("expected policy error message to be redacted, got %q", res.Message)
}
if recorder.executeCalls != 0 {
t.Fatalf("expected transport ExecuteCommand not called, got %d", recorder.executeCalls)
}
}
func TestJVMExecuteDiagnosticCommandEmitsRedactedChunksWithRequestIDs(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
app.ctx = context.Background()
var emitted []diagnosticChunkEventPayload
prevEmitter := emitJVMDiagnosticRuntimeEvent
emitJVMDiagnosticRuntimeEvent = func(ctx context.Context, eventName string, optionalData ...interface{}) {
if eventName != diagnosticChunkEvent {
return
}
payload, ok := optionalData[0].(diagnosticChunkEventPayload)
if !ok {
t.Fatalf("expected diagnostic chunk event payload, got %#v", optionalData[0])
}
emitted = append(emitted, payload)
}
defer func() { emitJVMDiagnosticRuntimeEvent = prevEmitter }()
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return &fakeStreamingDiagnosticTransport{
chunks: []jvm.DiagnosticEventChunk{
{
SessionID: "remote-sess",
CommandID: "remote-cmd-1",
Event: "diagnostic",
Phase: "running",
Content: "PRIVATE_KEY=-----BEG",
},
{
SessionID: "remote-sess",
CommandID: "remote-cmd-2",
Event: "diagnostic",
Phase: "failed",
Content: "IN PRIVATE KEY-----\nabc123\n-----END PRIVATE KEY-----",
},
},
}, nil
})
defer restore()
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-event-secret",
Command: "thread -n 5",
Source: "manual",
Reason: "观察线程",
})
if !res.Success {
t.Fatalf("expected accepted command, got %+v", res)
}
if len(emitted) != 2 {
t.Fatalf("expected 2 emitted chunks, got %#v", emitted)
}
combined := ""
for _, payload := range emitted {
if payload.TabID != "tab-diag-1" {
t.Fatalf("unexpected tab id in emitted payload: %#v", payload)
}
if payload.Chunk.SessionID != "sess-1" || payload.Chunk.CommandID != "cmd-event-secret" {
t.Fatalf("expected emitted chunk to use request ids, got %#v", payload.Chunk)
}
combined += payload.Chunk.Content
}
for _, leaked := range []string{"PRIVATE KEY", "abc123", "-----END"} {
if strings.Contains(combined, leaked) {
t.Fatalf("expected emitted chunks to be redacted, leaked %q in %q", leaked, combined)
}
}
}
func TestJVMExecuteDiagnosticCommandFailsClosedWhenAuditWriteFails(t *testing.T) {
app := NewAppWithSecretStore(nil)
tempDir := t.TempDir()
blockerPath := filepath.Join(tempDir, "audit-blocker")
if err := os.WriteFile(blockerPath, []byte("blocker"), 0o600); err != nil {
t.Fatalf("WriteFile returned error: %v", err)
}
app.configDir = blockerPath
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
readOnly := true
res := app.JVMExecuteDiagnosticCommand(connection.ConnectionConfig{
ID: "conn-orders",
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
ReadOnly: &readOnly,
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
AllowObserveCommands: true,
},
},
}, "tab-diag-1", jvm.DiagnosticCommandRequest{
SessionID: "sess-1",
CommandID: "cmd-observe-audit",
Command: "thread -n 5",
Source: "manual",
Reason: "观察线程",
})
if res.Success {
t.Fatalf("expected command to fail closed when initial audit write fails, got %+v", res)
}
if !strings.Contains(res.Message, "审计") {
t.Fatalf("expected audit failure message, got %+v", res)
}
if recorder.executeCalls != 0 {
t.Fatalf("expected transport ExecuteCommand not called, got %d", recorder.executeCalls)
}
}
func TestJVMCancelDiagnosticCommandDelegatesToTransport(t *testing.T) {
app := NewAppWithSecretStore(nil)
recorder := &fakeDiagnosticTransport{}
restore := swapJVMDiagnosticTransportFactory(func(mode string) (jvm.DiagnosticTransport, error) {
return diagnosticTransportRecorder{recorder: recorder}, nil
})
defer restore()
res := app.JVMCancelDiagnosticCommand(connection.ConnectionConfig{
Type: "jvm",
Host: "orders.internal",
JVM: connection.JVMConfig{
Diagnostic: connection.JVMDiagnosticConfig{
Enabled: true,
Transport: jvm.DiagnosticTransportAgentBridge,
BaseURL: "http://127.0.0.1:19091/gonavi/diag",
},
},
}, "tab-diag-1", "sess-1", "cmd-1")
if !res.Success {
t.Fatalf("expected success, got %+v", res)
}
if recorder.cancelSession != "sess-1" || recorder.cancelCommand != "cmd-1" {
t.Fatalf("unexpected cancel request: %#v", recorder)
}
}
func TestJVMListDiagnosticAuditRecordsReturnsRecords(t *testing.T) {
app := NewAppWithSecretStore(nil)
app.configDir = t.TempDir()
store := jvm.NewDiagnosticAuditStore(filepath.Join(app.auditRootDir(), "jvm_diag_audit.jsonl"))
if err := store.Append(jvm.DiagnosticAuditRecord{
ConnectionID: "conn-orders",
Transport: jvm.DiagnosticTransportAgentBridge,
SessionID: "sess-1",
CommandID: "cmd-1",
Command: "thread -n 5",
CommandType: jvm.DiagnosticCommandCategoryObserve,
RiskLevel: "low",
Status: "completed",
Reason: "定位线程堆积",
}); err != nil {
t.Fatalf("append audit record failed: %v", err)
}
res := app.JVMListDiagnosticAuditRecords("conn-orders", 10)
if !res.Success {
t.Fatalf("expected success, got %+v", res)
}
records, ok := res.Data.([]jvm.DiagnosticAuditRecord)
if !ok {
t.Fatalf("expected audit record slice, got %#v", res.Data)
}
if len(records) != 1 || records[0].Command != "thread -n 5" {
t.Fatalf("unexpected audit records: %#v", records)
}
}
type diagnosticTransportRecorder struct {
recorder *fakeDiagnosticTransport
}
func (d diagnosticTransportRecorder) Mode() string { return jvm.DiagnosticTransportAgentBridge }
func (d diagnosticTransportRecorder) TestConnection(ctx context.Context, cfg connection.ConnectionConfig) error {
return d.recorder.TestConnection(ctx, cfg)
}
func (d diagnosticTransportRecorder) ProbeCapabilities(ctx context.Context, cfg connection.ConnectionConfig) ([]jvm.DiagnosticCapability, error) {
return d.recorder.ProbeCapabilities(ctx, cfg)
}
func (d diagnosticTransportRecorder) StartSession(ctx context.Context, cfg connection.ConnectionConfig, req jvm.DiagnosticSessionRequest) (jvm.DiagnosticSessionHandle, error) {
return d.recorder.StartSession(ctx, cfg, req)
}
func (d diagnosticTransportRecorder) ExecuteCommand(ctx context.Context, cfg connection.ConnectionConfig, req jvm.DiagnosticCommandRequest) error {
d.recorder.executeReq = req
d.recorder.executeCalls++
return d.recorder.ExecuteCommand(ctx, cfg, req)
}
func (d diagnosticTransportRecorder) CancelCommand(ctx context.Context, cfg connection.ConnectionConfig, sessionID string, commandID string) error {
d.recorder.cancelSession = sessionID
d.recorder.cancelCommand = commandID
return d.recorder.CancelCommand(ctx, cfg, sessionID, commandID)
}
func (d diagnosticTransportRecorder) CloseSession(ctx context.Context, cfg connection.ConnectionConfig, sessionID string) error {
return d.recorder.CloseSession(ctx, cfg, sessionID)
}