package web
import (
"fmt"
"testing"
)
// fakeTallies is a counter plane holding whatever a test puts in it.
type fakeTallies struct {
n map[string]int64
err error
}
func (f fakeTallies) Total(repo, name string) (int64, error) {
return f.n[repo+"/"+name], nil
}
func (f fakeTallies) Totals(repo, prefix string) (map[string]int64, error) {
if f.err != nil {
return nil, f.err
}
head := repo + "/" + prefix + "."
out := map[string]int64{}
for k, v := range f.n {
if len(k) > len(head) && k[:len(head)] == head {
out[k[len(head):]] = v
}
}
return out, nil
}
func sample() fakeTallies {
return fakeTallies{n: map[string]int64{
"alpha/clone": 341,
"alpha/fetch": 12486,
"alpha/clone.anon": 293,
"alpha/clone.auth.zeitgeist": 31,
"alpha/clone.auth.alice": 8,
"alpha/clone.auth.bob": 5,
"alpha/clone.claim.dave": 2,
"alpha/clone.client.zit._": 12,
"alpha/clone.client.zit.22900": 271,
"alpha/clone.client.fossil.22800": 23,
}}
}
// The three identity states arrive in one list, and their ORDER is the
// claim the page makes: proven names, then asserted ones, then everyone
// who said nothing. A claim sorted in among accounts by size would read
// as an account.
func TestClaimsNeverSortInAmongAccounts(t *testing.T) {
v := trafficFor(sample(), "alpha", "clone")
var got []string
for _, r := range v.Readers {
got = append(got, fmt.Sprintf("%s/%s/%d", r.Kind, r.Label, r.Count))
}
want := []string{
"auth/zeitgeist/31", "auth/alice/8", "auth/bob/5",
"claim/dave/2",
"anon/unidentified/293",
}
if len(got) != len(want) {
t.Fatalf("FAIL: readers = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("FAIL: readers = %v, want %v", got, want)
}
}
// dave has fewer clones than bob, so a size-only sort would still
// put him last among the names. Pin the rule rather than the
// accident: a BUSY claim still comes after the smallest account.
busy := sample()
busy.n["alpha/clone.claim.dave"] = 9999
v = trafficFor(busy, "alpha", "clone")
last := v.Readers[len(v.Readers)-2] // before the unidentified row
if !last.Claimed() || last.Label != "dave" {
t.Errorf("FAIL: a 9999-count claim sorted to %+v, want it after every account", last)
}
}
// Identified counts only what was VERIFIED. A claim added in would be
// the page stating a forgery as a fact in its headline number.
func TestIdentifiedCountsOnlyVerifiedAccounts(t *testing.T) {
v := trafficFor(sample(), "alpha", "clone")
if v.Identified != 44 {
t.Errorf("FAIL: identified = %d, want 44 (31+8+5, and never dave's 2)", v.Identified)
}
if v.Unidentified != 293 {
t.Errorf("FAIL: unidentified = %d, want 293", v.Unidentified)
}
if v.Clones != 341 || v.Fetches != 12486 {
t.Errorf("FAIL: totals = %d clones, %d fetches; want 341 and 12486", v.Clones, v.Fetches)
}
}
// A client that sent no version pragma is a real shape on the wire. It
// must render as the client, not as the sanitizer's underscore.
func TestAClientWithoutAVersionIsNamedAnyway(t *testing.T) {
v := trafficFor(sample(), "alpha", "clone")
var got []string
for _, r := range v.Clients {
got = append(got, fmt.Sprintf("%s/%d", r.Label, r.Count))
}
want := []string{"zit 22900/271", "fossil 22800/23", "zit/12"}
if len(got) != len(want) {
t.Fatalf("FAIL: clients = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("FAIL: clients = %v, want %v", got, want)
}
}
}
// Bars are relative to the biggest row, not to the total: with one row
// at 293 and the rest in single figures, shares would draw every real
// reader as nothing at all.
func TestEveryRealReaderGetsAVisibleBar(t *testing.T) {
v := trafficFor(sample(), "alpha", "clone")
for _, r := range v.Readers {
if r.Count > 0 && r.Pct < 1 {
t.Errorf("FAIL: %s has %d and a bar of %d%%", r.Label, r.Count, r.Pct)
}
if r.Pct > 100 {
t.Errorf("FAIL: %s has a bar of %d%%", r.Label, r.Pct)
}
}
if v.Readers[len(v.Readers)-1].Pct != 100 {
t.Error("FAIL: the biggest row is not full width")
}
}
// A repository nobody has cloned renders empty rather than breaking,
// and a counter plane that errors renders no breakdown rather than a
// wrong one — the totals beside it are still true.
func TestAnUntouchedRepositoryAndABrokenPlane(t *testing.T) {
v := trafficFor(fakeTallies{n: map[string]int64{}}, "quiet", "clone")
if v.Clones != 0 || len(v.Readers) != 0 || len(v.Clients) != 0 {
t.Errorf("FAIL: an untouched repository = %+v, want empty", v)
}
broken := sample()
broken.err = fmt.Errorf("counter plane is away")
v = trafficFor(broken, "alpha", "clone")
if len(v.Readers) == 0 || v.Readers[0].Kind != "anon" {
t.Errorf("FAIL: a broken plane gave readers %+v, want only the unidentified row", v.Readers)
}
if v.Clones != 341 {
t.Errorf("FAIL: a broken breakdown lost the total: %d", v.Clones)
}
if v.Identified != 0 {
t.Errorf("FAIL: a broken plane invented %d identified", v.Identified)
}
}
// A forge with no counter plane at all must render the page, not panic.
func TestNoCounterPlaneAtAll(t *testing.T) {
v := trafficFor(nil, "alpha", "clone")
if v.Clones != 0 || len(v.Readers) != 0 {
t.Errorf("FAIL: no plane gave %+v, want empty", v)
}
}