You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@texera.apache.org by gi...@apache.org on 2026/08/12 04:03:23 UTC
(texera) branch gh-readonly-queue/main/pr-7560-cd3872a3a68393644ac28227532ae181b8a01b8f created (now 408b33a98a)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a change to branch gh-readonly-queue/main/pr-7560-cd3872a3a68393644ac28227532ae181b8a01b8f
in repository https://gitbox.apache.org/repos/asf/texera.git
at 408b33a98a test(config-service): cover ConfigService.run in its RunSpec (#7560)
This branch includes the following new commits:
new 408b33a98a test(config-service): cover ConfigService.run in its RunSpec (#7560)
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(texera) 01/01: test(config-service): cover ConfigService.run in its RunSpec (#7560)
Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-7560-cd3872a3a68393644ac28227532ae181b8a01b8f
in repository https://gitbox.apache.org/repos/asf/texera.git
commit 408b33a98a73177ec5175806cef539603dcdb8ed
Author: Meng Wang <me...@uci.edu>
AuthorDate: Tue Aug 11 20:58:13 2026 -0700
test(config-service): cover ConfigService.run in its RunSpec (#7560)
### What changes were proposed in this PR?
`ConfigServiceRunSpec` only asserted role annotations on the resource
classes, so
`ConfigService` itself was never instantiated and the file sat at 0%.
Adds four tests
that call `run()` against a mocked Dropwizard `Environment`, following
`AccessControlServiceRunSpec`. The existing role-annotation assertion is
kept.
| counter | before | after |
| --- | --- | --- |
| line | 0/30 | 13/30 |
| instruction | 0/171 | 99/171 |
| branch | 0/6 | 4/6 |
| method | 0/8 | 4/8 |
`run()` itself is now fully covered by line; every remaining missed line
is in
`initialize()` (39-51) or `object ConfigService.main` (95-108), which
the issue puts out
of scope because #5983 moves that boilerplate into a shared
`ServiceBootstrap`.
What the tests pin:
- the `/api/*` url pattern, the session handler on both the Jersey and
servlet
environments, and the `HealthCheckResource` / `ConfigResource`
registrations;
- the auth stack `AuthFeatures.register` installs —
`AuthDynamicFeature`,
`UnauthorizedExceptionMapper` and `RolesAllowedDynamicFeature` — without
which `@Auth`
parameters do not resolve and `@RolesAllowed` on the settings endpoints
is ignored;
- `RequestLoggingFilter.register(environment.getApplicationContext)`,
verified as the
`addFilter(FilterHolder, "/*", …)` it performs;
- the default-settings preload, checked against the database rather than
a mock: every
entry of `DefaultsConfig.allDefaults` must be present in `site_settings`
with its value;
- that a preload failure is rethrown rather than swallowed — a service
that came up with
no settings would look healthy while serving none of them.
`run()` ends by writing `default.conf` into `site_settings`, so it needs
a live
`SqlServer`; mocking the `Environment` alone cannot reach the
request-logging filter that
follows. The suite therefore mixes in `MockTexeraDB`, which gives it its
own embedded
database and points `SqlServer` at it — `config-service` already
declares
`.dependsOn(DAO % "test->test")` for exactly this, and
`ConfigResourceSpec` in the same
module already does it. The failure case swaps in a `ConnectionProvider`
that cannot
acquire a connection; `MockTexeraDB`'s fixture reinstalls the healthy
context before the
next test, so it stays local (the fixture does not truncate tables, so
dropping one would
not have).
The two branches still missed are not application logic: one on the
class declaration
(`with LazyLogging`, 3/4 arms covered) and the implicit non-`Exception`
arm of
`case ex: Exception`.
No production code was changed.
### Any related issues, documentation, discussions?
Closes #7558.
### How was this PR tested?
`sbt "ConfigService/testOnly *ConfigServiceRunSpec"` — 5 tests pass, run
repeatedly with
the same result; `sbt ConfigService/jacoco` over the module is green (38
tests) and gives
the table above. The failure path was verified by breaking the
url-pattern assertion (red,
non-zero exit) and restoring it. `ConfigService/Test/scalafmtCheck` and
`ConfigService/Test/scalafix --check` are clean.
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8 [1M context])
---
.../texera/service/ConfigServiceRunSpec.scala | 132 ++++++++++++++++++++-
1 file changed, 130 insertions(+), 2 deletions(-)
diff --git a/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala b/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala
index 1481b311e6..a5c14da61f 100644
--- a/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala
+++ b/config-service/src/test/scala/org/apache/texera/service/ConfigServiceRunSpec.scala
@@ -19,12 +19,140 @@
package org.apache.texera.service
-import org.apache.texera.auth.RoleAnnotationEnforcer
+import io.dropwizard.auth.AuthDynamicFeature
+import io.dropwizard.core.setup.Environment
+import io.dropwizard.jersey.DropwizardResourceConfig
+import io.dropwizard.jersey.setup.JerseyEnvironment
+import io.dropwizard.jetty.MutableServletContextHandler
+import io.dropwizard.jetty.setup.ServletEnvironment
+import org.apache.texera.auth.{RoleAnnotationEnforcer, UnauthorizedExceptionMapper}
+import org.apache.texera.common.config.DefaultsConfig
+import org.apache.texera.dao.{MockTexeraDB, SqlServer}
+import org.apache.texera.dao.jooq.generated.Tables.SITE_SETTINGS
import org.apache.texera.service.resource.{ConfigResource, HealthCheckResource}
+import org.eclipse.jetty.server.session.SessionHandler
+import org.eclipse.jetty.servlet.FilterHolder
+import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature
+import org.jooq.{ConnectionProvider, SQLDialect}
+import org.jooq.impl.{DSL, DefaultConfiguration}
+import org.mockito.ArgumentMatchers.{any, eq => eqTo, isA}
+import org.mockito.Mockito.{mock, verify, when}
+import org.scalatest.BeforeAndAfterAll
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
-class ConfigServiceRunSpec extends AnyFlatSpec with Matchers {
+import java.sql.{Connection, SQLException}
+
+// `run` ends by preloading default.conf into site_settings, so it needs a live
+// SqlServer: MockTexeraDB gives this suite its own embedded database and points
+// SqlServer at it, which lets the whole method — including the request-logging
+// filter installed after the preload — run against mocked Dropwizard wiring.
+class ConfigServiceRunSpec
+ extends AnyFlatSpec
+ with Matchers
+ with BeforeAndAfterAll
+ with MockTexeraDB {
+
+ override protected def beforeAll(): Unit = initializeDBAndReplaceDSLContext()
+
+ override protected def afterAll(): Unit = shutdownDB()
+
+ "ConfigService.run" should "install the API prefix, session handling and its resources" in {
+ val jersey = mock(classOf[JerseyEnvironment])
+ val servlets = mock(classOf[ServletEnvironment])
+ val context = mock(classOf[MutableServletContextHandler])
+ val env = mock(classOf[Environment])
+ when(env.jersey).thenReturn(jersey)
+ when(env.servlets).thenReturn(servlets)
+ when(env.getApplicationContext).thenReturn(context)
+ when(jersey.getResourceConfig).thenReturn(DropwizardResourceConfig.forTesting())
+
+ new ConfigService().run(mock(classOf[ConfigServiceConfiguration]), env)
+
+ // Everything the service serves lives under /api; losing this silently moves every
+ // endpoint to the root.
+ verify(jersey).setUrlPattern("/api/*")
+ verify(jersey).register(classOf[SessionHandler])
+ verify(servlets).setSessionHandler(isA(classOf[SessionHandler]))
+ verify(jersey).register(classOf[HealthCheckResource])
+ verify(jersey).register(isA(classOf[ConfigResource]))
+ }
+
+ it should "install the auth stack and the request logging filter" in {
+ val jersey = mock(classOf[JerseyEnvironment])
+ val servlets = mock(classOf[ServletEnvironment])
+ val context = mock(classOf[MutableServletContextHandler])
+ val env = mock(classOf[Environment])
+ when(env.jersey).thenReturn(jersey)
+ when(env.servlets).thenReturn(servlets)
+ when(env.getApplicationContext).thenReturn(context)
+ when(jersey.getResourceConfig).thenReturn(DropwizardResourceConfig.forTesting())
+
+ new ConfigService().run(mock(classOf[ConfigServiceConfiguration]), env)
+
+ // AuthFeatures.register: without these, @Auth parameters do not resolve and
+ // @RolesAllowed on the settings endpoints is ignored.
+ verify(jersey).register(isA(classOf[AuthDynamicFeature]))
+ verify(jersey).register(classOf[UnauthorizedExceptionMapper])
+ verify(jersey).register(classOf[RolesAllowedDynamicFeature])
+
+ // RequestLoggingFilter.register, which runs only after the preload below succeeds.
+ verify(context).addFilter(isA(classOf[FilterHolder]), eqTo("/*"), any())
+ }
+
+ it should "preload the default settings into site_settings" in {
+ val jersey = mock(classOf[JerseyEnvironment])
+ val env = mock(classOf[Environment])
+ when(env.jersey).thenReturn(jersey)
+ when(env.servlets).thenReturn(mock(classOf[ServletEnvironment]))
+ when(env.getApplicationContext).thenReturn(mock(classOf[MutableServletContextHandler]))
+ when(jersey.getResourceConfig).thenReturn(DropwizardResourceConfig.forTesting())
+
+ // The fixture does not truncate between tests and the runs above already seeded the
+ // table, so start from empty: otherwise this passes even for a run() that skipped the
+ // preload entirely.
+ getDSLContext.deleteFrom(SITE_SETTINGS).execute()
+
+ new ConfigService().run(mock(classOf[ConfigServiceConfiguration]), env)
+
+ DefaultsConfig.allDefaults should not be empty
+ DefaultsConfig.allDefaults.foreach {
+ case (key, value) =>
+ val stored = getDSLContext
+ .select(SITE_SETTINGS.VALUE)
+ .from(SITE_SETTINGS)
+ .where(SITE_SETTINGS.KEY.eq(key))
+ .fetchOne()
+ withClue(s"site_settings row for '$key': ") {
+ stored should not be null
+ stored.value1() shouldBe value
+ }
+ }
+ }
+
+ it should "surface a failed preload instead of starting without the defaults" in {
+ val jersey = mock(classOf[JerseyEnvironment])
+ val env = mock(classOf[Environment])
+ when(env.jersey).thenReturn(jersey)
+ when(env.servlets).thenReturn(mock(classOf[ServletEnvironment]))
+ when(env.getApplicationContext).thenReturn(mock(classOf[MutableServletContextHandler]))
+ when(jersey.getResourceConfig).thenReturn(DropwizardResourceConfig.forTesting())
+
+ // Point SqlServer at a context that cannot acquire a connection. MockTexeraDB's fixture
+ // reinstalls the suite's healthy context before the next test, so this stays local.
+ val unusable = new DefaultConfiguration()
+ unusable.set(SQLDialect.POSTGRES)
+ unusable.set(new ConnectionProvider {
+ override def acquire(): Connection = throw new SQLException("database unavailable")
+ override def release(connection: Connection): Unit = ()
+ })
+ SqlServer.getInstance().replaceDSLContext(DSL.using(unusable))
+
+ // Rethrown rather than swallowed: a service that came up with no settings would look
+ // healthy while serving none of them.
+ a[RuntimeException] should be thrownBy new ConfigService()
+ .run(mock(classOf[ConfigServiceConfiguration]), env)
+ }
// Every endpoint this service registers declares @RolesAllowed/@PermitAll/@DenyAll.
"ConfigService's registered resources" should "all declare access control" in {