Skip to content
quarry Artifact [bceb5a45df]
Login

Artifact [bceb5a45df]

Artifact bceb5a45dfeb71ece038b3ad4d34bac997620c47054c69bd466f7c7407d675dc:


package web

import (
	"bytes"
	"net/http"
	"strings"
	"testing"
	"time"
)

// The tier leads the row: a local ticket's first chip says so, because
// "not yet in the permanent history" changes what every other chip on
// the row means.
func TestLocalChipLeadsTheRow(t *testing.T) {
	rows := ticketRows("tk", []ticketSummary{
		{ID: "aaaa11112222", Title: "from a visitor", Status: "Open", Local: true},
	}, nil)
	if len(rows) != 1 || len(rows[0].Chips) == 0 || rows[0].Chips[0].Text != "local" {
		t.Fatalf("rows = %+v", rows)
	}
}

// is:local is where a reader will try to spell the tier, so the query
// box honours it — both directions.
func TestIsLocalToken(t *testing.T) {
	v := ticketParseQuery("is:local crash")
	if v.Get("origin") != "local" || v.Get("title") != "crash" || v.Get("state") != "" {
		t.Fatalf("parse = %v", v)
	}
	rep := ticketReportOf(v, append([]string{"origin"}, ticketAxes...))
	if got := ticketQueryString("", rep, "crash"); got != "is:local crash" {
		t.Fatalf("round trip = %q", got)
	}
}

// The ticket page banners a local ticket and offers promote/discard to
// an admin — and only to an admin.
func TestLocalTicketBanner(t *testing.T) {
	base := pageData{Repo: "tk", Ticket: ticketDetail{ID: strings.Repeat("ab", 20)}, TicketLocal: true}
	var buf bytes.Buffer
	admin := base
	admin.CanAdmin = true
	if err := ticketPage.ExecuteTemplate(&buf, "content", admin); err != nil {
		t.Fatal(err)
	}
	html := buf.String()
	for _, want := range []string{"tkt-local", "Promote to fossil", "/promote", "/discard"} {
		if !strings.Contains(html, want) {
			t.Fatalf("admin view missing %q:\n%.500s", want, html)
		}
	}
	buf.Reset()
	if err := ticketPage.ExecuteTemplate(&buf, "content", base); err != nil {
		t.Fatal(err)
	}
	html = buf.String()
	if !strings.Contains(html, "tkt-local") || strings.Contains(html, "Promote to fossil") {
		t.Fatal("a non-admin saw the promote button, or no banner at all")
	}
}

// The new-ticket form carries the honeypot and defaults to the local
// door; only a repository ADMIN is offered the fossil checkbox — a
// writer files locally and an admin promotes what belongs.
func TestNewTicketFormDoors(t *testing.T) {
	var buf bytes.Buffer
	if err := ticketNewPage.ExecuteTemplate(&buf, "content", pageData{Repo: "tk", CanWrite: true, CanAdmin: true}); err != nil {
		t.Fatal(err)
	}
	html := buf.String()
	for _, want := range []string{`name="website"`, `name="fossil"`, "start local"} {
		if !strings.Contains(html, want) {
			t.Fatalf("admin form missing %q", want)
		}
	}
	buf.Reset()
	if err := ticketNewPage.ExecuteTemplate(&buf, "content", pageData{Repo: "tk", CanWrite: true}); err != nil {
		t.Fatal(err)
	}
	if strings.Contains(buf.String(), `name="fossil"`) {
		t.Fatalf("a writer is offered the fossil checkbox")
	}
	buf.Reset()
	if err := ticketNewPage.ExecuteTemplate(&buf, "content", pageData{Repo: "tk"}); err != nil {
		t.Fatal(err)
	}
	if strings.Contains(buf.String(), `name="fossil"`) {
		t.Fatal("a visitor was offered the fossil door")
	}
}

func TestIntakeRateLimit(t *testing.T) {
	now := time.Now()
	addr := "test-10.9.9.9"
	for i := 0; i < 5; i++ {
		if !intakeAllowed(addr, now) {
			t.Fatalf("filing %d refused", i+1)
		}
	}
	if intakeAllowed(addr, now) {
		t.Fatal("the sixth filing inside an hour passed")
	}
	if !intakeAllowed(addr, now.Add(2*time.Hour)) {
		t.Fatal("the window never reopened")
	}
	if !intakeAllowed("test-other", now) {
		t.Fatal("one loud address silenced another")
	}
}

func TestRemoteAddrPrefersForwardHeader(t *testing.T) {
	req, _ := http.NewRequest("GET", "/", nil)
	req.RemoteAddr = "127.0.0.1:9999"
	if remoteAddr(req) != "127.0.0.1" {
		t.Fatalf("addr = %q", remoteAddr(req))
	}
	req.Header.Set("X-Forwarded-For", "203.0.113.9, 127.0.0.1")
	if remoteAddr(req) != "203.0.113.9" {
		t.Fatalf("forwarded addr = %q", remoteAddr(req))
	}
}