@@ -201,4 +201,71 @@ void GIVEN_evicted_autostart_builtin_WHEN_main_readds_it_from_topic_THEN_it_gets
201201 assertEquals (Collections .emptySet (), otherService .getDependencies ().keySet ());
202202 }
203203 }
204+
205+ @ Test
206+ void GIVEN_default_dependency_never_listed_in_topic_WHEN_topic_omits_it_THEN_it_is_retained ()
207+ throws Exception {
208+ try (Context realContext = new Context ()) {
209+ Configuration realConfig = new Configuration (realContext );
210+ Kernel kernel = mock (Kernel .class );
211+ realContext .put (Kernel .class , kernel );
212+
213+ GreengrassService main = new GreengrassService (
214+ realConfig .lookupTopics (SERVICES_NAMESPACE_TOPIC , "main" ));
215+ GreengrassService builtin = new GreengrassService (
216+ realConfig .lookupTopics (SERVICES_NAMESPACE_TOPIC , "builtinService" ));
217+ GreengrassService explicitDep = new GreengrassService (
218+ realConfig .lookupTopics (SERVICES_NAMESPACE_TOPIC , "explicitService" ));
219+ when (kernel .locateIgnoreError ("explicitService" )).thenReturn (explicitDep );
220+
221+ // Launch-style injection; the builtin never appears in the dependencies topic
222+ main .addOrUpdateDependency (builtin , DependencyType .HARD , true );
223+
224+ // A topic write which omits the default dependency must not remove it, and must not
225+ // touch its default flag
226+ realConfig .lookup (SERVICES_NAMESPACE_TOPIC , "main" , SERVICE_DEPENDENCIES_NAMESPACE_TOPIC )
227+ .withValue (new ArrayList <>(Collections .singletonList ("explicitService" )));
228+ realContext .waitForPublishQueueToClear ();
229+
230+ assertEquals (new HashSet <>(Arrays .asList (builtin , explicitDep )),
231+ main .getDependencies ().keySet ());
232+ assertTrue (main .dependencies .get (builtin ).isDefaultDependency ,
233+ "never-listed default dependency must keep its default flag" );
234+ }
235+ }
236+
237+ @ Test
238+ void GIVEN_evicted_dependency_WHEN_readded_as_default_THEN_protection_restored () throws Exception {
239+ try (Context realContext = new Context ()) {
240+ Configuration realConfig = new Configuration (realContext );
241+ Kernel kernel = mock (Kernel .class );
242+ realContext .put (Kernel .class , kernel );
243+
244+ GreengrassService main = new GreengrassService (
245+ realConfig .lookupTopics (SERVICES_NAMESPACE_TOPIC , "main" ));
246+ GreengrassService dep = new GreengrassService (
247+ realConfig .lookupTopics (SERVICES_NAMESPACE_TOPIC , "someService" ));
248+ when (kernel .locateIgnoreError ("someService" )).thenReturn (dep );
249+
250+ // A plain (non-default, non-builtin-named) dependency is added from the topic, then
251+ // evicted by a topic update omitting it
252+ Topic dependenciesTopic = realConfig .lookup (SERVICES_NAMESPACE_TOPIC , "main" ,
253+ SERVICE_DEPENDENCIES_NAMESPACE_TOPIC );
254+ dependenciesTopic .withValue (new ArrayList <>(Collections .singletonList ("someService" )));
255+ realContext .waitForPublishQueueToClear ();
256+ dependenciesTopic .withValue (new ArrayList <>());
257+ realContext .waitForPublishQueueToClear ();
258+ assertEquals (Collections .emptySet (), main .getDependencies ().keySet ());
259+
260+ // Re-injection with isDefault=true (what launch() does at every boot) must restore the
261+ // dependency with default protection, so a later topic omission no longer removes it
262+ main .addOrUpdateDependency (dep , DependencyType .HARD , true );
263+ assertTrue (main .dependencies .get (dep ).isDefaultDependency ,
264+ "re-injection must restore default protection" );
265+
266+ dependenciesTopic .withValue (new ArrayList <>());
267+ realContext .waitForPublishQueueToClear ();
268+ assertEquals (new HashSet <>(Collections .singletonList (dep )), main .getDependencies ().keySet ());
269+ }
270+ }
204271}
0 commit comments