11package controlpanel
22
33import (
4- "reflect"
5- "testing"
6- "unsafe"
7-
8- "github.com/google/uuid"
9- "github.com/stretchr/testify/assert"
10-
11- "github.com/langgenius/dify-plugin-daemon/internal/core/debugging_runtime"
12- "github.com/langgenius/dify-plugin-daemon/internal/core/local_runtime"
13- "github.com/langgenius/dify-plugin-daemon/internal/types/app"
14- "github.com/langgenius/dify-plugin-daemon/pkg/entities/manifest_entities"
15- "github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
4+ "reflect"
5+ "testing"
6+ "unsafe"
7+
8+ "github.com/google/uuid"
9+ "github.com/stretchr/testify/assert"
10+
11+ "github.com/langgenius/dify-plugin-daemon/internal/core/debugging_runtime"
12+ "github.com/langgenius/dify-plugin-daemon/internal/core/local_runtime"
13+ "github.com/langgenius/dify-plugin-daemon/internal/types/app"
14+ "github.com/langgenius/dify-plugin-daemon/pkg/entities/manifest_entities"
15+ "github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
1616)
1717
1818type mockNotifier struct {
19- connected bool
20- disconnected bool
19+ connected bool
20+ disconnected bool
2121}
2222
23- func (m * mockNotifier ) OnLocalRuntimeStarting (plugin_entities.PluginUniqueIdentifier ) {}
24- func (m * mockNotifier ) OnLocalRuntimeReady (* local_runtime.LocalPluginRuntime ) {}
23+ func (m * mockNotifier ) OnLocalRuntimeStarting (plugin_entities.PluginUniqueIdentifier ) {}
24+ func (m * mockNotifier ) OnLocalRuntimeReady (* local_runtime.LocalPluginRuntime ) {}
2525func (m * mockNotifier ) OnLocalRuntimeStartFailed (plugin_entities.PluginUniqueIdentifier , error ) {}
26- func (m * mockNotifier ) OnLocalRuntimeStop (* local_runtime.LocalPluginRuntime ) {}
27- func (m * mockNotifier ) OnLocalRuntimeStopped (* local_runtime.LocalPluginRuntime ) {}
28- func (m * mockNotifier ) OnLocalRuntimeScaleUp (* local_runtime.LocalPluginRuntime , int32 ) {}
29- func (m * mockNotifier ) OnLocalRuntimeScaleDown (* local_runtime.LocalPluginRuntime , int32 ) {}
30- func (m * mockNotifier ) OnLocalRuntimeInstanceLog (* local_runtime.LocalPluginRuntime , * local_runtime.PluginInstance , plugin_entities.PluginLogEvent ) {}
31- func (m * mockNotifier ) OnDebuggingRuntimeConnected (r * debugging_runtime.RemotePluginRuntime ) { m .connected = true }
32- func (m * mockNotifier ) OnDebuggingRuntimeDisconnected (r * debugging_runtime.RemotePluginRuntime ) { m .disconnected = true }
26+ func (m * mockNotifier ) OnLocalRuntimeStop (* local_runtime.LocalPluginRuntime ) {}
27+ func (m * mockNotifier ) OnLocalRuntimeStopped (* local_runtime.LocalPluginRuntime ) {}
28+ func (m * mockNotifier ) OnLocalRuntimeScaleUp (* local_runtime.LocalPluginRuntime , int32 ) {}
29+ func (m * mockNotifier ) OnLocalRuntimeScaleDown (* local_runtime.LocalPluginRuntime , int32 ) {}
30+ func (m * mockNotifier ) OnLocalRuntimeInstanceLog (* local_runtime.LocalPluginRuntime , * local_runtime.PluginInstance , plugin_entities.PluginLogEvent ) {
31+ }
32+ func (m * mockNotifier ) OnDebuggingRuntimeConnected (r * debugging_runtime.RemotePluginRuntime ) {
33+ m .connected = true
34+ }
35+ func (m * mockNotifier ) OnDebuggingRuntimeDisconnected (r * debugging_runtime.RemotePluginRuntime ) {
36+ m .disconnected = true
37+ }
3338
3439// setPrivateString sets an unexported string field on a struct value via unsafe reflection.
3540func setPrivateString (target any , field string , value string ) {
36- rv := reflect .ValueOf (target ).Elem ()
37- f := rv .FieldByName (field )
38- reflect .NewAt (f .Type (), unsafe .Pointer (f .UnsafeAddr ())).Elem ().SetString (value )
41+ rv := reflect .ValueOf (target ).Elem ()
42+ f := rv .FieldByName (field )
43+ reflect .NewAt (f .Type (), unsafe .Pointer (f .UnsafeAddr ())).Elem ().SetString (value )
3944}
4045
4146func newFakeRemoteRuntime (t * testing.T , name , version string ) * debugging_runtime.RemotePluginRuntime {
42- t .Helper ()
43-
44- r := & debugging_runtime.RemotePluginRuntime {
45- PluginRuntime : plugin_entities.PluginRuntime {
46- Config : plugin_entities.PluginDeclaration {
47- PluginDeclarationWithoutAdvancedFields : plugin_entities.PluginDeclarationWithoutAdvancedFields {
48- // Author is overwritten with tenantId inside Identity()
49- Name : name ,
50- Version : manifest_entities .Version (version ),
51- },
52- },
53- },
54- }
55-
56- // Provide required tenantId and checksum so Identity() succeeds
57- setPrivateString (r , "tenantId" , uuid .New ().String ())
58- setPrivateString (r , "checksum" , "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" )
59-
60- return r
47+ t .Helper ()
48+
49+ r := & debugging_runtime.RemotePluginRuntime {
50+ PluginRuntime : plugin_entities.PluginRuntime {
51+ Config : plugin_entities.PluginDeclaration {
52+ PluginDeclarationWithoutAdvancedFields : plugin_entities.PluginDeclarationWithoutAdvancedFields {
53+ // Author is overwritten with tenantId inside Identity()
54+ Name : name ,
55+ Version : manifest_entities .Version (version ),
56+ },
57+ },
58+ },
59+ }
60+
61+ // Provide required tenantId and checksum so Identity() succeeds
62+ setPrivateString (r , "tenantId" , uuid .New ().String ())
63+ setPrivateString (r , "checksum" , "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" )
64+ reflect .NewAt (
65+ reflect .ValueOf (r ).Elem ().FieldByName ("initialized" ).Type (),
66+ unsafe .Pointer (reflect .ValueOf (r ).Elem ().FieldByName ("initialized" ).UnsafeAddr ()),
67+ ).Elem ().SetBool (true )
68+
69+ return r
6170}
6271
6372func TestOnDebuggingRuntimeConnectedAndDisconnected (t * testing.T ) {
64- cp := NewControlPanel (& app.Config {PluginLocalLaunchingConcurrent : 1 }, nil , nil , nil , nil )
73+ cp := NewControlPanel (& app.Config {PluginLocalLaunchingConcurrent : 1 }, nil , nil , nil , nil )
6574
66- // Attach mock notifier to observe callbacks
67- mn := & mockNotifier {}
68- cp .AddNotifier (mn )
75+ // Attach mock notifier to observe callbacks
76+ mn := & mockNotifier {}
77+ cp .AddNotifier (mn )
6978
70- r := newFakeRemoteRuntime (t , "conn_test" , "1.2.3" )
79+ r := newFakeRemoteRuntime (t , "conn_test" , "1.2.3" )
7180
72- // Connected path
73- err := cp .onDebuggingRuntimeConnected (r )
74- assert .NoError (t , err )
81+ // Connected path
82+ err := cp .onDebuggingRuntimeConnected (r )
83+ assert .NoError (t , err )
7584
76- id , err := r .Identity ()
77- assert .NoError (t , err )
85+ id , err := r .Identity ()
86+ assert .NoError (t , err )
7887
79- // Stored in runtime map and notifier called
80- _ , ok := cp .debuggingPluginRuntime .Load (id )
81- assert .True (t , ok , "runtime should be stored on connect" )
82- assert .True (t , mn .connected , "connected notifier should be triggered" )
88+ // Stored in runtime map and notifier called
89+ _ , ok := cp .debuggingPluginRuntime .Load (id )
90+ assert .True (t , ok , "runtime should be stored on connect" )
91+ assert .True (t , mn .connected , "connected notifier should be triggered" )
8392
84- // Disconnected path
85- cp .onDebuggingRuntimeDisconnected (r )
93+ // Disconnected path
94+ cp .onDebuggingRuntimeDisconnected (r )
95+
96+ _ , ok = cp .debuggingPluginRuntime .Load (id )
97+ assert .False (t , ok , "runtime should be removed on disconnect" )
98+ assert .True (t , mn .disconnected , "disconnected notifier should be triggered" )
99+ }
86100
87- _ , ok = cp .debuggingPluginRuntime .Load (id )
88- assert .False (t , ok , "runtime should be removed on disconnect" )
89- assert .True (t , mn .disconnected , "disconnected notifier should be triggered" )
90- }
101+ func TestOnDebuggingRuntimeDisconnected_IgnoresUninitializedRuntime (t * testing.T ) {
102+ cp := NewControlPanel (& app.Config {PluginLocalLaunchingConcurrent : 1 }, nil , nil , nil , nil )
103+
104+ mn := & mockNotifier {}
105+ cp .AddNotifier (mn )
106+
107+ r := & debugging_runtime.RemotePluginRuntime {}
108+
109+ assert .NotPanics (t , func () {
110+ cp .onDebuggingRuntimeDisconnected (r )
111+ })
112+ assert .False (t , mn .disconnected , "disconnected notifier should not be triggered for uninitialized runtime" )
113+ }
0 commit comments