86a78c6767
gates / gates (push) Successful in 14s
The orphan card told a customer their set-aside off-site history may be restorable later with their recovery code. The discriminator lives on the hub and no wire field carries it, so the box rendering that card cannot evaluate the promise. Copy replaced per the spec: state what happens, decline what we cannot know and say why, name a route. The claim page called the same three-word dashboard code two different names depending on branch, one of which collides with the ten-word escrow code. Retired 'Visszaallito kod'; the name is now constant and the sentence changes. Naming only - a test pins that a reset code is still accepted. secret_in_markup_gate no longer convicts Go template comments, which are stripped before render; still convicts a real rendered secret.
105 lines
5.1 KiB
Go
105 lines
5.1 KiB
Go
package web
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
)
|
|
|
|
// ── R-294 / R-202 — THE ORPHAN CARD STOPS PROMISING WHAT IT CANNOT KNOW ─────────────────────────
|
|
//
|
|
// The card told a customer, at the moment they had just lost their off-site history, that the old
|
|
// copies "may be restorable later with their recovery code". The discriminator is
|
|
// host_escrow_superseded.identity_blob and it lives on the HUB; the box caches only
|
|
// HubEscrowIdentityPresent, which describes the CURRENT escrow, and no report or ACK field carries
|
|
// superseded-blob retention. The renderer therefore could not evaluate the condition it stated.
|
|
//
|
|
// For everything set aside before hub v0.93.0 (in force 2026-08-04 ~11:11Z) the promise is false and
|
|
// unfixable, and on 2026-08-10 it was being made to a real machine in exactly that state.
|
|
//
|
|
// Implements documentation/design/SPEC-orphan-card-copy-2026-08-10.md §5. Render tests per branch of
|
|
// the gate, because a template gate without one is the seam-wiring lesson.
|
|
|
|
// orphanCardData renders the backups_remote page with the offbox repo in the given state. Only
|
|
// RepoState varies between the branches below, so what each test proves is attributable.
|
|
func orphanCardData(repoState string) map[string]interface{} {
|
|
d := splitTestData()
|
|
d["Offbox"] = &settings.OffboxTarget{
|
|
Enabled: true, Host: "nas.local", User: "felhom", RepoPath: "/srv/repo",
|
|
EscrowState: "escrowed", RepoState: repoState, QuotaGB: 50, StatsKnown: true,
|
|
}
|
|
d["OffboxQuotaPct"] = 0
|
|
return d
|
|
}
|
|
|
|
// ── Branch 1: the card IS shown ─────────────────────────────────────────────────────────────────
|
|
|
|
// SPEC §5.1 — the regression guard. The promise must not return in any form.
|
|
//
|
|
// RED-PROOF: restore the old sentence („…és a hozzá tartozó helyreállítási kóddal később
|
|
// visszaállítható lehet.") in backups_remote.html and this fails on the first assertion, with the
|
|
// promise quoted back in the failure message.
|
|
func TestOrphanCard_DoesNotPromiseRestorability(t *testing.T) {
|
|
html := renderBackupPage(t, "backups_remote", orphanCardData("orphaned"))
|
|
|
|
if !strings.Contains(html, "offbox-orphan-card") {
|
|
t.Fatal("the orphan card did not render at all — this test would then pass vacuously, " +
|
|
"which is the way a copy guard silently stops guarding")
|
|
}
|
|
if strings.Contains(html, "visszaállítható lehet") {
|
|
t.Error("R-294: the card still promises the set-aside copies may be restorable later. The box " +
|
|
"cannot evaluate that — the discriminator (superseded identity_blob) is on the hub and no " +
|
|
"wire field carries it — and for everything set aside before 2026-08-04 it is false")
|
|
}
|
|
}
|
|
|
|
// SPEC §5.2 — a refusal that names no route is a defect in this project. Pin the route, not only the
|
|
// absence of the promise.
|
|
//
|
|
// RED-PROOF: delete the „írj nekünk" sentence and this fails — the customer is told we cannot promise
|
|
// anything and given nowhere to go.
|
|
func TestOrphanCard_NamesARouteAfterDeclining(t *testing.T) {
|
|
html := renderBackupPage(t, "backups_remote", orphanCardData("orphaned"))
|
|
|
|
if !strings.Contains(html, "nem tudjuk megígérni") {
|
|
t.Error("R-294: the card no longer DECLINES the claim — stating nothing is not the same as " +
|
|
"saying plainly that we cannot promise it")
|
|
}
|
|
if !strings.Contains(html, "írj nekünk") {
|
|
t.Error("R-294: the card declines the promise but names no route the customer can take")
|
|
}
|
|
// It must still say the copy is kept — otherwise "we cannot promise" reads as "it is gone".
|
|
if !strings.Contains(html, "nem töröljük") {
|
|
t.Error("R-294: the card no longer says the set-aside copy is kept; without that, declining " +
|
|
"the promise reads to the customer as data loss")
|
|
}
|
|
}
|
|
|
|
// The reason the card exists — the orphan EXPLANATION — is accurate and must survive the copy change.
|
|
func TestOrphanCard_KeepsTheExplanation(t *testing.T) {
|
|
html := renderBackupPage(t, "backups_remote", orphanCardData("orphaned"))
|
|
|
|
if !strings.Contains(html, "korábbi, már nem elérhető kulccsal") {
|
|
t.Error("the explanation of WHY the store is orphaned was lost — the customer is then shown a " +
|
|
"refusal with no cause")
|
|
}
|
|
}
|
|
|
|
// ── Branch 2: the card is NOT shown ─────────────────────────────────────────────────────────────
|
|
|
|
// SPEC §5.3 — per branch of the gate. A healthy store must not see any of this copy: R-215's shape is
|
|
// a screen about a situation the customer is not in.
|
|
func TestOrphanCard_HealthyStoreSeesNoneOfIt(t *testing.T) {
|
|
html := renderBackupPage(t, "backups_remote", orphanCardData("ok"))
|
|
|
|
if strings.Contains(html, "offbox-orphan-card") {
|
|
t.Fatal("the orphan card rendered for a healthy store")
|
|
}
|
|
for _, s := range []string{"nem tudjuk megígérni", "írj nekünk", "félretéve marad"} {
|
|
if strings.Contains(html, s) {
|
|
t.Errorf("orphan copy %q leaked onto a healthy box's page", s)
|
|
}
|
|
}
|
|
}
|