### Question:
ExposedThingAction extends ThingAction<I, O> { public CompletableFuture<O> invoke() { return invoke(null); } ExposedThingAction(String name, ThingAction<I, O> action, ExposedThing thing); @SuppressWarnings("squid:S107")  ExposedThingAction(String name,
                       ExposedThing thing,
                       ActionState<I, O> state,
                       String description,
                       Map<String, String> descriptions,
                       Map<String, Map<String, Object>> uriVariables,
                       DataSchema input,
                       DataSchema output); CompletableFuture<O> invoke(); CompletableFuture<O> invoke(I input); CompletableFuture<O> invoke(I input, Map<String, Map<String, Object>> options); ActionState<I, O> getState(); @Override int hashCode(); @Override boolean equals(Object o); @Override String toString();  }### Answer:
@Test public void invokeWithoutHandlerShouldReturnNullFuture() throws ExecutionException, InterruptedException { ExposedThingAction<Object, Object> exposedThingAction = new ExposedThingAction<Object, Object>("myAction", thing, state, description, Map.of(), Map.of(), input, output); exposedThingAction.invoke(invokeInput, invokeOptions); assertNull(exposedThingAction.invoke(invokeInput, invokeOptions).get()); }
@Test public void invokeWithHandlerShouldCallHandler() { when(state.getHandler()).thenReturn(handler); ExposedThingAction<Object, Object> exposedThingAction = new ExposedThingAction<Object, Object>("myAction", thing, state, description, Map.of(), Map.of(), input, output); exposedThingAction.invoke(invokeInput, invokeOptions); verify(handler).apply(invokeInput, invokeOptions); }
@Test(expected = ExecutionException.class) public void invokeWithBrokenHandlerShouldReturnFailedFuture() throws ExecutionException, InterruptedException { when(handler.apply(any(), any())).thenThrow(new RuntimeException()); when(state.getHandler()).thenReturn(handler); ExposedThingAction<Object, Object> exposedThingAction = new ExposedThingAction<Object, Object>("myAction", thing, state, description, Map.of(), Map.of(), input, output); exposedThingAction.invoke(invokeInput, invokeOptions).get(); verify(handler).apply(invokeInput, invokeOptions); }
@Test public void invokeShouldWrapNullResultsFromHandlerIntoFuture() { when(state.getHandler()).thenReturn(handler); when(handler.apply(any(), any())).thenReturn(null); ExposedThingAction<Object, Object> exposedThingAction = new ExposedThingAction<Object, Object>("myAction", thing, state, description, Map.of(), Map.of(), input, output); assertNotNull(exposedThingAction.invoke()); }