> **WARNING:** This document describes only the change applied in this PR. > It may already be outdated once the next PR is merged. > Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation. ## The Problem After PR#106 the highest Mutation-CRAP scores of Werkator were methods that ask the host something, and no test reached them: | Method | Mutation-CRAP | Killed / eligible | | ----------------------------------- | ------------- | ----------------- | | `DockerSocketLocator.locate` | 56 | 0 / 9 | | `InitCommand.runningJarPath` | 56 | 0 / 7 | | `HostingEnvironments.readHostname` | 42 | 0 / 7 | | `MetricsConfiguration.stateHome` | 42 | 0 / 6 | | `NginxProxyManager.defaultStateDir` | 26.83 | 1 / 6 | Each read the environment, the file system or the JVM directly, so its rules — which socket wins, where the jar is, which name counts — could only be tested on a host that happens to look a certain way. The rule for Werkator's state directory, `XDG_STATE_HOME` or `~/.local/state`, was written out three times: for the metrics, the managed nginx and the artifact store. ## Non-Goals - No change in behaviour: every socket, path and name is found as before. - `MetricsConfiguration.monitoredDisks` and `NginxProxyManager.cleanupStaleContainers` stay for a later PR. ## The Scenarios ### Feature: What Werkator asks the host is decided by rules with tests #### Scenario#107.01: A unix DOCKER_HOST wins, then the rootless socket, then the system socket So that a build container gets the socket the legacy script would have given it. - **Given** an environment, a set of existing sockets and their groups - **When** the socket for a uid is located - **Then** a unix `DOCKER_HOST` wins, then `/run/user//docker.sock` (rootless), then `/var/run/docker.sock` with its group - **And** a tcp `DOCKER_HOST` or a socket that does not exist gives none ##### Verified by - [DockerSocketLocatorUnitTest](../../src/test/kotlin/de/hoennig/werkator/build/DockerSocketLocatorUnitTest.kt) — Scenario#107.01 #### Scenario#107.02: The running jar is the one java -jar names, else the one in the loader's URL So that `init --systemd` writes the jar the service really runs. - **Given** a launch command and a code source URL - **When** the running jar is found - **Then** a `java -jar` command names it, a Spring Boot loader URL holds it, and running from classes gives none ##### Verified by - [InitCommandUnitTest](../../src/test/kotlin/de/hoennig/werkator/commands/InitCommandUnitTest.kt) — Scenario#107.02 #### Scenario#107.03: The machine's name comes from /etc/hostname, the resolver only when the file says nothing So that a resolver alias never names a Hostsharing machine. - **Given** a hostname file and a resolver, each answering, blank, or failing - **When** the machine's name is read - **Then** the file's name wins, the resolver answers when the file is blank or missing, and nothing answers when both fail ##### Verified by - [HostingEnvironmentUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/HostingEnvironmentUnitTest.kt) — Scenario#107.03 #### Scenario#107.04: The state home is XDG_STATE_HOME, else ~/.local/state So that the artifact store, the managed nginx and the metrics agree where Werkator's state lives. - **Given** `XDG_STATE_HOME` set, unset, and blank - **When** the state home is asked for - **Then** it is that directory, or `~/.local/state` ##### Verified by - [StateHomeUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/StateHomeUnitTest.kt) — Scenario#107.04 ## The Solution - `DockerSocketLocator` asks the host through three seams — the environment, whether a path exists, the group of a socket — given by its Spring constructor; `candidate` holds the order of the rules. - `InitCommand.jarPathOf` decides from the launch command and the code source URL; `runningJarPath` only reads the two. - `HostingEnvironments.hostnameOf` decides between the hostname file and the resolver; `readHostname` only names the two. - `StateHome.of` is the one rule for the state directory; the metrics, the nginx default and the artifact store use it. - The gate `maxMutationCrap` falls from 57 to 47: the highest score left is `BuildExecutor.runBuildCommands` at 46.55. Measured with PIT as the nightly build runs it: | Method | Before | After | | ----------------------------------- | ------ | ---------------- | | `DockerSocketLocator.locate` | 56 | 5.12 | | `DockerSocketLocator.candidate` | — | 5 (6 / 6 killed) | | `InitCommand.runningJarPath` | 56 | 6 | | `InitCommand.jarPathOf` | — | 6 (6 / 6 killed) | | `HostingEnvironments.readHostname` | 42 | 2 | | `HostingEnvironments.hostnameOf` | — | 8 (8 / 8 killed) | | `MetricsConfiguration.stateHome` | 42 | no mutant left | | `StateHome.of` | — | 6.17 | | `NginxProxyManager.defaultStateDir` | 26.83 | 2 | ## Additional Changes - Version 1.34.4 with its release note. ## Follow-up PRs - The rest of `BuildExecutor` (`runBuildCommands` 46.55, `shutdown` 42, `cancel` 30, `startBuild` 28.67), with a decision on how the nightly run measures it.