0fe04bb6d5
gates / gates (push) Successful in 28s
The 2026-09-20 English drill ended one screen short: the claim page was English and its answers were Hungarian, so a household who mistyped the code from their e-mail could not tell a typo from a dead code. Fourteen call sites carrying nine messages now go through s.msg; the backup page's two protection warnings — which are promises about whether the customer's files are safe — follow the same route. Hungarian is byte-identical, proved structurally by the go-parity gate against the frozen base capture and red-proofed on a single added full stop. data["Title"] was DEAD (claim.html is standalone; .Title is layout.html's) and is deleted rather than translated — a translated dead field is a permanent false signal about where the page's title comes from. Six existing copy-contract tests were kept, not weakened: each now resolves its key through the real bundle, so it still convicts on a reworded Hungarian sentence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
218 lines
10 KiB
Go
218 lines
10 KiB
Go
package web
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/i18n"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
)
|
|
|
|
// R-114 — the third state: CONFIGURED, and its drive is GONE.
|
|
//
|
|
// THE BUG THESE PIN, measured live in E-2d (felhom.eu audits/E2D-fresh-vm-2026-07-29.md §5.3). With
|
|
// the assigned target's drive detached, the endpoint returned:
|
|
//
|
|
// {"degraded":true,"target":"felhom-backup",
|
|
// "message":"A rendszermentés jelenleg ugyanazon a lemezen van, mint a rendszer …",
|
|
// "offer_path":"/mnt/felhom-drives/mentes2","offer_label":"Mentés meghajtó"}
|
|
//
|
|
// Two falsehoods in one payload. The backup was NOT on the system disk — it was on a drive that had
|
|
// vanished. And the remedy offered was THE DRIVE THAT JUST DISAPPEARED. resolveBackupTargetState had
|
|
// only two outcomes (a disk claims the target, or nothing does) so the third state fell into the
|
|
// second and inherited its message and its offer.
|
|
//
|
|
// The fixtures below are the observed shapes, not invented ones: the drive is still listed with a
|
|
// MountPath (the registry keeps the configured path), Role user-data, but BackupTarget is false —
|
|
// which is exactly why the healthy branch missed it and the offer branch matched it.
|
|
|
|
// absentHarness wires a Server whose agent reports `primary` as the tier target and `disks` as the
|
|
// drive list, with one registered storage path so firstOfferableDrive has something to find. Both
|
|
// halves come through the production seams (tiersFn / disksFn), so the resolver under test is the
|
|
// real one.
|
|
func absentHarness(t *testing.T, primary string, disks []agentapi.DiskInfo, registerPath string) *Server {
|
|
t.Helper()
|
|
s := testServer(t)
|
|
if registerPath != "" {
|
|
if err := s.settings.AddStoragePath(settings.StoragePath{Path: registerPath, Label: "Mentés meghajtó"}); err != nil {
|
|
t.Fatalf("register storage path: %v", err)
|
|
}
|
|
}
|
|
s.tiersFn = func(context.Context) (agentapi.TiersResponse, error) {
|
|
return agentapi.TiersResponse{Tiers: []agentapi.BackupTierInfo{{Target: primary, Primary: true}}}, nil
|
|
}
|
|
s.disksFn = func(context.Context) (agentapi.DisksResponse, error) {
|
|
return agentapi.DisksResponse{Disks: disks}, nil
|
|
}
|
|
return s
|
|
}
|
|
|
|
// huText renders a bundle KEY into the Hungarian a household actually reads.
|
|
//
|
|
// v0.259.0 (R-598) turned the three backup-protection constants into bundle keys, so every
|
|
// assertion below would otherwise have quietly become an assertion about key SPELLING — the exact
|
|
// hollowing this repo has paid for before. Resolving through the real embedded bundle keeps them
|
|
// assertions about the WORDS, and fails loudly if a key is ever dropped from hu.json.
|
|
func huText(t *testing.T, key string) string {
|
|
t.Helper()
|
|
if key == "" {
|
|
return ""
|
|
}
|
|
b, err := i18n.Shared()
|
|
if err != nil {
|
|
t.Fatalf("i18n bundle: %v", err)
|
|
}
|
|
got := b.Msg("hu", key)
|
|
if got == key {
|
|
t.Fatalf("the bundle does not know %q — it would render the KEY onto the page", key)
|
|
}
|
|
return got
|
|
}
|
|
|
|
// theVanishedDrive is the E-2d shape: registered, still carrying its configured MountPath, user-data,
|
|
// but no longer claiming the backup target because its device is gone.
|
|
var theVanishedDrive = agentapi.DiskInfo{
|
|
Name: "mentes2",
|
|
MountPath: "/mnt/mentes2",
|
|
GuestPath: "/mnt/felhom-drives/mentes2",
|
|
Role: "user-data",
|
|
BackupTarget: false,
|
|
}
|
|
|
|
// ── Scenario C — configured, drive absent ───────────────────────────────────────────────────────
|
|
|
|
// RED-PROOF: delete the `if targetIsConfiguredDrive(primary)` branch from resolveBackupTargetState
|
|
// and this fails twice — the message becomes the system-disk copy and OfferPath becomes non-empty.
|
|
func TestConfiguredButAbsentTargetDoesNotClaimTheSystemDisk(t *testing.T) {
|
|
s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{theVanishedDrive}, "/mnt/felhom-drives/mentes2")
|
|
st := s.resolveBackupTargetState(context.Background())
|
|
|
|
if !st.Known {
|
|
t.Fatal("state is unknown — the fixture answers both agent calls")
|
|
}
|
|
if !st.TargetAbsent {
|
|
t.Fatalf("TargetAbsent is false for target %q with no disk claiming it — the configured-but-gone "+
|
|
"state fell back into never-configured, which is the R-114 bug", st.TargetID)
|
|
}
|
|
msg := huText(t, degradedMessageFor(st))
|
|
if strings.Contains(msg, "ugyanazon a lemezen") {
|
|
t.Errorf("the customer is told the backup is on the SYSTEM DISK, which is false — the target is "+
|
|
"%q, a drive that has vanished. Got: %s", st.TargetID, msg)
|
|
}
|
|
if !strings.Contains(msg, "nem érhető el") {
|
|
t.Errorf("the absent-drive copy is missing; got: %s", msg)
|
|
}
|
|
}
|
|
|
|
// The remedy must be "reconnect THAT drive", never "pick a different one" — and above all never the
|
|
// drive that just disappeared, which is what E-2d actually observed being offered.
|
|
func TestConfiguredButAbsentTargetOffersNothing(t *testing.T) {
|
|
s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{theVanishedDrive}, "/mnt/felhom-drives/mentes2")
|
|
st := s.resolveBackupTargetState(context.Background())
|
|
|
|
if st.OfferPath != "" {
|
|
t.Errorf("offered %q while the configured target is absent — E-2d observed this exact payload "+
|
|
"offering the drive that had just vanished", st.OfferPath)
|
|
}
|
|
if st.OfferLabel != "" {
|
|
t.Errorf("offer label %q leaked with no offer path", st.OfferLabel)
|
|
}
|
|
}
|
|
|
|
// The suppression must be the branch's own doing, NOT a side effect of the drive being flagged
|
|
// Disconnected — that flag is set by the agent-side drive gate in another repo (R-113), and this
|
|
// state has to be correct before, during and independently of that landing.
|
|
func TestAbsentTargetSuppressesOfferWithoutRelyingOnTheDisconnectedFlag(t *testing.T) {
|
|
s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{theVanishedDrive}, "/mnt/felhom-drives/mentes2")
|
|
for _, sp := range s.settings.GetStoragePaths() {
|
|
if sp.Disconnected {
|
|
t.Fatalf("fixture invalid: %s is already marked Disconnected, so this test would pass "+
|
|
"for the wrong reason", sp.Path)
|
|
}
|
|
}
|
|
if st := s.resolveBackupTargetState(context.Background()); st.OfferPath != "" {
|
|
t.Errorf("offer %q survived with Disconnected UNSET — the suppression is leaning on the "+
|
|
"other repo's flag instead of on this state", st.OfferPath)
|
|
}
|
|
}
|
|
|
|
// ── Scenario A — never configured (must be unchanged) ───────────────────────────────────────────
|
|
|
|
func TestNeverConfiguredStillSaysSystemDiskAndStillOffers(t *testing.T) {
|
|
s := absentHarness(t, builtinLocalTarget, []agentapi.DiskInfo{{
|
|
Name: "hdd1", MountPath: "/mnt/hdd1", GuestPath: "/mnt/felhom-drives/hdd1", Role: "user-data",
|
|
}}, "/mnt/felhom-drives/hdd1")
|
|
st := s.resolveBackupTargetState(context.Background())
|
|
|
|
if st.TargetAbsent {
|
|
t.Fatal("target `local` marked absent — the builtin root-fs storage is the NEVER-CONFIGURED " +
|
|
"state, not a drive that went missing")
|
|
}
|
|
if !st.Degraded {
|
|
t.Fatal("a backup on the system disk must still read degraded")
|
|
}
|
|
if msg := huText(t, degradedMessageFor(st)); !strings.Contains(msg, "ugyanazon a lemezen") {
|
|
t.Errorf("the never-configured copy changed; got: %s", msg)
|
|
}
|
|
if st.OfferPath == "" {
|
|
t.Error("no offer in the never-configured state — this is exactly where an offer belongs")
|
|
}
|
|
}
|
|
|
|
// An unset tier is the same customer situation as `local`: nothing chosen yet, so it must not be
|
|
// reported as a drive that vanished.
|
|
func TestUnsetTierIsNeverConfiguredNotAbsent(t *testing.T) {
|
|
if targetIsConfiguredDrive("") {
|
|
t.Error("an empty target id was treated as a configured drive")
|
|
}
|
|
if targetIsConfiguredDrive(builtinLocalTarget) {
|
|
t.Error("the builtin `local` was treated as a configured drive")
|
|
}
|
|
if !targetIsConfiguredDrive("felhom-backup") {
|
|
t.Error("a real storage id was not treated as a configured drive")
|
|
}
|
|
}
|
|
|
|
// ── Scenario B — healthy still renders nothing ──────────────────────────────────────────────────
|
|
|
|
func TestHealthyTargetIsNeitherDegradedNorAbsent(t *testing.T) {
|
|
s := absentHarness(t, "felhom-backup", []agentapi.DiskInfo{{
|
|
Name: "mentes2", MountPath: "/mnt/mentes2", GuestPath: "/mnt/felhom-drives/mentes2",
|
|
Role: "user-data", BackupTarget: true,
|
|
}}, "/mnt/felhom-drives/mentes2")
|
|
st := s.resolveBackupTargetState(context.Background())
|
|
|
|
if st.Degraded || st.TargetAbsent {
|
|
t.Fatalf("a healthy target reported degraded=%v absent=%v", st.Degraded, st.TargetAbsent)
|
|
}
|
|
if msg := huText(t, degradedMessageFor(st)); msg != "" {
|
|
t.Errorf("healthy produced copy %q — a working box must look normal", msg)
|
|
}
|
|
}
|
|
|
|
// ── the copy contract ───────────────────────────────────────────────────────────────────────────
|
|
|
|
// The absent copy is VERBATIM the hub's customerMessages["backup_target_absent"]. If either side is
|
|
// reworded this fails, which is the only thing currently binding the two repos' strings together.
|
|
func TestAbsentCopyMatchesTheHubEmailWordForWord(t *testing.T) {
|
|
const hubCopy = "A rendszermentés meghajtója nem érhető el — amíg vissza nem " +
|
|
"csatlakoztatod, a teljes rendszermentés nem készül el."
|
|
banner := huText(t, backupTargetAbsentKey)
|
|
if banner != hubCopy {
|
|
t.Errorf("the banner and the email now tell the customer different stories about one drive.\n"+
|
|
" banner: %s\n email : %s", banner, hubCopy)
|
|
}
|
|
msg := huText(t, degradedMessageFor(BackupTargetState{Known: true, Degraded: true, TargetAbsent: true}))
|
|
for _, want := range []struct{ frag, why string }{
|
|
{"nem érhető el", "the FACT — the drive cannot be reached"},
|
|
{"nem készül el", "the CONSEQUENCE — the full system backup does not happen"},
|
|
{"vissza nem csatlakoztatod", "the REMEDY — reconnect it"},
|
|
} {
|
|
if !strings.Contains(msg, want.frag) {
|
|
t.Errorf("absent copy is missing %s (%q); got: %s", want.why, want.frag, msg)
|
|
}
|
|
}
|
|
}
|