package web import ( "bytes" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" "gitea.dooplex.hu/admin/felhom-controller/internal/stacks" ) // R-351 — RENDER TESTS, ONE PER BRANCH. // // A green build and a green vet say NOTHING about a template: `index` against an undefined key, or a // method with a pointer receiver, both compile, both pass vet, both pass the suite, and both 500 at // render. This repo has that on file twice ("template methods need value receivers", "seam built but // never wired"). The existing deploy render test does not reach these branches — it renders a page // with only AutoFields, so the subdomain and path blocks never execute — which is precisely how a // broken branch stays green. // // Every case below therefore RENDERS and asserts on the HTML. const ( prefillDataDrive = "/mnt/felhom-drives/hdd_1" prefillOtherDriv = "/mnt/felhom-drives/hdd_2" prefillSysDrive = "/mnt/sys_drive" ) // renderDeployWithFields renders the deploy page for an app that HAS user-facing fields, so the // subdomain and path branches are actually executed. func renderDeployWithFields(t *testing.T, declaresPath bool, extra map[string]interface{}) string { t.Helper() s := securityHarness(t) s.loadTemplates() fields := []stacks.DeployField{ {EnvVar: "SUBDOMAIN", Label: "Aldomain", Type: "subdomain", Default: "katalogus-alap"}, } if declaresPath { fields = append(fields, stacks.DeployField{EnvVar: "HDD_PATH", Label: "Adatmeghajto", Type: "path"}) } data := map[string]interface{}{ "Page": "stacks", "Title": "Telepites", "Domain": "example.hu", "Stack": stacks.Stack{Name: "demoapp"}, "Meta": stacks.Metadata{DisplayName: "DemoApp", Slug: "demoapp"}, "AlreadyDeployed": false, "UserFields": fields, "StoragePaths": []DeployStoragePath{ {StoragePath: settings.StoragePath{Path: prefillDataDrive, Label: "USB 1"}, FreeHuman: "100 GB", FreePercent: 50}, {StoragePath: settings.StoragePath{Path: prefillOtherDriv, Label: "USB 2", IsDefault: true}, FreeHuman: "200 GB", FreePercent: 80}, }, // The defaults the handler always sets. A test that omitted them would be testing a page the // handler never produces. "RestoreFieldValues": map[string]string{}, "RestorePrefillHDDPath": "", "RestoreRecordedDrive": "", "RestoreRecordedAddress": "", "RestoreRecordedDeclaresPath": declaresPath, "RestoreHasRecord": false, "SystemDataPath": prefillSysDrive, } for k, v := range extra { data[k] = v } var buf bytes.Buffer if err := s.tmpl.ExecuteTemplate(&buf, "deploy", data); err != nil { t.Fatalf("render deploy: %v", err) } return buf.String() } // SCENARIO D — the ordinary path. No backup record: the catalog default stands and the configured // default drive stays selected. WRONG OUTCOME: a new question or obstacle where there was none. func TestDeployPrefill_NoRecord_LeavesTheOrdinaryPathAlone(t *testing.T) { html := renderDeployWithFields(t, true, nil) if !strings.Contains(html, `value="katalogus-alap"`) { t.Error("with no recorded address the catalog default must still fill the subdomain field") } // The configured default drive keeps its selection. if !strings.Contains(html, `value="`+prefillOtherDriv+`" data-free-percent="80"`) { t.Fatal("fixture: the default drive option must render, or the assertion below proves nothing") } if !optionSelected(html, prefillOtherDriv) { t.Error("with no record the configured default drive must remain the selected option") } if strings.Contains(html, "saját mentése alapján") { t.Error("no record means no prefill notice — claiming one would be a fabricated fact") } } // SCENARIO A — the record exists and is offered back, visibly labelled as coming from the backup. func TestDeployPrefill_WithRecord_OffersTheRecordedValuesAndSaysWhy(t *testing.T) { html := renderDeployWithFields(t, true, map[string]interface{}{ "RestoreFieldValues": map[string]string{"SUBDOMAIN": "konyvek", "DOMAIN": "example.hu", "HDD_PATH": prefillDataDrive}, "RestorePrefillHDDPath": prefillDataDrive, "RestoreRecordedDrive": prefillDataDrive, "RestoreRecordedAddress": "konyvek.example.hu", "RestoreHasRecord": true, }) if !strings.Contains(html, `value="konyvek"`) { t.Error("the recorded subdomain must be prefilled — this is the value the person had to remember") } if strings.Contains(html, `value="katalogus-alap"`) { t.Error("the catalog default must NOT win over the customer's own recorded answer") } if !optionSelected(html, prefillDataDrive) { t.Error("the drive the BACKUP recorded must be preselected, not the configured default") } if optionSelected(html, prefillOtherDriv) { t.Error("the configured default must not also be selected — two selected options is a broken form") } // The origin must be stated. An unexplained prefill is indistinguishable from a default. for _, must := range []string{"saját mentése alapján", "konyvek.example.hu", prefillDataDrive} { if !strings.Contains(html, must) { t.Errorf("the notice must state %q so the prefill is not mistaken for a default", must) } } // A MEMORY, NOT A LOCK. The whole ruling turns on the customer still being able to change these, // so the input itself must carry neither `disabled` nor `readonly`. if tag, ok := inputTag(html, "SUBDOMAIN"); !ok { t.Error("the subdomain input must render, or the prefill has nowhere to live") } else if strings.Contains(tag, "disabled") || strings.Contains(tag, "readonly") { t.Errorf("the prefill is a memory, not a lock — the field must stay editable; got: %s", tag) } } // inputTag returns the rendered tag for a field, so an assertion can be scoped to it rather // than searching the whole page (where `disabled` legitimately appears on other controls). func inputTag(html, envVar string) (string, bool) { i := strings.Index(html, `id="field-`+envVar+`"`) if i < 0 { return "", false } end := strings.Index(html[i:], ">") if end < 0 { return "", false } return html[i : i+end], true } // THE 40-OF-53 CLASS — no storage field exists, so the placement is STATED as a fact and never // written into an input that is not there. Part 1's visibility line names the system drive. func TestDeployPrefill_NoDeclaredPath_StatesWhereTheDataGoes(t *testing.T) { html := renderDeployWithFields(t, false, map[string]interface{}{ "RestoreRecordedDrive": prefillSysDrive, "RestoreRecordedAddress": "gist.example.hu", "RestoreHasRecord": true, "RestoreFieldValues": map[string]string{"SUBDOMAIN": "gist", "DOMAIN": "example.hu"}, }) if strings.Contains(html, `name="HDD_PATH"`) { t.Error("an app that declares no data path must not grow a storage field from the prefill") } if !strings.Contains(html, "rendszermeghajtóra") || !strings.Contains(html, prefillSysDrive) { t.Error("Part 1: the page must say where the data will live BEFORE the button is pressed") } if !strings.Contains(html, `value="gist"`) { t.Error("the recorded address still applies to this class — only the folder has no field") } } // Part 1's line must appear for the declaring class too, pointing at the selection. Its absence on // one branch is how "we told the customer" becomes true only half the time. func TestDeployPrefill_DeclaredPath_StillSaysWhereTheDataGoes(t *testing.T) { html := renderDeployWithFields(t, true, nil) if !strings.Contains(html, "kiválasztott adatmeghajtóra") { t.Error("an app WITH a storage field must still be told that the choice is where its data lands") } if strings.Contains(html, "rendszermeghajtóra") { t.Error("the system-drive sentence belongs only to apps with no storage field") } } // optionSelected reports whether the