@@ -51,12 +51,15 @@ func TestGetNaiveFlags(t *testing.T) {
5151
5252func TestPluginMatch (t * testing.T ) {
5353 testCases := []struct {
54- commandString string
55- pluginConfig map [string ]string
56- expectedMatch string
57- expectedOk bool
54+ doc string
55+ commandString string
56+ pluginConfig map [string ]string
57+ cmdErrorMessage string
58+ expectedMatch string
59+ expectedOk bool
5860 }{
5961 {
62+ doc : "hooks prefix match" ,
6063 commandString : "image ls" ,
6164 pluginConfig : map [string ]string {
6265 "hooks" : "image" ,
@@ -65,6 +68,7 @@ func TestPluginMatch(t *testing.T) {
6568 expectedOk : true ,
6669 },
6770 {
71+ doc : "hooks no match" ,
6872 commandString : "context ls" ,
6973 pluginConfig : map [string ]string {
7074 "hooks" : "build" ,
@@ -73,6 +77,7 @@ func TestPluginMatch(t *testing.T) {
7377 expectedOk : false ,
7478 },
7579 {
80+ doc : "hooks exact match" ,
7681 commandString : "context ls" ,
7782 pluginConfig : map [string ]string {
7883 "hooks" : "context ls" ,
@@ -81,6 +86,7 @@ func TestPluginMatch(t *testing.T) {
8186 expectedOk : true ,
8287 },
8388 {
89+ doc : "hooks first match wins" ,
8490 commandString : "image ls" ,
8591 pluginConfig : map [string ]string {
8692 "hooks" : "image ls,image" ,
@@ -89,6 +95,7 @@ func TestPluginMatch(t *testing.T) {
8995 expectedOk : true ,
9096 },
9197 {
98+ doc : "hooks empty string" ,
9299 commandString : "image ls" ,
93100 pluginConfig : map [string ]string {
94101 "hooks" : "" ,
@@ -97,6 +104,7 @@ func TestPluginMatch(t *testing.T) {
97104 expectedOk : false ,
98105 },
99106 {
107+ doc : "hooks partial token no match" ,
100108 commandString : "image inspect" ,
101109 pluginConfig : map [string ]string {
102110 "hooks" : "image i" ,
@@ -105,19 +113,148 @@ func TestPluginMatch(t *testing.T) {
105113 expectedOk : false ,
106114 },
107115 {
116+ doc : "hooks prefix token match" ,
108117 commandString : "image inspect" ,
109118 pluginConfig : map [string ]string {
110119 "hooks" : "image" ,
111120 },
112121 expectedMatch : "image" ,
113122 expectedOk : true ,
114123 },
124+ {
125+ doc : "error-hooks match on error" ,
126+ commandString : "build" ,
127+ pluginConfig : map [string ]string {
128+ "error-hooks" : "build" ,
129+ },
130+ cmdErrorMessage : "exit status 1" ,
131+ expectedMatch : "build" ,
132+ expectedOk : true ,
133+ },
134+ {
135+ doc : "error-hooks no match on success" ,
136+ commandString : "build" ,
137+ pluginConfig : map [string ]string {
138+ "error-hooks" : "build" ,
139+ },
140+ cmdErrorMessage : "" ,
141+ expectedMatch : "" ,
142+ expectedOk : false ,
143+ },
144+ {
145+ doc : "error-hooks prefix match on error" ,
146+ commandString : "compose up" ,
147+ pluginConfig : map [string ]string {
148+ "error-hooks" : "compose" ,
149+ },
150+ cmdErrorMessage : "exit status 1" ,
151+ expectedMatch : "compose" ,
152+ expectedOk : true ,
153+ },
154+ {
155+ doc : "error-hooks no match for wrong command" ,
156+ commandString : "pull" ,
157+ pluginConfig : map [string ]string {
158+ "error-hooks" : "build" ,
159+ },
160+ cmdErrorMessage : "exit status 1" ,
161+ expectedMatch : "" ,
162+ expectedOk : false ,
163+ },
164+ {
165+ doc : "hooks takes precedence over error-hooks" ,
166+ commandString : "build" ,
167+ pluginConfig : map [string ]string {
168+ "hooks" : "build" ,
169+ "error-hooks" : "build" ,
170+ },
171+ cmdErrorMessage : "exit status 1" ,
172+ expectedMatch : "build" ,
173+ expectedOk : true ,
174+ },
175+ {
176+ doc : "hooks fires on success even with error-hooks configured" ,
177+ commandString : "build" ,
178+ pluginConfig : map [string ]string {
179+ "hooks" : "build" ,
180+ "error-hooks" : "build" ,
181+ },
182+ cmdErrorMessage : "" ,
183+ expectedMatch : "build" ,
184+ expectedOk : true ,
185+ },
186+ {
187+ doc : "error-hooks with multiple commands" ,
188+ commandString : "compose up" ,
189+ pluginConfig : map [string ]string {
190+ "error-hooks" : "build,compose up,pull" ,
191+ },
192+ cmdErrorMessage : "exit status 1" ,
193+ expectedMatch : "compose up" ,
194+ expectedOk : true ,
195+ },
196+ }
197+
198+ for _ , tc := range testCases {
199+ t .Run (tc .doc , func (t * testing.T ) {
200+ match , ok := pluginMatch (tc .pluginConfig , tc .commandString , tc .cmdErrorMessage )
201+ assert .Equal (t , ok , tc .expectedOk )
202+ assert .Equal (t , match , tc .expectedMatch )
203+ })
204+ }
205+ }
206+
207+ func TestMatchHookConfig (t * testing.T ) {
208+ testCases := []struct {
209+ doc string
210+ configuredHooks string
211+ subCmd string
212+ expectedMatch string
213+ expectedOk bool
214+ }{
215+ {
216+ doc : "empty config" ,
217+ configuredHooks : "" ,
218+ subCmd : "build" ,
219+ expectedMatch : "" ,
220+ expectedOk : false ,
221+ },
222+ {
223+ doc : "exact match" ,
224+ configuredHooks : "build" ,
225+ subCmd : "build" ,
226+ expectedMatch : "build" ,
227+ expectedOk : true ,
228+ },
229+ {
230+ doc : "prefix match" ,
231+ configuredHooks : "image" ,
232+ subCmd : "image ls" ,
233+ expectedMatch : "image" ,
234+ expectedOk : true ,
235+ },
236+ {
237+ doc : "comma-separated match" ,
238+ configuredHooks : "pull,build,push" ,
239+ subCmd : "build" ,
240+ expectedMatch : "build" ,
241+ expectedOk : true ,
242+ },
243+ {
244+ doc : "no match" ,
245+ configuredHooks : "pull,push" ,
246+ subCmd : "build" ,
247+ expectedMatch : "" ,
248+ expectedOk : false ,
249+ },
115250 }
116251
117252 for _ , tc := range testCases {
118- match , ok := pluginMatch (tc .pluginConfig , tc .commandString )
119- assert .Equal (t , ok , tc .expectedOk )
120- assert .Equal (t , match , tc .expectedMatch )
253+ t .Run (tc .doc , func (t * testing.T ) {
254+ match , ok := matchHookConfig (tc .configuredHooks , tc .subCmd )
255+ assert .Equal (t , ok , tc .expectedOk )
256+ assert .Equal (t , match , tc .expectedMatch )
257+ })
121258 }
122259}
123260
@@ -170,21 +307,37 @@ func TestRunPluginHooksPassesErrorMessage(t *testing.T) {
170307 RunPluginHooks (context .Background (), provider , root , sub , []string {"build" }, "exit status 1" )
171308}
172309
173- func TestInvokeAndCollectHooksForwardsErrorMessage (t * testing.T ) {
310+ func TestRunPluginHooksErrorHooks (t * testing.T ) {
174311 cfg := configfile .New ("" )
175312 cfg .Plugins = map [string ]map [string ]string {
176- "nonexistent " : {"hooks" : "build" },
313+ "test-plugin " : {"error- hooks" : "build" },
177314 }
315+ provider := & fakeConfigProvider {cfg : cfg }
178316 root := & cobra.Command {Use : "docker" }
179317 sub := & cobra.Command {Use : "build" }
180318 root .AddCommand (sub )
181319
182- // Plugin binary doesn't exist — invokeAndCollectHooks skips it
183- // gracefully and returns empty. Verifies the error message path
184- // doesn't cause issues when forwarded through the call chain.
320+ // Should not panic — error-hooks with error message
321+ RunPluginHooks (context .Background (), provider , root , sub , []string {"build" }, "exit status 1" )
322+
323+ // Should not panic — error-hooks with no error (should be skipped)
324+ RunPluginHooks (context .Background (), provider , root , sub , []string {"build" }, "" )
325+ }
326+
327+ func TestInvokeAndCollectHooksErrorHooksSkippedOnSuccess (t * testing.T ) {
328+ cfg := configfile .New ("" )
329+ cfg .Plugins = map [string ]map [string ]string {
330+ "nonexistent" : {"error-hooks" : "build" },
331+ }
332+ root := & cobra.Command {Use : "docker" }
333+ sub := & cobra.Command {Use : "build" }
334+ root .AddCommand (sub )
335+
336+ // On success, error-hooks should not match, so the plugin
337+ // binary is never looked up and no results are returned.
185338 result := invokeAndCollectHooks (
186339 context .Background (), cfg , root , sub ,
187- "build" , map [string ]string {}, "exit status 1 " ,
340+ "build" , map [string ]string {}, "" ,
188341 )
189342 assert .Check (t , is .Len (result , 0 ))
190343}
0 commit comments