v0.185.1 — E-2: the offer endpoints were mounted where nothing routed to them

Registered as /api/backup-target inside ServeStorageAPI, which main.go mounts ONLY
at /api/storage/. Live result: endpoint not found, while every unit test passed --
the tests called the handlers directly and never travelled the mount. Caught by
the first live call, which is why the live call is part of the procedure.

Moved to /api/storage/backup-target[/assign]. A new test asserts the dispatcher
source contains both paths, so a handler nothing routes to fails the suite --
the seam-wiring rule applied to a route rather than a button.
This commit is contained in:
2026-07-29 09:13:13 +02:00
parent 3f7cf2a965
commit cdaeb36972
4 changed files with 53 additions and 4 deletions
@@ -136,7 +136,7 @@ func degradedMessageFor(st BackupTargetState) string {
return backupTargetDegradedText
}
// handleBackupTargetState serves GET /api/backup-target — the dashboard's source for the degraded
// handleBackupTargetState serves GET /api/storage/backup-target — the dashboard's source for the degraded
// banner and the offer.
func (s *Server) handleBackupTargetState(w http.ResponseWriter, r *http.Request) {
st := s.resolveBackupTargetState(r.Context())
@@ -157,7 +157,7 @@ func (s *Server) handleBackupTargetState(w http.ResponseWriter, r *http.Request)
writeDiskJSON(w, http.StatusOK, true, "", out)
}
// handleBackupTargetAssign is the ACCEPTANCE of the offer — POST /api/backup-target/assign.
// handleBackupTargetAssign is the ACCEPTANCE of the offer — POST /api/storage/backup-target/assign.
//
// NOTHING calls this except an explicit customer action. Registration does not, the drive-gate does
// not, and no scheduler does (E-2 §3: a drive never acquires a role by appearing). Declining is