spring-projects / spring-projects/spring-boot

AutoConfigureTestGrpcTransport does not participate in the test context's key and tries to start the same in-process server multiple times

Open
#50,860 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: team-only type: bug
Dominant language
Java
Stars
81.5k
Forks
42.7k
Avg merge
2d 4h
Merged PRs (30d)
65

Description

My working 1.0.3 setup:

    <dependency>
      <groupId>org.springframework.grpc</groupId>
      <artifactId>spring-grpc-server-spring-boot-starter</artifactId>
      <version>1.0.3</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.grpc</groupId>
      <artifactId>spring-grpc-test</artifactId>
      <version>${spring-grpc.version}</version>
      <optional>true</optional>
    </dependency>

Test class:

@ExtensionMethod({WebTestClientExtensions.class, GrpcClientExtensions.class})
@WebFluxTest
@AutoConfigureInProcessTransport
@ImportAutoConfiguration({
  ReactiveWebSecurityAutoConfiguration.class,
  GrpcServerAutoConfiguration.class,
  GrpcServerFactoryAutoConfiguration.class
})
public class GrpcIntegrationTests extends AbstractGrpcIntegrationTests {

  private static final String TEST_FIXTURES_FOLDER = "test-fixtures/grpc/";

  @SpringBootConfiguration
  @EnableAutoConfiguration
  static class TestApplication extends AbstractGrpcTestApplication {}

  GrpcIntegrationTests() {
    super(TEST_FIXTURES_FOLDER);
  }

  @Nested
  @ActiveProfiles("oauth2-disabled")
  class GrpcOAuth2DisabledTests extends AbstractGrpcIntegrationTests {


    GrpcOAuth2DisabledTests() {
      super(TEST_FIXTURES_FOLDER);
    }

Abstract class:

@ContextConfiguration(
    initializers = AbstractGrpcIntegrationTests.GrpcApplicationContextInitializer.class)
@TestPropertySource(
    properties = {})
public abstract class AbstractGrpcIntegrationTests extends AbstractApiReactiveIntegrationTests {

  @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
  @Autowired
  protected ReactorGrpcTestServiceGrpc.ReactorGrpcTestServiceStub stub;

  protected AbstractGrpcIntegrationTests(String testFixturesFolder) {
    super(testFixturesFolder);
  }

  public static final class GrpcApplicationContextInitializer
      implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(@NonNull ConfigurableApplicationContext applicationContext) {
      TestPropertyValues.of("spring.grpc.server.inprocess.name=test-inprocess-" + UUID.randomUUID())
          .applyTo(applicationContext);
    }
  }

  protected abstract static class AbstractGrpcTestApplication extends AbstractTestApplication {

    @Bean
    public GrpcTestService grpcTestService() {
      return new GrpcTestService();
    }

    @Bean
    public ReactorGrpcTestServiceGrpc.ReactorGrpcTestServiceStub stub(
        GrpcChannelFactory channels,
        @Value("${spring.grpc.server.inprocess.name}") String name) {
      return ReactorGrpcTestServiceGrpc.newReactorStub(channels.createChannel(name));
    }
  }
}

So the idea was that the @Nested tests would each create a test-inprocess- instead and wire up the stub.

Now I'm trying to get this to work with:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-grpc-server</artifactId>
      <optional>true</optional>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-grpc-server-test</artifactId>
      <optional>true</optional>
    </dependency>

new test class preamble:

@ExtensionMethod({WebTestClientExtensions.class, GrpcClientExtensions.class})
@WebFluxTest
@AutoConfigureTestGrpcTransport
@ImportAutoConfiguration({
  ReactiveWebSecurityAutoConfiguration.class,
  GrpcServerAutoConfiguration.class
})

The top level test class works fine, but @Nested classes fail with:

Caused by: java.io.IOException: name already registered: 3f6df1c8-474e-47ea-9995-acf6d64baff4

Seems like it is ignoring the spring.grpc.server.inprocess.name property and/or starting up duplicate instances.

I read the migration guide and didn't see anything around this.

Except maybe this blurb:

Test Specific Factory Classes
Tests now use a custom GrpcServerFactory to create in-process channels rather than using InProcessGrpcServerFactory. This helps keep InProcessGrpcServerFactory for user code and allow better detection of then a factory is for test purposes.

but not much information on it.

Trying to debug, it only gets into InProcessGrpcServerConfiguration if I use:

@AutoConfigureTestGrpcTransport(enableServerFactory = true)

Then it uses my custom names, but it still tries to create the inprocess server with the plain guid which I assume is the spring boot one.

EDIT: Seems to be an oversight with TestGrpcTransportAutoConfiguration not respecting the custom name. If I exclude it and then c&p TestGrpcChannelFactory into my code and create these 3 beans:

    @Bean
    ClientInterceptorsConfigurer grpcClientInterceptorsConfigurer(ApplicationContext applicationContext) {
      return new ClientInterceptorsConfigurer(applicationContext);
    }

    @Bean
    public SslBundles sslBundles() {
      SslBundle sslBundle = SslBundle.of(SslStoreBundle.NONE);
      return new DefaultSslBundleRegistry("grpc", sslBundle);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    TestGrpcChannelFactory testGrpcChannelFactory(ClientInterceptorsConfigurer interceptorsConfigurer, @Value("${spring.grpc.server.inprocess.name}") String name) {
      return new TestGrpcChannelFactory(name, interceptorsConfigurer);
    }


Then it works, so the problem seems to be that you're using private static final String address = InProcessServerBuilder.generateName(); and ignoring the property.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with TestGrpcTransportAutoConfiguration and TestGrpcChannelFactory, then trace how InProcessGrpcServerConfiguration handles spring.grpc.server.inprocess.name. Reproduce the failure with the shown nested test classes and distinct UUID-based names. Done means nested contexts start without duplicate in-process server registration and the configured names are respected.

Written by the indexing model from the issue text.

Assessment

Tech stack
grpc, java, spring-boot
Domain
api, backend, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.