@@ -222,6 +222,84 @@ mod tests {
222222 assert_eq ! ( config[ "labels" ] [ "rolegroup" ] , "default" ) ;
223223 }
224224
225+ #[ test]
226+ fn applies_json_patch ( ) {
227+ let config = config_json_for ( json ! ( {
228+ "image" : { "productVersion" : "1.2.3" } ,
229+ "servers" : {
230+ "configOverrides" : {
231+ "config.json" : { "jsonPatch" : [
232+ {
233+ "op" : "replace" ,
234+ "path" : "/bundles/stackable/polling/min_delay_seconds" ,
235+ "value" : 1 ,
236+ } ,
237+ { "op" : "add" , "path" : "/default_decision" , "value" : "test/allow" } ,
238+ ] }
239+ } ,
240+ "roleGroups" : { "default" : { } } ,
241+ } ,
242+ } ) ) ;
243+
244+ // The `replace` operation edits an existing value.
245+ assert_eq ! (
246+ config[ "bundles" ] [ "stackable" ] [ "polling" ] [ "min_delay_seconds" ] ,
247+ 1
248+ ) ;
249+ // The `add` operation introduces a new key.
250+ assert_eq ! ( config[ "default_decision" ] , "test/allow" ) ;
251+ }
252+
253+ #[ test]
254+ fn skips_invalid_json_patch ( ) {
255+ // The `remove` targets a path that does not exist, so the whole patch fails to apply. The
256+ // error is logged and skipped, leaving the base config untouched (the earlier `replace`
257+ // operation is rolled back as well).
258+ let config = config_json_for ( json ! ( {
259+ "image" : { "productVersion" : "1.2.3" } ,
260+ "servers" : {
261+ "configOverrides" : {
262+ "config.json" : { "jsonPatch" : [
263+ {
264+ "op" : "replace" ,
265+ "path" : "/bundles/stackable/polling/min_delay_seconds" ,
266+ "value" : 1 ,
267+ } ,
268+ { "op" : "remove" , "path" : "/does/not/exist" } ,
269+ ] }
270+ } ,
271+ "roleGroups" : { "default" : { } } ,
272+ } ,
273+ } ) ) ;
274+
275+ // The default is preserved because the invalid patch is skipped as a whole.
276+ assert_eq ! (
277+ config[ "bundles" ] [ "stackable" ] [ "polling" ] [ "min_delay_seconds" ] ,
278+ 10
279+ ) ;
280+ }
281+
282+ #[ test]
283+ fn user_provided_replaces_entire_config ( ) {
284+ let config = config_json_for ( json ! ( {
285+ "image" : { "productVersion" : "1.2.3" } ,
286+ "servers" : {
287+ "configOverrides" : {
288+ "config.json" : { "userProvided" : {
289+ "services" : [ { "name" : "custom" , "url" : "http://example.com/opa/v1" } ] ,
290+ } }
291+ } ,
292+ "roleGroups" : { "default" : { } } ,
293+ } ,
294+ } ) ) ;
295+
296+ // The user-provided document replaces the operator defaults entirely.
297+ assert_eq ! ( config[ "services" ] [ 0 ] [ "name" ] , "custom" ) ;
298+ // Operator defaults such as the bundle config and prometheus status are gone.
299+ assert ! ( config. get( "bundles" ) . is_none_or( Value :: is_null) ) ;
300+ assert ! ( config. get( "status" ) . is_none_or( Value :: is_null) ) ;
301+ }
302+
225303 #[ test]
226304 fn decision_logs_follow_decision_logger_level ( ) {
227305 // Decision logging is enabled when the `decision` logger is set above `NONE`.
0 commit comments