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))
}
}