Checking your tools in CI
The starter gatool-spring-boot-starter-test brings two assertions. Add it in
test scope beside spring-boot-starter-test:
<dependency>
<groupId>io.gatool</groupId>
<artifactId>gatool-spring-boot-starter-test</artifactId>
<version>0.1.0</version>
<scope>test</scope>
</dependency>
The operation files validate. This assertion is the CI check, the
OperationFilesAssert test. It runs the checks that startup runs on the
operation files and the schema. It runs from a plain JUnit test, without an
issuer, a running application or a reachable schema URL. A broken file
therefore fails the build that changed it. Five stops depend on the rest of
the configuration, so they run at startup alone:
-
A tool name that clashes with one of the dynamic tools, the tools generated when
gatool.dev.experimental.generate-toolsis set todynamic-three-step. -
A tool name that clashes with a tool the application declares.
-
The scope rules under a shared credential, which is client credentials or a static header, one identity for every caller.
-
The scopes of
gatool.mcp.stdio.granted-scopes. -
An image mime type beside an output schema.
A test that starts the context covers those five:
@Test
void operationFiles_shouldValidate() {
OperationFilesAssert.assertValid(Path.of("src/main/resources/movies.graphqls"),
List.of(Path.of("src/main/resources/gatool/mcp")), List.of(), CheckSettings.defaults());
}
The second argument lists the folders of operation files served over the
Model Context Protocol (MCP), the MCP folders. The third lists the folders of
operation files served in process, the in-process folders.
The CI check reads each folder under that folder's own rules. An application
with only one of the two passes List.of() for the other. A listed folder that
does not exist fails the assertion, and the message names the folder. A renamed
folder therefore cannot pass with zero tools. The CI check follows a symbolic
link to a folder. It lists the tools in path order, the way startup reads a
location. The fourth argument is a CheckSettings. It carries the naming
strategy, the output schema switch and the scalar fragments the application
configures. A scalar fragment is a piece of JSON Schema configured under
scalar-schemas. The CI check then runs under the same settings startup uses.
An application on every default passes CheckSettings.defaults(). One with
gatool.naming.strategy: snake-case passes
CheckSettings.defaults().withNamingStrategy(ToolNamingStrategy.snakeCase()).
One that publishes output schemas or declares scalar fragments builds its
settings with the other with... methods.
The tool contract matches its snapshot. A tool name is what the model calls and what a client configuration records. A renamed tool or a changed input schema therefore shows up in review as a changed file:
@SpringBootTest
class ToolContractTests {
@Autowired
GAToolCatalog catalog;
@Test
void tools_shouldMatchTheSnapshot() {
ToolContractSnapshot.of(catalog).assertMatches(Path.of("src/test/resources/gatool-contract.json"));
}
}
When the snapshot is missing, the test writes it and then fails, asking for the
file to be committed. A run in CI without the file is therefore a failure. A
deliberate change is accepted with -Dgatool.snapshot.update=true. That flag
rewrites the file for review. The snapshot records each tool's scopes beside
its schemas, because a changed scope list is a contract change a client
notices. The file is written with line feeds on every platform. The path is
resolved against the JVM's working directory. Under Maven, Gradle and the IDEs
that is the module directory. Under Gradle the flag reaches the test JVM
through systemProperty on the test task.