| 1 | package home |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | func TestPersonRefIsOpaqueAndUnique(t *testing.T) { |
| 9 | seen := map[string]bool{} |
| 10 | for i := 0; i < 1000; i++ { |
| 11 | ref, err := NewPersonRef() |
| 12 | if err != nil { |
| 13 | t.Fatalf("NewPersonRef: %v", err) |
| 14 | } |
| 15 | if seen[ref] { |
| 16 | t.Fatalf("duplicate person ref %q after %d draws", ref, i) |
| 17 | } |
| 18 | seen[ref] = true |
| 19 | if !ValidPersonRef(ref) { |
| 20 | t.Fatalf("NewPersonRef produced %q, which ValidPersonRef rejects", ref) |
| 21 | } |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | func TestPersonRefDoesNotEmbedInput(t *testing.T) { |
| 26 | ref, err := NewPersonRef() |
| 27 | if err != nil { |
| 28 | t.Fatalf("NewPersonRef: %v", err) |
| 29 | } |
| 30 | if strings.Contains(ref, "@") { |
| 31 | t.Fatalf("person ref %q contains an @", ref) |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | // NewPersonRef takes no argument, and that is the point rather than an |
| 36 | // oversight: a ref derived from the address would put the address back |
| 37 | // at rest in a reversible form under another name. This test is the |
| 38 | // reminder, since a future signature change is exactly how that would |
| 39 | // arrive. |
| 40 | func TestPersonRefTakesNoInput(t *testing.T) { |
| 41 | var f func() (string, error) = NewPersonRef |
| 42 | if f == nil { |
| 43 | t.Fatal("NewPersonRef does not have the signature func() (string, error)") |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | func TestValidPersonRefRejectsRubbish(t *testing.T) { |
| 48 | for _, bad := range []string{ |
| 49 | "", |
| 50 | "person@example.invalid", |
| 51 | "../etc", |
| 52 | strings.Repeat("a", 500), |
| 53 | "pr_", // prefix only |
| 54 | "pr_" + strings.Repeat("a", 25), // one short |
| 55 | "pr_" + strings.Repeat("a", 27), // one long |
| 56 | strings.Repeat("a", 26), // no prefix |
| 57 | "PR_" + strings.Repeat("a", 26), // wrong-case prefix |
| 58 | "pr_" + strings.Repeat("A", 26), // wrong-case body |
| 59 | "pr_" + strings.Repeat("1", 26), // 1 is not in base32's alphabet |
| 60 | "pr_" + strings.Repeat("a", 25) + "=", // padding |
| 61 | "pr_" + strings.Repeat("a", 25) + "/", // path separator |
| 62 | "pr_" + strings.Repeat("a", 25) + "\n", // a log-line break |
| 63 | } { |
| 64 | if ValidPersonRef(bad) { |
| 65 | t.Errorf("ValidPersonRef(%q) = true", bad) |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | // The ref appears in log lines by design — §3.4: "Errors carry person |
| 71 | // refs and codes" — so it must be safe there and in a URL path. |
| 72 | func TestPersonRefIsSafeInLogsAndPaths(t *testing.T) { |
| 73 | ref, err := NewPersonRef() |
| 74 | if err != nil { |
| 75 | t.Fatal(err) |
| 76 | } |
| 77 | if strings.ContainsAny(ref, " \t\r\n\"'/\\?#&=%") { |
| 78 | t.Fatalf("person ref %q contains a character that needs escaping", ref) |
| 79 | } |
| 80 | } |
| 81 | |