package web
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"craftdesign.group/quarry/internal/ops"
)
// The section offers its form to a repository admin, with a password
// input that is never filled in, and says a blank keeps the saved one
// only while a mirror is on.
func TestFossilMirrorSettingsOfferTheFormWithoutThePassword(t *testing.T) {
d := pageData{Repo: "alpha", Caps: &capsPanel{}, RepoAdmin: true,
FossilMirror: &fossilMirrorPanel{Remote: "https://fossil.example.org/alpha", User: "pusher"}}
body := renderSettings(t, d)
for _, hook := range []string{`id="fossil-mirror"`, `data-hook="fossil-mirror-form"`, `data-mirroring="false"`} {
if !strings.Contains(body, hook) {
t.Errorf("FAIL: the page lacks %s", hook)
}
}
if strings.Contains(body, `data-hook="fossil-mirror-keeps"`) {
t.Error("FAIL: a mirror that is off says a password is kept")
}
pw := inputTag(body, "password")
if !strings.Contains(pw, `type="password"`) || strings.Contains(pw, "value=") || !strings.Contains(pw, "required") {
t.Errorf("FAIL: the first-enable password input: %q", pw)
}
d.FossilMirror.On = true
body = renderSettings(t, d)
pw = inputTag(body, "password")
if !strings.Contains(body, `data-mirroring="true"`) || !strings.Contains(body, `data-hook="fossil-mirror-keeps"`) ||
strings.Contains(pw, "value=") || strings.Contains(pw, "required") || !strings.Contains(body, `value="pusher"`) {
t.Errorf("FAIL: a mirror that is on: password input %q", pw)
}
if !strings.Contains(body, `name="do" value="off"`) {
t.Error("FAIL: no way to stop mirroring")
}
t.Log("ok")
}
// Only a repository admin sees the section; a private repository gets
// no form; a Git-following repository gets none, as for the Git mirror.
func TestFossilMirrorSectionIsGated(t *testing.T) {
panel := &fossilMirrorPanel{}
if body := renderSettings(t, pageData{Repo: "alpha", Caps: &capsPanel{}, FossilMirror: panel}); strings.Contains(body, `data-hook="fossil-mirror"`) {
t.Error("FAIL: a viewer without a sees the section")
}
if body := renderSettings(t, pageData{Repo: "alpha", Caps: &capsPanel{}, RepoAdmin: true, RepoPrivate: true, FossilMirror: panel}); !strings.Contains(body, `data-hook="fossil-mirror-private"`) ||
strings.Contains(body, `data-hook="fossil-mirror-form"`) {
t.Error("FAIL: a private repository offers a form")
}
d := pageData{Repo: "alpha", Caps: &capsPanel{}, RepoAdmin: true, FossilMirror: panel}
d.Posture.Mirror.Tracking, d.Posture.Mirror.Git = true, true
if body := renderSettings(t, d); strings.Contains(body, `data-hook="fossil-mirror"`) {
t.Error("FAIL: a repository that follows Git offers a Fossil mirror")
}
t.Log("ok")
}
// A state line says paused, failed or pushed, each under its own hook.
func TestFossilMirrorStateNamesWhyItIsNotPushing(t *testing.T) {
on := func(p fossilMirrorPanel) string {
p.On = true
return renderSettings(t, pageData{Repo: "alpha", Caps: &capsPanel{}, RepoAdmin: true, FossilMirror: &p})
}
if b := on(fossilMirrorPanel{Paused: "the repository is private, so the mirror is paused"}); !strings.Contains(b, `data-hook="fossil-mirror-paused"`) {
t.Error("FAIL: no paused hook")
}
if b := on(fossilMirrorPanel{LastErr: "the host could not be reached", LastErrAt: "2026-10-01T16:00:00Z"}); !strings.Contains(b, "ci-fail-text") {
t.Error("FAIL: a failure is not shown")
}
if b := on(fossilMirrorPanel{PushedAt: "2026-10-01T16:00:00Z", Pushed: "abcdef0123456789"}); !strings.Contains(b, "/checkin/abcdef0123456789") {
t.Error("FAIL: the pushed tip does not link to its check-in")
}
t.Log("ok")
}
// The flash that carries a refusal back removes a user name and password
// the typed remote held, even from words that quote them: this is the
// scrub itself, which the op's own refusals never reach.
func TestAFossilRefusalFlashScrubsTheRemotesCredentials(t *testing.T) {
const user, pass = "scrub-user-71", "scrub-pass-93"
remote := "https://" + user + ":" + pass + "@fossil.example.org/x"
err := fmt.Errorf("could not reach %s: %w", remote, ops.ErrInvalid)
w := httptest.NewRecorder()
flashFossilMirrorRefusal(w, httptest.NewRequest(http.MethodPost, "/", nil), err, remote)
cookie := strings.Join(w.Header().Values("Set-Cookie"), " ")
for _, secret := range []string{user, pass} {
if strings.Contains(cookie, secret) {
t.Errorf("FAIL: the flash carries %q: %s", secret, cookie)
}
}
words, _ := takeFossilMirrorRefusal(strings.TrimPrefix(strings.SplitN(cookie, ";", 2)[0], flashCookie+"="))
if words == "" {
t.Errorf("FAIL: the refusal did not survive the flash: %s", cookie)
}
t.Log("ok")
}