# PR#122: Git Credentials and the Reading of Pull Requests Split Up and Tested > **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 Two methods of the git and Gitea access are above the Kotlin target of 15: | Method | Mutation-CRAP | Complexity | Mutation score | |--------------------------------------------------|---------------|------------|----------------| | `GitService.authenticated` | 30.12 | 6 | 0.13 | | `GiteaClient.GiteaPullRequest.toOpenPullRequest` | 16.04 | 16 | 0.95 | `authenticated` decides which remote git commands get the configured credentials. No test looked at that decision: the git tests run against local fixture repositories, whose origin is a path, so every fetch ran without credentials and 7 of 8 mutants survived. `toOpenPullRequest` maps Gitea's answer and is well tested, but every absent field is a branch of its own. ## Non-Goals - No change in behaviour. - The other groups above 15 follow in their own PRs. ## The Scenarios ### Feature: remote git commands get credentials only where they can work #### Scenario#122.01: only an HTTP(S) origin with account and token fetches through the askpass script So that the token reaches git exactly where git can use it, and never a prompt blocks a fetch. - **Given** a repository whose top level configures `git.account` and `git.token` - **When** its origin is an HTTPS or an HTTP URL - **Then** the fetch runs with `GIT_ASKPASS`, `WERKATOR_GIT_ACCOUNT`, `WERKATOR_GIT_TOKEN` and `GIT_TERMINAL_PROMPT=0` - **and** the credentials are read from the repository's top level, also when the command runs in a subdirectory - **and** the askpass script exists while the command runs and is gone afterwards - **When** the origin is an SSH URL or there is no origin - **Then** the fetch runs with `GIT_TERMINAL_PROMPT=0` alone, and no configuration is read - **When** the account or the token is blank - **Then** the fetch runs with `GIT_TERMINAL_PROMPT=0` alone ##### Verified by - [GitServiceCredentialsUnitTest](../../src/test/kotlin/de/hoennig/werkator/git/GitServiceCredentialsUnitTest.kt) ### Feature: open pull requests are read whatever Gitea leaves out #### Scenario#122.02: a pull request without head or base reads as a fork, and the title prefix is matched in any case So that a sparse or older answer never marks a foreign branch as the repository's own, nor misses a draft. - **Given** Gitea answers a pull request without `head` and `base`, titled `[wip] lower case` - **and** one whose head has no `repo_id` - **and** one whose title only mentions `WIP:` in its middle - **When** the open pull requests are read - **Then** the first is a draft from a fork with empty refs - **and** the second is from a fork, since nothing says its branch is the repository's own - **and** the third is no draft ##### Verified by - [GiteaClientIntegrationTest](../../src/test/kotlin/de/hoennig/werkator/gitea/GiteaClientIntegrationTest.kt) `a pull request without head or base reads as a fork, and the title prefix is matched in any case (Scenario#122.02)`, and the Scenario#117.02 test ## The Solution `GitService.credentialsFor` answers which credentials a repository's remote commands get, null for none; `authenticated` runs the command with the askpass script or with `NO_PROMPT`. `GiteaPullRequest.toOpenPullRequest` replaces an absent head or base with an empty branch once, the branch answers its ref and commit itself, and `isDraft` holds the fallback on the title. A local PIT run of `GitService` and `GiteaClient` afterwards: | Method | Mutation-CRAP | Complexity | Mutation score | |--------------------------------------------------|---------------|------------|----------------| | `GitService.authenticated` | 2.15 | 2 | 0.67 | | `GitService.credentialsFor` | 9 | 9 | 1.00 | | `GiteaClient.GiteaPullRequest.toOpenPullRequest` | 6 | 6 | 1.00 | | `GiteaClient.GiteaPullRequest.isDraft` | 3.03 | 3 | 0.86 | The highest score left in both classes is 12 (`GitService.originBranchCommitTimes`). The project maximum is now `NginxProxyManager.resolveSettings` at 27.44, so the gate goes down from 31 to 28. ## Additional Changes - `maxMutationCrap` in `build.gradle.kts` lowered from 31 to 28. - Version 1.37.3 with its entry in `releases.html`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)