1
0
mirror of https://github.com/google/nomulus synced 2026-04-24 02:00:50 +00:00

Replace Front/Back-end servlets with single TestServlet (#2874)

The servlets, at this point now that we're off GAE, are only used for
the test server (and, indirectly, in one BSA test). Instead of having
them all remain separate, we can unify them in one test servlet that
lives in the test/ folder.

This removes one avenue of potential confusion w/r/t how request routing
actually works and where we would want to add new routing.
This commit is contained in:
gbrodman
2025-11-12 16:01:14 -05:00
committed by GitHub
parent 816180f3b3
commit 759aaddb5f
24 changed files with 133 additions and 830 deletions

View File

@@ -169,7 +169,7 @@ public class UploadBsaUnavailableDomainsActionTest {
new TestServer(
HostAndPort.fromParts(InetAddress.getLocalHost().getHostAddress(), pickUnusedPort()),
ImmutableMap.of(),
ImmutableList.of(Route.route("/upload", Servelet.class)));
ImmutableList.of(Route.route("/upload", Servlet.class)));
testServer.start();
newSingleThreadExecutor()
.execute(
@@ -191,7 +191,7 @@ public class UploadBsaUnavailableDomainsActionTest {
maxRequestSize = 20971520L, // 20MB
fileSizeThreshold = 1048576 // Save in memory if file size < 1MB
)
public static class Servelet extends HttpServlet {
public static class Servlet extends HttpServlet {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Override

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.frontend;
package google.registry.module;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;

View File

@@ -12,12 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.frontend;
package google.registry.module;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Component;
import dagger.Lazy;
import google.registry.batch.BatchModule;
import google.registry.bigquery.BigqueryModule;
import google.registry.config.CloudTasksUtilsModule;
import google.registry.config.CredentialModule;
import google.registry.config.RegistryConfig;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.export.DriveModule;
import google.registry.export.sheet.SheetsServiceModule;
import google.registry.flows.ServerTridProviderModule;
import google.registry.flows.custom.CustomLogicFactoryModule;
import google.registry.flows.domain.DomainDeletionTimeCacheModule;
@@ -26,35 +32,51 @@ import google.registry.groups.GroupsModule;
import google.registry.groups.GroupssettingsModule;
import google.registry.keyring.KeyringModule;
import google.registry.keyring.api.KeyModule;
import google.registry.module.TestRequestComponent.TestRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.persistence.PersistenceModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules;
import google.registry.rde.JSchModule;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UrlConnectionServiceModule;
import google.registry.request.auth.AuthModule;
import google.registry.util.UtilsModule;
import jakarta.inject.Singleton;
/** Dagger component with instance lifetime for the test server. */
@Singleton
@Component(
modules = {
AuthModule.class,
BatchModule.class,
BigqueryModule.class,
CloudTasksUtilsModule.class,
RegistryConfig.ConfigModule.class,
ConfigModule.class,
CredentialModule.class,
CustomLogicFactoryModule.class,
CloudTasksUtilsModule.class,
DomainDeletionTimeCacheModule.class,
FrontendRequestComponent.FrontendRequestComponentModule.class,
DriveModule.class,
GmailModule.class,
GroupsModule.class,
GroupssettingsModule.class,
MockDirectoryModule.class,
Modules.GsonModule.class,
GsonModule.class,
JSchModule.class,
KeyModule.class,
KeyringModule.class,
Modules.NetHttpTransportModule.class,
MockDirectoryModule.class,
NetHttpTransportModule.class,
PersistenceModule.class,
SecretManagerModule.class,
ServerTridProviderModule.class,
SheetsServiceModule.class,
StackdriverModule.class,
TestRequestComponentModule.class,
UrlConnectionServiceModule.class,
UtilsModule.class
})
interface FrontendTestComponent extends FrontendComponent {}
interface TestRegistryComponent {
TestRequestHandler requestHandler();
Lazy<MetricReporter> metricReporter();
}

View File

@@ -0,0 +1,86 @@
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module;
import dagger.Module;
import dagger.Subcomponent;
import google.registry.batch.BatchModule;
import google.registry.cron.CronModule;
import google.registry.dns.DnsModule;
import google.registry.dns.writer.DnsWritersModule;
import google.registry.dns.writer.dnsupdate.DnsUpdateConfigModule;
import google.registry.export.sheet.SheetModule;
import google.registry.flows.CheckApiAction.CheckApiModule;
import google.registry.flows.EppToolAction.EppToolModule;
import google.registry.flows.TlsCredentials.EppTlsModule;
import google.registry.flows.custom.CustomLogicModule;
import google.registry.loadtest.LoadTestModule;
import google.registry.monitoring.whitebox.WhiteboxModule;
import google.registry.rdap.RdapModule;
import google.registry.rde.RdeModule;
import google.registry.reporting.ReportingModule;
import google.registry.reporting.billing.BillingModule;
import google.registry.reporting.icann.DnsCountQueryCoordinator.DnsCountQueryCoordinatorModule;
import google.registry.reporting.icann.IcannReportingModule;
import google.registry.reporting.spec11.Spec11Module;
import google.registry.request.RequestComponentBuilder;
import google.registry.request.RequestModule;
import google.registry.request.RequestScope;
import google.registry.tmch.TmchModule;
import google.registry.tools.server.ToolsServerModule;
import google.registry.ui.server.console.ConsoleModule;
/** Dagger component with per-request lifetime for the test server. */
@RequestScope
@Subcomponent(
modules = {
BatchModule.class,
BillingModule.class,
CheckApiModule.class,
ConsoleModule.class,
CronModule.class,
CustomLogicModule.class,
DnsCountQueryCoordinatorModule.class,
DnsModule.class,
DnsUpdateConfigModule.class,
DnsWritersModule.class,
EppTlsModule.class,
EppToolModule.class,
IcannReportingModule.class,
LoadTestModule.class,
RdapModule.class,
RdeModule.class,
ReportingModule.class,
RequestModule.class,
SheetModule.class,
Spec11Module.class,
TmchModule.class,
ToolsServerModule.class,
WhiteboxModule.class
})
interface TestRequestComponent extends RequestComponent {
@Subcomponent.Builder
abstract class Builder implements RequestComponentBuilder<TestRequestComponent> {
@Override
public abstract TestRequestComponent.Builder requestModule(RequestModule requestModule);
@Override
public abstract TestRequestComponent build();
}
@Module(subcomponents = TestRequestComponent.class)
static class TestRequestComponentModule {}
}

View File

@@ -0,0 +1,31 @@
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module;
import google.registry.request.RequestHandler;
import google.registry.request.auth.RequestAuthenticator;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
/** Request handler for the specialized test server. */
public class TestRequestHandler extends RequestHandler<TestRequestComponent> {
@Inject
public TestRequestHandler(
Provider<TestRequestComponent.Builder> componentBuilderProvider,
RequestAuthenticator requestAuthenticator) {
super(componentBuilderProvider, requestAuthenticator);
}
}

View File

@@ -12,19 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.frontend;
package google.registry.module;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Lazy;
import google.registry.module.ServletBase;
public class FrontendTestServlet extends ServletBase {
private static final FrontendTestComponent component = DaggerFrontendTestComponent.create();
private static final FrontendRequestHandler requestHandler = component.requestHandler();
/**
* Servlet used in the test server to handle routing.
*
* <p>This functions somewhat as a mock, because in the production environment our routing is
* handled through the Kubernetes configuration (in the jetty package). Here, we can manually
* configure routes for the test server when we need them.
*/
public class TestServlet extends ServletBase {
private static final TestRegistryComponent component = DaggerTestRegistryComponent.create();
private static final TestRequestHandler requestHandler = component.requestHandler();
private static final Lazy<MetricReporter> metricReporter = component.metricReporter();
public FrontendTestServlet() {
public TestServlet() {
super(requestHandler, metricReporter);
}
}

View File

@@ -1,41 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.backend;
import static com.google.common.truth.Truth.assertThat;
import google.registry.request.Action.GaeService;
import google.registry.request.RouterDisplayHelper;
import google.registry.testing.GoldenFileTestHelper;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link BackendRequestComponent}. */
class BackendRequestComponentTest {
@Test
void testRoutingMap() {
GoldenFileTestHelper.assertThatRoutesFromComponent(BackendRequestComponent.class)
.describedAs("backend routing map")
.isEqualToGolden(BackendRequestComponentTest.class, "backend_routing.txt");
}
@Test
void testRoutingService() {
assertThat(
RouterDisplayHelper.extractHumanReadableRoutesWithWrongService(
BackendRequestComponent.class, GaeService.BACKEND))
.isEmpty();
}
}

View File

@@ -1,38 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.backend;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link BackendServlet}. */
class BackendServletTest {
private final HttpServletRequest req = mock(HttpServletRequest.class);
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
@Test
void testService_unknownPath_returnsNotFound() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/lol");
new BackendServlet().service(req, rsp);
verify(rsp).sendError(404);
}
}

View File

@@ -1,41 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.frontend;
import static com.google.common.truth.Truth.assertThat;
import google.registry.request.Action.GaeService;
import google.registry.request.RouterDisplayHelper;
import google.registry.testing.GoldenFileTestHelper;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link FrontendRequestComponent}. */
class FrontendRequestComponentTest {
@Test
void testRoutingMap() {
GoldenFileTestHelper.assertThatRoutesFromComponent(FrontendRequestComponent.class)
.describedAs("frontend routing map")
.isEqualToGolden(FrontendRequestComponentTest.class, "frontend_routing.txt");
}
@Test
void testRoutingService() {
assertThat(
RouterDisplayHelper.extractHumanReadableRoutesWithWrongService(
FrontendRequestComponent.class, GaeService.DEFAULT))
.isEmpty();
}
}

View File

@@ -1,38 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.module.frontend;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link FrontendServlet}. */
class FrontendServletTest {
private final HttpServletRequest req = mock(HttpServletRequest.class);
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
@Test
void testService_unknownPath_returnNotFound() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/lol");
new FrontendServlet().service(req, rsp);
verify(rsp).sendError(404);
}
}

View File

@@ -21,9 +21,7 @@ import static google.registry.util.BuildPathUtils.getResourcesDir;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HostAndPort;
import google.registry.module.backend.BackendServlet;
import google.registry.module.frontend.FrontendServlet;
import google.registry.module.frontend.FrontendTestServlet;
import google.registry.module.TestServlet;
import java.net.URL;
import java.nio.file.Path;
@@ -40,29 +38,28 @@ public final class RegistryTestServer {
public static final ImmutableList<Route> ROUTES =
ImmutableList.of(
route("/rdap/*", TestServlet.class),
// Frontend Services
route("/rdap/*", FrontendServlet.class),
route("/check", FrontendServlet.class),
route("/console-api/*", FrontendTestServlet.class),
route("/console-api/*", TestServlet.class),
// Proxy Services
route("/_dr/epp", FrontendServlet.class),
route("/_dr/epp", TestServlet.class),
// Registry Data Escrow (RDE)
route("/_dr/cron/rdeCreate", BackendServlet.class),
route("/_dr/task/rdeStaging", BackendServlet.class),
route("/_dr/task/rdeUpload", BackendServlet.class),
route("/_dr/task/rdeReport", BackendServlet.class),
route("/_dr/task/brdaCopy", BackendServlet.class),
route("/_dr/cron/rdeCreate", TestServlet.class),
route("/_dr/task/rdeStaging", TestServlet.class),
route("/_dr/task/rdeUpload", TestServlet.class),
route("/_dr/task/rdeReport", TestServlet.class),
route("/_dr/task/brdaCopy", TestServlet.class),
// Trademark Clearinghouse (TMCH)
route("/_dr/cron/tmchDnl", BackendServlet.class),
route("/_dr/task/tmchSmdrl", BackendServlet.class),
route("/_dr/task/tmchCrl", BackendServlet.class),
route("/_dr/cron/tmchDnl", TestServlet.class),
route("/_dr/task/tmchSmdrl", TestServlet.class),
route("/_dr/task/tmchCrl", TestServlet.class),
// Notification of Registered Domain Names (NORDN)
route("/_dr/task/nordnUpload", BackendServlet.class),
route("/_dr/task/nordnVerify", BackendServlet.class));
route("/_dr/task/nordnUpload", TestServlet.class),
route("/_dr/task/nordnVerify", TestServlet.class));
private final TestServer server;

View File

@@ -247,11 +247,6 @@ public final class TestServerExtension implements BeforeEachCallback, AfterEachC
return this;
}
/** Sets the list of servlet {@link Route} objects for {@link TestServer}. */
public Builder setRoutes(Route... routes) {
return setRoutes(ImmutableList.copyOf(routes));
}
/** Sets an ordered list of fixtures that should be loaded on startup. */
public Builder setFixtures(Fixture... fixtures) {
this.fixtures = ImmutableList.copyOf(fixtures);

View File

@@ -1,39 +0,0 @@
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
BACKEND /_dr/cron/fanout TldFanoutAction GET y APP ADMIN
BACKEND /_dr/task/brdaCopy BrdaCopyAction POST y APP ADMIN
BACKEND /_dr/task/copyDetailReports CopyDetailReportsAction POST n APP ADMIN
BACKEND /_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n APP ADMIN
BACKEND /_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n APP ADMIN
BACKEND /_dr/task/deleteProberData DeleteProberDataAction POST n APP ADMIN
BACKEND /_dr/task/dnsRefresh RefreshDnsAction GET y APP ADMIN
BACKEND /_dr/task/executeCannedScript CannedScriptExecutionAction POST,GET y APP ADMIN
BACKEND /_dr/task/expandBillingRecurrences ExpandBillingRecurrencesAction GET n APP ADMIN
BACKEND /_dr/task/exportDomainLists ExportDomainListsAction POST n APP ADMIN
BACKEND /_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n APP ADMIN
BACKEND /_dr/task/exportReservedTerms ExportReservedTermsAction POST n APP ADMIN
BACKEND /_dr/task/generateInvoices GenerateInvoicesAction POST n APP ADMIN
BACKEND /_dr/task/generateSpec11 GenerateSpec11ReportAction POST n APP ADMIN
BACKEND /_dr/task/icannReportingStaging IcannReportingStagingAction POST n APP ADMIN
BACKEND /_dr/task/icannReportingUpload IcannReportingUploadAction POST n APP ADMIN
BACKEND /_dr/task/nordnUpload NordnUploadAction POST y APP ADMIN
BACKEND /_dr/task/nordnVerify NordnVerifyAction POST y APP ADMIN
BACKEND /_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y APP ADMIN
BACKEND /_dr/task/publishInvoices PublishInvoicesAction POST n APP ADMIN
BACKEND /_dr/task/publishSpec11 PublishSpec11ReportAction POST n APP ADMIN
BACKEND /_dr/task/rdeReport RdeReportAction POST n APP ADMIN
BACKEND /_dr/task/rdeStaging RdeStagingAction GET,POST n APP ADMIN
BACKEND /_dr/task/rdeUpload RdeUploadAction POST n APP ADMIN
BACKEND /_dr/task/readDnsRefreshRequests ReadDnsRefreshRequestsAction POST y APP ADMIN
BACKEND /_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction POST n APP ADMIN
BACKEND /_dr/task/relockDomain RelockDomainAction POST y APP ADMIN
BACKEND /_dr/task/removeAllDomainContacts RemoveAllDomainContactsAction POST n APP ADMIN
BACKEND /_dr/task/resaveAllEppResourcesPipeline ResaveAllEppResourcesPipelineAction GET n APP ADMIN
BACKEND /_dr/task/resaveEntity ResaveEntityAction POST n APP ADMIN
BACKEND /_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n APP ADMIN
BACKEND /_dr/task/syncGroupMembers SyncGroupMembersAction POST n APP ADMIN
BACKEND /_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n APP ADMIN
BACKEND /_dr/task/tmchCrl TmchCrlAction POST y APP ADMIN
BACKEND /_dr/task/tmchDnl TmchDnlAction POST y APP ADMIN
BACKEND /_dr/task/tmchSmdrl TmchSmdrlAction POST y APP ADMIN
BACKEND /_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y APP ADMIN
BACKEND /_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n APP ADMIN

View File

@@ -1,21 +0,0 @@
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
FRONTEND /_dr/epp EppTlsAction POST n APP ADMIN
FRONTEND /ready/frontend ReadinessProbeActionFrontend GET n NONE PUBLIC
CONSOLE /console-api/bulk-domain ConsoleBulkDomainAction POST n USER PUBLIC
CONSOLE /console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
CONSOLE /console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
CONSOLE /console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
CONSOLE /console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
CONSOLE /console-api/ote ConsoleOteAction GET,POST n USER PUBLIC
CONSOLE /console-api/password-reset-request PasswordResetRequestAction POST n USER PUBLIC
CONSOLE /console-api/password-reset-verify PasswordResetVerifyAction GET,POST n USER PUBLIC
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
CONSOLE /console-api/settings/contacts ContactAction GET,POST,DELETE,PUT n USER PUBLIC
CONSOLE /console-api/settings/rdap-fields RdapRegistrarFieldsAction POST n USER PUBLIC
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
CONSOLE /ready/console ReadinessProbeConsoleAction GET n NONE PUBLIC