docs(updater): correct PaperMC UA/fixture overclaims, re-tier the boundary
An out-of-band curl of the live Fill v3 endpoint contradicted two claims the previous commit shipped and surfaced a mis-tiering: - User-Agent is NOT enforced: fill.papermc.io/v3/projects/velocity returned HTTP 200 to a bare curl UA. The comments claimed a generic UA "is refused" and the API "REQUIRES" a contact UA. Reword to what is true — PaperMC's usage policy asks for a descriptive UA and may block generic ones, but sending it is etiquette/defensive here, not a gate Felis depends on. - The test fixture's shape was invented, not captured: the real "versions" object groups the entire 3.x line under a single key "3.0.0", not the per-minor keys the fixture used. Replace it with the real body (keys and version strings as returned). The key-agnostic parser already produced the right answer, and an independent max-stable check confirms 3.4.0. - Re-tier doc.go: the GitHub Releases source is verifiable-here (the same httptest-testable shape as PaperMC), not integration remainder. It is why 3 of 4 components report "latest unknown" today and is the next verifiable slice — the release-source work is only ~half done until it exists. No production logic changed. WSL oracle: build + vet clean, internal/updater 10/10, full tree go test RC=0 (19 ok, 0 fail).
This commit is contained in:
3 files changed
+62
-35
No files matched your search
+23
-14
@@ -11,20 +11,29 @@
|
|||||||
// "the updater works against real infra":
|
// "the updater works against real infra":
|
||||||
//
|
//
|
||||||
// - BUILT + UNIT-VERIFIED (Go tests, WSL oracle): the topology, the PaperMC Fill
|
// - BUILT + UNIT-VERIFIED (Go tests, WSL oracle): the topology, the PaperMC Fill
|
||||||
// v3 parser (its fixture is captured from the REAL live response shape on
|
// v3 parser, the routing source, and the Runner's report-only composition. The
|
||||||
// 2026-07-04 — a grounded contract test, not a self-referential one), the
|
// PaperMC fixture is captured from the live endpoint, and the live call was
|
||||||
// routing source, and the Runner's report-only composition. These prove the
|
// exercised out-of-band on 2026-07-04 (curl fill.papermc.io/v3/projects/velocity):
|
||||||
// parse/plan/compose LOGIC and that the core is now wired to a caller.
|
// the response shape matches the fixture and 3.4.0 is confirmed the newest stable
|
||||||
|
// (3.5.0-SNAPSHOT correctly filtered). These prove the parse/plan/compose LOGIC
|
||||||
|
// and that the core is now wired to a caller.
|
||||||
//
|
//
|
||||||
// - WRITTEN, NOT LIVE-VERIFIED: the tests assert the required non-generic
|
// - NOT YET BUILT, but VERIFIABLE HERE (same technique as PaperMC — HTTP GET, JSON
|
||||||
// User-Agent is transmitted, but real fill.papermc.io network/TLS/UA-enforcement
|
// decode, tolerant Parse, prerelease filter, all httptest-testable): the GitHub
|
||||||
// is not exercised here; the fixture proves today's shape, not its future
|
// Releases source. It is why 3 of the 4 components (felis-api, k3s, cloudflared)
|
||||||
// stability.
|
// currently report "latest unknown" — RoutingSource returns errGitHubNotWired for
|
||||||
|
// them. This is the next VERIFIABLE slice, not integration remainder; until it
|
||||||
|
// exists the verifiable release-source work is only ~half done.
|
||||||
//
|
//
|
||||||
// - REMAINING INTEGRATION (not built here): the GitHub Releases source (felis-api,
|
// - CAVEATS on what the tests do NOT prove: they run against httptest, not the live
|
||||||
// k3s, cloudflared — RoutingSource returns errGitHubNotWired for them today), the
|
// host, so future upstream shape drift is not caught; and while Felis sends a
|
||||||
// concrete VersionGatherer (`k3s --version`, image-tag / jar inspection), the
|
// descriptive User-Agent (PaperMC etiquette), upstream UA enforcement was not
|
||||||
// concrete Notifier (SMTP + in-game) and Applier (control-plane image bump,
|
// active on the project endpoint on 2026-07-04 (a bare UA got HTTP 200), so the UA
|
||||||
// cloudflared swap), the `felis update` CLI + CronJob entry point, and the
|
// is defensive, not load-bearing.
|
||||||
// runtime append of the live Pinned Minecraft fleet.
|
//
|
||||||
|
// - REMAINING INTEGRATION (pure I/O, no verifiable-here logic): the concrete
|
||||||
|
// VersionGatherer (`k3s --version`, image-tag / jar inspection), the concrete
|
||||||
|
// Notifier (SMTP + in-game) and Applier (control-plane image bump, cloudflared
|
||||||
|
// swap), the `felis update` CLI + CronJob entry point, and the runtime append of
|
||||||
|
// the live Pinned Minecraft fleet.
|
||||||
package updater
|
package updater
|
||||||
@@ -10,11 +10,15 @@ import (
|
|||||||
"felis.lolicon.best/internal/updates"
|
"felis.lolicon.best/internal/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultUserAgent identifies Felis to the PaperMC Fill v3 API, which REQUIRES a
|
// defaultUserAgent identifies Felis to the PaperMC Fill v3 API. PaperMC's API usage
|
||||||
// non-generic User-Agent that names the software and carries a contact URL — a
|
// policy asks consumers to send a descriptive User-Agent that names the application
|
||||||
// generic default (curl, wget, Go-http-client) is refused. It uses the public Felis
|
// and carries contact info, and warns that generic/library-default agents (curl, wget,
|
||||||
// module path as the contact and contains no operator-specific serving domain; a
|
// Go-http-client) may be rate-limited or blocked. Enforcement was NOT active on the
|
||||||
// deployment can override it (paperMC.userAgent) with a SysAdmin contact from config.
|
// project-metadata endpoint as of 2026-07-04 — a bare UA still returned HTTP 200 — so
|
||||||
|
// sending this is documented etiquette and future-proofing, not an empirically
|
||||||
|
// confirmed hard gate. It uses the public Felis module path as the contact and
|
||||||
|
// contains no operator-specific serving domain; a deployment can override it
|
||||||
|
// (paperMC.userAgent) with a SysAdmin contact from config.
|
||||||
const defaultUserAgent = "felis-updater/0.1 (+https://felis.lolicon.best)"
|
const defaultUserAgent = "felis-updater/0.1 (+https://felis.lolicon.best)"
|
||||||
|
|
||||||
// paperMC discovers the latest STABLE version of a PaperMC project (Velocity, for
|
// paperMC discovers the latest STABLE version of a PaperMC project (Velocity, for
|
||||||
@@ -25,7 +29,7 @@ const defaultUserAgent = "felis-updater/0.1 (+https://felis.lolicon.best)"
|
|||||||
// network (see papermc_test.go, whose fixture is captured from the real v3 shape).
|
// network (see papermc_test.go, whose fixture is captured from the real v3 shape).
|
||||||
type paperMC struct {
|
type paperMC struct {
|
||||||
baseURL string // e.g. "https://fill.papermc.io"
|
baseURL string // e.g. "https://fill.papermc.io"
|
||||||
userAgent string // non-generic UA with a contact (Fill v3 requirement)
|
userAgent string // descriptive UA with a contact (PaperMC usage-policy etiquette)
|
||||||
hc *http.Client
|
hc *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,32 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// velocityV3Fixture is the PaperMC Fill v3 GET /v3/projects/velocity body — its shape
|
// velocityV3Fixture is the PaperMC Fill v3 GET /v3/projects/velocity response body,
|
||||||
// and version strings captured verbatim from the live API on 2026-07-04. Grounding
|
// captured from the live API on 2026-07-04 (keys and version strings exactly as
|
||||||
// the fixture in the real response is what makes this a contract test rather than a
|
// returned; JSON whitespace normalized). Grounding the fixture in the real response is
|
||||||
// self-referential one: the newest overall version is a -SNAPSHOT (3.5.0-SNAPSHOT)
|
// what makes this a contract test rather than a self-referential one:
|
||||||
// while the newest stable release is 3.4.0, so the stable filter is exercised against
|
// - the newest overall version is a -SNAPSHOT (3.5.0-SNAPSHOT) while the newest
|
||||||
// real-world data, not an invented shape. (The v2 API this replaces now returns 410.)
|
// stable release is 3.4.0, so the stable filter runs against real data; and
|
||||||
|
// - the "versions" object groups the ENTIRE 3.x line under a single key "3.0.0"
|
||||||
|
// (not per-minor keys), so a parser that trusted the group key to bound the
|
||||||
|
// versions inside it would be wrong — proof the key-agnostic flatten is required.
|
||||||
|
// (The v2 API this replaces now returns HTTP 410.)
|
||||||
const velocityV3Fixture = `{
|
const velocityV3Fixture = `{
|
||||||
"project": {"id": "velocity", "name": "Velocity"},
|
"project": {"id": "velocity", "name": "Velocity"},
|
||||||
"versions": {
|
"versions": {
|
||||||
"3.5": ["3.5.0-SNAPSHOT"],
|
"3.0.0": [
|
||||||
"3.4": ["3.4.0", "3.4.0-SNAPSHOT"],
|
"3.5.0-SNAPSHOT",
|
||||||
"3.3": ["3.3.0-SNAPSHOT"],
|
"3.4.0",
|
||||||
"3.2": ["3.2.0-SNAPSHOT"],
|
"3.4.0-SNAPSHOT",
|
||||||
"3.1": ["3.1.2-SNAPSHOT", "3.1.1", "3.1.1-SNAPSHOT", "3.1.0"],
|
"3.3.0-SNAPSHOT",
|
||||||
"1.1": ["1.1.9"],
|
"3.2.0-SNAPSHOT",
|
||||||
"1.0": ["1.0.10"]
|
"3.1.2-SNAPSHOT",
|
||||||
|
"3.1.1",
|
||||||
|
"3.1.1-SNAPSHOT",
|
||||||
|
"3.1.0"
|
||||||
|
],
|
||||||
|
"1.1.0": ["1.1.9"],
|
||||||
|
"1.0.0": ["1.0.10"]
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -61,8 +71,12 @@ func TestPaperMCLatestStableFiltersSnapshots(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPaperMCSendsNonGenericUserAgent proves Felis transmits the contact-carrying,
|
// TestPaperMCSendsNonGenericUserAgent proves Felis transmits a descriptive,
|
||||||
// non-generic User-Agent the Fill v3 API requires (a generic UA is refused upstream).
|
// contact-carrying User-Agent rather than a generic library default. PaperMC's API
|
||||||
|
// usage policy asks for this and reserves the right to block anonymous/generic agents;
|
||||||
|
// upstream enforcement was not active on the project endpoint as of 2026-07-04 (a bare
|
||||||
|
// UA got HTTP 200), so this verifies OUR compliance with the policy, not an upstream
|
||||||
|
// gate we depend on.
|
||||||
func TestPaperMCSendsNonGenericUserAgent(t *testing.T) {
|
func TestPaperMCSendsNonGenericUserAgent(t *testing.T) {
|
||||||
var gotUA string
|
var gotUA string
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
Reference in new issue
Block a user