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) } } }