Files
werkator/docs/prs/2026-10-09-PR#130-repository-privacy-answer-steps.md
mi 1943a593c0
werkator build succeeded after 02:04
werkator/deploy build succeeded after 00:07
Repository privacy answer kept and renewed in steps (v1.37.11) (#130)
Co-authored-by: mi <1+mi@noreply.git.javagil.de>
2026-10-09 13:10:46 +02:00

79 lines
3.6 KiB
Markdown

# PR#130: Repository Privacy Answer Kept and Renewed in Steps
> **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
`RepositoryPrivacy.isPrivate` is above the Mutation-CRAP target of 15 in the nightly mutation build of 2026-10-07 and 2026-10-09:
| Method | Mutation-CRAP | Complexity | Killed / eligible |
|-------------------------------|--------------:|-----------:|------------------:|
| `RepositoryPrivacy.isPrivate` | 16.40 | 10 | 9 / 15 |
One method decided whether the cached answer still stands, asked the forge, warned about a silent forge and stored the new answer.
The tests never touched the exact moment an answer expires, and nothing checked that a silent forge is reported once and not on every retry.
## Non-Goals
- No change of the behaviour: the same answers, the same expiry times, the same warning.
## The Scenarios
### Feature: The privacy answer of a repository
#### Scenario#130.01: A cached answer expires at exactly its validity
So that a repository that changed its visibility is followed within the stated time, and not one poll later or earlier.
- **Given** the forge answered for a repository
- **When** one second less than the answer's validity has passed
- **Then** the cached answer is returned and the forge is not asked
- **When** the full validity has passed
- **Then** the forge is asked again
- The validity is `REFRESH_AFTER` (15 minutes) for a confirmed answer and `RETRY_UNKNOWN_AFTER` (1 minute) for a guess.
##### Verified by
- [RepositoryPrivacyUnitTest: a confirmed answer stands for exactly REFRESH_AFTER](../../src/test/kotlin/de/hoennig/werkator/server/RepositoryPrivacyUnitTest.kt)
- [RepositoryPrivacyUnitTest: a guess stands for exactly RETRY_UNKNOWN_AFTER](../../src/test/kotlin/de/hoennig/werkator/server/RepositoryPrivacyUnitTest.kt)
#### Scenario#130.02: A forge that stops answering is reported once
So that the log names the outage without repeating it every minute while the forge stays silent.
- **Given** the forge answered for a repository
- **When** the forge stops answering and is asked three times in a row
- **Then** the warning "Cannot tell whether … is private" is logged once
##### Verified by
- [RepositoryPrivacyUnitTest: a forge that stops answering is reported once](../../src/test/kotlin/de/hoennig/werkator/server/RepositoryPrivacyUnitTest.kt)
## The Solution
- `Answer.standsAt(now)` says whether a cached answer is still valid.
- `isPrivate` returns the cached answer or calls `ask`.
- `ask` reads the visibility, warns when a known answer turns unknown, and stores the new answer.
Measured locally with PIT on `RepositoryPrivacy`:
| Method | Mutation-CRAP before | after |
|-------------------------------|---------------------:|------:|
| `RepositoryPrivacy.isPrivate` | 16.40 | 6.04 |
| `RepositoryPrivacy.ask` | new | 6.11 |
| `Answer.standsAt` | new | 3.00 |
The gate stays at 15.
## Prerequisite PRs
- PR#129 — this branch is stacked on it, for the version and the release notes.
## Follow-up PRs
- PR#131 splits two more methods of `ConfigLoader` into smaller steps, so that their scores drop below 15.
`ConfigLoader.renameLegacySandbox` is at 15.93.
`ConfigLoader.checkRestartsInstance` is at exactly 15.00: one more surviving mutant would put it above the gate.