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