Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert main integration tests into reusable blackbox tests #590

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

dimas-b
Copy link
Contributor

@dimas-b dimas-b commented Dec 26, 2024

  • Introduce new module: integration-tests to contain the test code for backbox integration tests

  • Refactors existing main integration tests to avoid relying on Dropwizard internals.

  • Add new test extension for managing the test environment with custom server managers discovered via ServiceLoader

  • Use Dropwizard test extentions for the reference implementation of PolarisServerManager. Custom server builds can provide their own environment-specific implementations.

  • Add helper classes for REST API (Management and Catalog)

  • Run reusable tests by extending them in the DW module. Using test suites is also possible, but it only uses one gradle test worker, so test parallelism is reduced. At the same time running tests in parallel withing the same JVM using JUnit5 threads currently leads to errors.


This PR is meant to be the first step towards #524. Subsequent PRs can certainly enhance the testing framework more.

gradle/libs.versions.toml Outdated Show resolved Hide resolved
this.realm = realm;
this.server = server;

ObjectMapper mapper = new ObjectMapper();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we create this client outside of the extension? In Quarkus, it would be better to inject the ObjectMapper that's used server-side, but it won't be available for injection here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, this is for blackbox testing only. My bad.

Copy link
Contributor

@adutra adutra Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unresolving this conversation because it turns out the Server impl will need access to an ObjectMapper. We can't access the one created by Quarkus because this is a black box test. But we may be able to share the object mapper created here with the server impl. This is minor though, for now my Quarkus server impl is simply creating another ObjectMapper identical to this one.


@Override
public URI baseUri() {
return URI.create(String.format("http://localhost:%d", ext.getLocalPort()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old TestEnv extension could modify this to hit a remote Polaris server afaict.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was that remote cases would have their own Server Manager impl.

The old TestEnvironmentExtension is too intertwined with existing whitebox DW extensions... I though we'd have a totally new interface and gradually migrate TestEnvironmentExtension use cases to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: if a remote server is available outside the JUnit5 lifecycle, one can simply make an Server Manager impl. that returned a pre-configured URI and Realm, and then use a JUnit5 "suite" to execute tests from the new test module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the proposal for remote tests to implement their own PolarisServerManager. Otherwise, the concern for one use case ends up cluttering the code for the other use case.


@Override
public ClientCredentials adminCredentials() {
return new ClientCredentials("test-admin", "test-secret");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old TestEnv extension used to append a "test ID" to the generated credentials afaict.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that the purpose of that append was, TBH 😅 I believe having one admin user per realm (as in this case) is sufficient.

Currently, there's only one realm for Server Manager instance, so other implementations can chose to use unique admin user names, if they prefer.


AuthToken adminToken();

ClientCredentials adminCredentials();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where has the Snowman user gone?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Into the Nothing. The Server Manager provides "root" (admin) credentials. Tests create extra principals when they need to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with that, although my impression was that the snowman user was introduced precisely to avoid using the root user for all admin tasks. Let's wait and see what @andrew4699 says.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the work done by the SnowmanCredentialsExtension into specific test classes... hopefully maintaining the same functionality as before (the "snowman" principals used to be created using "admin" tokens too). Those can be found by searching for "snowman" in Principal names in the new test code. I believe explicit principal manipulation by test code is easier to follow and maintain than if it is done by a test extension.

The principal and role manipulation logic moved to the methods in the new ManagementApi class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention of the SnowmanExtension was simply to have a non-root user easily available so that all the tests didn't just run everything as root, thus missing issues that would be faced by non-root users. If snowman is just as easy to access without the extension, that's fine with me.

@dimas-b dimas-b force-pushed the sharable-tests branch 2 times, most recently from 8b7325e to 90ee9d0 Compare December 30, 2024 19:44
@adutra
Copy link
Contributor

adutra commented Dec 31, 2024

I noticed that 3 other tests use the Dropwizard extension and could be considered as "integration" tests:

  • FileIOIntegrationTest
  • RateLimiterFilterTest
  • TimedApplicationEventListenerTest

We could create "black-box" versions of those while still keeping the "white-box" ones around, wdyt?

.stream()
.collect(
Collectors.toMap(PolarisFeatureConfig::name, PolarisFeatureConfig::value));
return new Env(manager.realm(), manager.startServer(featureConfig));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling to adapt this to Quarkus. In a Quarkus test (integration or not), the application will be running already when the extension is executed, and it's not possible to change the feature configs at that moment. The only way to change the application config is through a QuarkusTestProfile afaik. I don't think we can use a QuarkusTestResourceLifecycleManager to launch the application itself on demand; that's intended for dependent resources like databases etc. I think the @PolarisFeatureConfig annotation would eventually go away completely with Quarkus.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see if we this can be refactored to fit Quarkus better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a closer look at the test that use @PolarisFeatureConfig I tend to think that those tests are an overkill to have at the integration test level. The test variations merely check how configuration keys are resolved between catalog properties, server config and default values. I believe that part can be done by a unit test.

I'll try and refactor the blackbox tests to rely only on default config settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm going to make a separate refactoring PR for tests currently using @PolarisFeatureConfig in order to push the config behaviour concerns to the unit test level, but keep blackbox test concentrating on user-visible functionality with default config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#607 converts the tests that relied on config overrides to proper unit tests (those will be outside the scope of this PR if 607 is merged)

@dimas-b
Copy link
Contributor Author

dimas-b commented Dec 31, 2024

FileIOIntegrationTest is not really a blackbox test. It tweaks the internals of CDI in DW. I'm not even sure why it needs a DropwizardAppExtension. I think it can be converted to a proper unit test.

RateLimiterFilterTest could indeed be converted to a blackbox test. I did not do this initially because I thought that the test would become non-deterministic in the presence of other tests if the server is shared. Do you think it would be valuable as a blackbox test?

TimedApplicationEventListenerTest makes assertions that assume exclusive access to the server, so I'm not sure it would be valuable as a blackbox text when the server is shared.

@dimas-b
Copy link
Contributor Author

dimas-b commented Dec 31, 2024

FileIOIntegrationTest refactoring: #598

@adutra
Copy link
Contributor

adutra commented Jan 2, 2025

Do you think it would be valuable as a blackbox test?

Indeed I haven't thought about interference when the server is shared. In this case let's leave RateLimiterFilterTest and TimedApplicationEventListenerTest out of the refactoring scope.

@dimas-b
Copy link
Contributor Author

dimas-b commented Jan 3, 2025

Another related unit test refactoring: #612

@dimas-b
Copy link
Contributor Author

dimas-b commented Jan 3, 2025

@adutra : I've rebased and resolved conflicts. Application log is now checked in DropwizardServerManager.

Config overrides are gone. I hope the backbox tests are reusable in Quarkus now.

@adutra adutra changed the title Convert main integration tests into reusable backbox tests Convert main integration tests into reusable blackbox tests Jan 4, 2025
@dimas-b dimas-b force-pushed the sharable-tests branch 3 times, most recently from 920a2fa to bb4b6fb Compare January 6, 2025 19:09
Copy link
Contributor

@adutra adutra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making good progress! With the latest changes the test at least are executed with Quarkus. I'm seeing a few failures though.

@dimas-b dimas-b force-pushed the sharable-tests branch 2 times, most recently from 07b6a08 to d3bc035 Compare January 9, 2025 00:26
@dimas-b dimas-b marked this pull request as draft January 9, 2025 00:27
@dimas-b dimas-b force-pushed the sharable-tests branch 2 times, most recently from e50c564 to f6d2a8c Compare January 9, 2025 14:35
@dimas-b dimas-b marked this pull request as ready for review January 9, 2025 14:36
@dimas-b dimas-b force-pushed the sharable-tests branch 2 times, most recently from ad84b95 to 09dcd88 Compare January 10, 2025 15:43
@dimas-b
Copy link
Contributor Author

dimas-b commented Jan 10, 2025

@andrew4699 : I propose we merge this PR and address your points in follow-up PRs if you do not mind :) Would this be ok?

@andrew4699
Copy link
Contributor

It's great that we're refactoring this and I think it's going in a good direction, my main concern is that this is a breaking change. Today, you can provide your own HTTP client. If this change is merged as-is, you won't be able to.

* Introduce new module: `integration-tests` to contain the test
  code for backbox integration tests

* Refactors existing main integration tests to avoid relying on
  Dropwizard internals.

* Add new test extension for managing the test environment with
  custom server managers discovered via `ServiceLoader`

* Use Dropwizard test extentions for the reference implementation
  of `PolarisServerManager`. Custom server builds can provide
  their own environment-specific implementations.

* Add helper classes for REST API (Management and Catalog)

* Run reusable tests by extending them in the DW module.
  Using test suites is also possible, but it only uses one
  gradle test worker, so test parallelism is reduced. At the
  same time running tests in parallel withing the same JVM using
  JUnit5 threads currently leads to errors.
Use Awaitility to make sure the expected response code
is received within a reasonable timeout (1 min).
@dimas-b
Copy link
Contributor Author

dimas-b commented Jan 10, 2025

@andrew4699 : does the commit entitled review: customizable client help with your use cases?

Copy link
Contributor

@collado-mike collado-mike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore outdated comments - there was refactoring after my initial review, which I forgot to publish :)

"grant_type",
"client_credentials",
"scope",
"PRINCIPAL_ROLE:ALL",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make scope an optional argument (default to PRINCIPAL_ROLE:ALL) so tests can verify sub-scoped tokens?


AuthToken adminToken();

ClientCredentials adminCredentials();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention of the SnowmanExtension was simply to have a non-root user easily available so that all the tests didn't just run everything as root, thus missing issues that would be faced by non-root users. If snowman is just as easy to access without the extension, that's fine with me.


import java.net.URI;

public interface Server extends AutoCloseable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add javadocs to public classes an interfaces? It's really helpful to see, at a glance, what classes purpose some of these classes are intended to serve.

config.entrySet().stream()
.map((e) -> ConfigOverride.config(e.getKey(), e.getValue()))
.toList()
.toArray(new ConfigOverride[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I tend to prefer Stream.toArray(ConfigOverride[]::new) for this purpose


@Override
public String realmId() {
return TEST_REALM;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the tests today create a distinct realm per test so that there's no overlap for roles or catalogs, etc. It also means that a test that fails to clean up doesn't leave clutter that effects other tests.


@Override
public URI baseUri() {
return URI.create(String.format("http://localhost:%d", ext.getLocalPort()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the proposal for remote tests to implement their own PolarisServerManager. Otherwise, the concern for one use case ends up cluttering the code for the other use case.

Comment on lines +138 to +140
// Create a new CatalogRole that has CATALOG_MANAGE_CONTENT and CATALOG_MANAGE_ACCESS
String catalogRoleName = "custom-admin";
createCatalogRole(catalog.getName(), catalogRoleName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a custom admin role? The service_admin already has the default catalog admin role that was created when the Catalog was created.

new ClientCredentials(
principal.getCredentials().getClientId(),
principal.getCredentials().getClientSecret(),
"dummy-principal"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the principal name?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants