quarkiverse / quarkus-operator-sdk

Quarkus Extension to create Kubernetes Operators in Java using the Java Operator SDK (https://github.com/java-operator-sdk/java-operator-sdk) project
Apache License 2.0
118 stars 50 forks source link

Example for QuarkusTest #191

Closed speedfl closed 1 year ago

speedfl commented 2 years ago

Hello! Do you have an example with quarkus tests such as:

https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java

Thanks a lot!

Regards

scrocquesel commented 2 years ago

Hi, I quickly set up a test https://github.com/scrocquesel/quarkus-operator-sdk/commit/6a4ca1a12e657f7fd449e792a2e210f8d6e6a6eb

@metacosm do you want me to make a PR ?

metacosm commented 2 years ago

@scrocquesel yes, it might make sense to add such a test indeed. Thank you!

scrocquesel commented 2 years ago

@metacosm do you think it could be possible to only have the reconciler as SUT and unit test the reconcile call ?

Doing integration test with external services could be difficult because mock are not that much dynamic and reaching a stable status for the resource can not be possible all the case. For eg, if the reconcile loop should call a rest client to create an external HTTP resource, it may call a GET to check existence and the POST. Mocking the GET to always return false, is possible but then integration test may loop indefinitely creating something that doesn't exist.

What I would want is to Inject the reconciler in the test class without running the application, like

@WithKubernetesTestServer
@QuarkusTest
public class JokeReconcilerTest {

    @InjectMock
    @RestClient
    JokeService jokeService;

    @KubernetesTestServer
    KubernetesServer mockServer;

    @Inject
    JokeReconciler sut;

    @Test
    void CanReconcile() {

        // arange
        final JokeModel joke = new JokeModel();
        joke.id = 1;
        joke.joke = "Hello";
        joke.flags = Map.of();

        Mockito.when(jokeService.getRandom(eq(Category.Any), anyString(), anyBoolean(), anyString())).thenReturn(joke);

        final JokeRequest testRequest = new JokeRequest();
        testRequest.setMetadata(new ObjectMetaBuilder()
                .withName("myjoke1")
                .withNamespace(mockServer.getClient().getNamespace())
                .build());
        testRequest.setSpec(new JokeRequestSpec());
        testRequest.getSpec().setCategory(Category.Any);

        // act
        var result = sut.reconcile(testRequest, new DefaultContext(null, null, null));

        // assert on result
        assertThat(result.getResource().getStatus().getState(), equalTo(State.CREATED));
        assertFalse(result.getScheduleDelay().isPresent());
    }
}
metacosm commented 2 years ago

When you write "without running the application", do you mean without starting the operator?

scrocquesel commented 2 years ago

Yes, the code i wrote works but the main is run.

I understand that @QuarkusTest launch the app aside, but if we remove it, we lose all CDI behavior, Mockito integration, ...

metacosm commented 2 years ago

What's the issue with running the main method? Not sure I understand what you're trying to achieve…

scrocquesel commented 2 years ago

Testing with PerResourcePollingEventSource for eg could be very difficult and we can't afford having unit tests waiting for time based event to occured. In this case, we can set up the context and call reconcile.

metacosm commented 2 years ago

Yes, but you can already call reconcile directly, no?

The only thing would be that we need to provide a TestContext instead of relying on DefaultContext (which probably shouldn't be public). An easily configurable test context could be provided as part of the SDK junit extension.

scrocquesel commented 2 years ago

Yes, but you can already call reconcile directly, no?

The only thing would be that we need to provide a TestContext instead of relying on DefaultContext (which probably shouldn't be public). An easily configurable test context could be provided as part of the SDK junit extension.

Yes, that's true but there is a running instance of the operator and in some circonstances as it use the same kubernetes mock server, it can lead to concurrency issues.

metacosm commented 2 years ago

Could you elaborate on how concurrency issues would occur? If nothing else interacts with the mock server during the test, the reconciliation shouldn't be triggered so I'm not sure I'm understanding what the issue is (but then again, I'm starting to get tired so 🤷🏼 😅)

metacosm commented 2 years ago

I created a PR with the alternative test style: #198.

scrocquesel commented 2 years ago

Could you elaborate on how concurrency issues would occur? If nothing else interacts with the mock server during the test, the reconciliation shouldn't be triggered so I'm not sure I'm understanding what the issue is (but then again, I'm starting to get tired so 🤷🏼 😅)

I am maybe thinking too much. So, let's try with something else.

Let's say we port the mysql schema operator from JOSDK to QuarkusTest. It has a PerResourcePollingEventSource that needs to connect to a database to get secondary resource state.

For a unit test that only call reconcile, the context is mocked and we don't need the event source to actually run. But the test will fails, because it will tries to run the registered event source and it cannot connect to a non existent database.

If we were doing that with the Tomcat sample, it has an Informer that will trigger the reconcile on the background app when the unit test call reconcile will actually create the secondary ressource on the shared mock kubernetes instance.

metacosm commented 2 years ago

I see. I should indeed try to port some more complex example to the extension.

scrocquesel commented 2 years ago

I see. I should indeed try to port some more complex example to the extension.

I was in the middle of something https://github.com/scrocquesel/quarkus-operator-sdk/tree/test/samples

metacosm commented 2 years ago

Do you think you can finish that effort?

scrocquesel commented 2 years ago

May be closed, samples are now fully tested, there is ongoing work for a test resource and operator can now be start/stop on demand.