@@ -245,6 +245,150 @@ func TestHandlePatch_RejectsWhenOverMaxEgressRules(t *testing.T) {
245245 require .Len (t , proxy .updated .Egress , 2 , "policy should be unchanged" )
246246}
247247
248+ func TestHandleDelete_RemovesMatchingTargets (t * testing.T ) {
249+ initial := & policy.NetworkPolicy {
250+ DefaultAction : policy .ActionDeny ,
251+ Egress : []policy.EgressRule {
252+ {Action : policy .ActionAllow , Target : "example.com" },
253+ {Action : policy .ActionDeny , Target : "blocked.com" },
254+ {Action : policy .ActionAllow , Target : "keep.com" },
255+ },
256+ }
257+ proxy := & stubProxy {updated : initial }
258+ nft := & stubNft {}
259+ srv := & policyServer {proxy : proxy , nft : nft , enforcementMode : "dns+nft" }
260+
261+ body := `["blocked.com","nonexistent.com"]`
262+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader (body ))
263+ w := httptest .NewRecorder ()
264+
265+ srv .handlePolicy (w , req )
266+
267+ resp := w .Result ()
268+ require .Equal (t , http .StatusOK , resp .StatusCode , "expected 200 OK" )
269+ require .Equal (t , 1 , nft .calls , "expected nft ApplyStatic called once" )
270+ require .NotNil (t , proxy .updated , "expected proxy policy updated" )
271+ require .Equal (t , policy .ActionDeny , proxy .updated .DefaultAction , "defaultAction should be preserved" )
272+ require .Len (t , proxy .updated .Egress , 2 , "expected 2 rules remaining after delete" )
273+ require .Equal (t , policy .ActionAllow , proxy .updated .Egress [0 ].Action )
274+ require .Equal (t , "example.com" , proxy .updated .Egress [0 ].Target )
275+ require .Equal (t , policy .ActionAllow , proxy .updated .Egress [1 ].Action )
276+ require .Equal (t , "keep.com" , proxy .updated .Egress [1 ].Target )
277+ }
278+
279+ func TestHandleDelete_CaseInsensitiveMatch (t * testing.T ) {
280+ initial := & policy.NetworkPolicy {
281+ DefaultAction : policy .ActionDeny ,
282+ Egress : []policy.EgressRule {
283+ {Action : policy .ActionAllow , Target : "Example.COM" },
284+ {Action : policy .ActionDeny , Target : "Blocked.COM" },
285+ },
286+ }
287+ proxy := & stubProxy {updated : initial }
288+ nft := & stubNft {}
289+ srv := & policyServer {proxy : proxy , nft : nft , enforcementMode : "dns+nft" }
290+
291+ body := `["example.com"]`
292+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader (body ))
293+ w := httptest .NewRecorder ()
294+
295+ srv .handlePolicy (w , req )
296+
297+ resp := w .Result ()
298+ require .Equal (t , http .StatusOK , resp .StatusCode , "expected 200 OK" )
299+ require .NotNil (t , proxy .updated )
300+ require .Len (t , proxy .updated .Egress , 1 , "expected 1 rule remaining" )
301+ require .Equal (t , "Blocked.COM" , proxy .updated .Egress [0 ].Target , "unmatched rule should remain" )
302+ }
303+
304+ func TestHandleDelete_NoMatchReturns200 (t * testing.T ) {
305+ initial := & policy.NetworkPolicy {
306+ DefaultAction : policy .ActionDeny ,
307+ Egress : []policy.EgressRule {
308+ {Action : policy .ActionAllow , Target : "keep.com" },
309+ },
310+ }
311+ proxy := & stubProxy {updated : initial }
312+ nft := & stubNft {}
313+ srv := & policyServer {proxy : proxy , nft : nft , enforcementMode : "dns+nft" }
314+
315+ body := `["nonexistent.com"]`
316+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader (body ))
317+ w := httptest .NewRecorder ()
318+
319+ srv .handlePolicy (w , req )
320+
321+ resp := w .Result ()
322+ require .Equal (t , http .StatusOK , resp .StatusCode , "expected 200 OK even when no targets match" )
323+ require .Equal (t , 0 , nft .calls , "nft should not be called when nothing changes" )
324+ require .Len (t , proxy .updated .Egress , 1 , "policy should be unchanged" )
325+ }
326+
327+ func TestHandleDelete_EmptyBodyReturns400 (t * testing.T ) {
328+ proxy := & stubProxy {updated : policy .DefaultDenyPolicy ()}
329+ srv := & policyServer {proxy : proxy , nft : nil , enforcementMode : "dns" }
330+
331+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader ("" ))
332+ w := httptest .NewRecorder ()
333+
334+ srv .handlePolicy (w , req )
335+
336+ resp := w .Result ()
337+ require .Equal (t , http .StatusBadRequest , resp .StatusCode , "expected 400 for empty body" )
338+ }
339+
340+ func TestHandleDelete_EmptyArrayReturns400 (t * testing.T ) {
341+ proxy := & stubProxy {updated : policy .DefaultDenyPolicy ()}
342+ srv := & policyServer {proxy : proxy , nft : nil , enforcementMode : "dns" }
343+
344+ body := `[]`
345+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader (body ))
346+ w := httptest .NewRecorder ()
347+
348+ srv .handlePolicy (w , req )
349+
350+ resp := w .Result ()
351+ require .Equal (t , http .StatusBadRequest , resp .StatusCode , "expected 400 for empty array" )
352+ }
353+
354+ func TestHandleDelete_InvalidJSONReturns400 (t * testing.T ) {
355+ proxy := & stubProxy {updated : policy .DefaultDenyPolicy ()}
356+ srv := & policyServer {proxy : proxy , nft : nil , enforcementMode : "dns" }
357+
358+ body := `not-json`
359+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader (body ))
360+ w := httptest .NewRecorder ()
361+
362+ srv .handlePolicy (w , req )
363+
364+ resp := w .Result ()
365+ require .Equal (t , http .StatusBadRequest , resp .StatusCode , "expected 400 for invalid JSON" )
366+ }
367+
368+ func TestHandleDelete_NftFailureReturns500 (t * testing.T ) {
369+ initial := & policy.NetworkPolicy {
370+ DefaultAction : policy .ActionDeny ,
371+ Egress : []policy.EgressRule {
372+ {Action : policy .ActionAllow , Target : "example.com" },
373+ },
374+ }
375+ proxy := & stubProxy {updated : initial }
376+ nft := & stubNft {err : errors .New ("nft apply failed" )}
377+ srv := & policyServer {proxy : proxy , nft : nft , enforcementMode : "dns+nft" }
378+
379+ body := `["example.com"]`
380+ req := httptest .NewRequest (http .MethodDelete , "/policy" , strings .NewReader (body ))
381+ w := httptest .NewRecorder ()
382+
383+ srv .handlePolicy (w , req )
384+
385+ resp := w .Result ()
386+ require .Equal (t , http .StatusInternalServerError , resp .StatusCode , "expected 500 on nft failure" )
387+ require .Equal (t , 1 , nft .calls , "expected nft ApplyStatic called once" )
388+ require .Len (t , proxy .updated .Egress , 1 , "proxy should not be updated on nft failure" )
389+ require .Equal (t , "example.com" , proxy .updated .Egress [0 ].Target , "original rule should remain" )
390+ }
391+
248392func TestHandlePost_RejectsWhenOverMaxEgressRules (t * testing.T ) {
249393 proxy := & stubProxy {}
250394 nft := & stubNft {}
0 commit comments