pkg/models/scope_v1_test.go
Rama main · réplica de lectura del repositorio original.
package models
import (
"strings"
"testing"
)
// A test's ID is [project::](file|key)::name, and a name holding "::" is
// escaped so the ID's last "::" always ends its path.
func TestTestID(t *testing.T) {
for _, tc := range []struct {
project, file, key, name, want string
}{
{"", "", "orders", "TestA", "orders::TestA"},
{"svc", "orders/x_test.go", "orders", "TestA", "svc::orders/x_test.go::TestA"},
{"", "", "", "TestA", "TestA"},
{"", "", "net", "TestDial/[::1]:80", "net::TestDial/[%3A%3A1]:80"},
{"", "", "net", "TestDial/50%/[::1]", "net::TestDial/50%25/[%3A%3A1]"},
{"", "", "net", "TestDial/50%", "net::TestDial/50%"},
// A name that already reads like an escape is escaped, so it never
// shares an ID with the name it would decode to.
{"", "", "k", "TestX/a%3A%3Ab", "k::TestX/a%253A%253Ab"},
{"", "", "k", "TestX/a::b", "k::TestX/a%3A%3Ab"},
{"", "", "k", "TestX/50%25", "k::TestX/50%2525"},
// A leading ":" would move the split to before it.
{"", "", "k", ":x", "k::%3Ax"},
{"", "", "k", ":::x", "k::%3A%3A:x"},
{"", "tests/x.py", "k", "TestCls::test_x", "tests/x.py::TestCls%3A%3Atest_x"},
} {
if got := TestID(tc.project, tc.file, tc.key, tc.name); got != tc.want {
t.Errorf("TestID(%q, %q, %q, %q) = %q, want %q", tc.project, tc.file, tc.key, tc.name, got, tc.want)
}
path := tc.file
if path == "" {
path = tc.key
}
if got := TestIDPath(tc.want); got != path {
t.Errorf("TestIDPath(%q) = %q, want %q", tc.want, got, path)
}
}
}
// Every name up to five characters from the characters escaping touches gets
// an ID of its own, and the ID's last "::" ends its path, for keys IDPart
// allows: what enterprise reads back (umproven.IsUMID) is what the agent
// built.
func TestTestIDsAreDistinctAndSplitAtThePath(t *testing.T) {
const alphabet = ":%3A25x"
names := []string{""}
all := []string{}
for n := 1; n <= 5; n++ {
var next []string
for _, p := range names {
for _, c := range alphabet {
next = append(next, p+string(c))
}
}
names = next
all = append(all, next...)
}
for _, key := range []string{"k", "k:x", "a/b"} {
if !IDPart(key) {
t.Fatalf("IDPart(%q) = false", key)
}
seen := make(map[string]string, len(all))
for _, name := range all {
id := TestID("", "", key, name)
if prev, ok := seen[id]; ok {
t.Fatalf("key %q: names %q and %q share the ID %q", key, prev, name, id)
}
seen[id] = name
i := strings.LastIndex(id, "::")
if i < 0 || id[:i] != key || id[i+2:] != escapeTestName(name) {
t.Fatalf("key %q, name %q: ID %q splits at %d", key, name, id, i)
}
if p := TestIDPath(id); p != key {
t.Fatalf("key %q, name %q: TestIDPath(%q) = %q", key, name, id, p)
}
if pid := TestID("proj", "", key, name); TestIDPath(pid) != key {
t.Fatalf("key %q, name %q: TestIDPath(%q) = %q", key, name, pid, TestIDPath(pid))
}
}
}
for _, bad := range []string{"a::b", ":a", "a:", "::"} {
if IDPart(bad) {
t.Errorf("IDPart(%q) = true", bad)
}
}
}
// A test charged a call from no worker by the agent (a worker's REAL use)
// and again by the record (a mock) says it in one sentence with the summed
// count and both chargers' What, never in a second "Also" sentence; the
// sentence for losses charged before (a Unix socket call) is written again
// in its place with them, and a reason with no such sentence gets it after
// what it says.
func TestChargeNotJudgedWritesOneSentenceWithTheSummedCount(t *testing.T) {
unattributed := func(n uint64, what string) CaptureLosses {
return CaptureLosses{{Kind: CaptureLossUnattributed, Count: n, What: what}}
}
real, mock := "the latest: a connection to mock.real destination db:5432, from process 7", "the latest: mock m3, from process 9"
ev := ScopeEvidence{}
ev.ChargeNotJudged(unattributed(2, real))
ev.ChargeNotJudged(unattributed(3, mock))
want := unattributed(5, real+"; "+mock)
if len(ev.CannotCapture) != 1 || ev.CannotCapture[0] != want[0] {
t.Fatalf("CannotCapture = %+v, want %+v", ev.CannotCapture, want)
}
if wantReason := "keploy: " + want.NotJudgedText() + "."; ev.Reason != wantReason {
t.Fatalf("Reason = %q, want %q", ev.Reason, wantReason)
}
unix := CaptureLosses{{Kind: CaptureLossUnixSocket, Count: 1}}
ev = ScopeEvidence{CannotCapture: unix, Reason: "keploy: a value it gave is not recorded. Also " + unix.NotJudgedText() + "."}
ev.ChargeNotJudged(unattributed(1, mock))
both := unix.Plus(unattributed(1, mock))
if wantReason := "keploy: a value it gave is not recorded. Also " + both.NotJudgedText() + "."; ev.Reason != wantReason {
t.Fatalf("Reason = %q, want %q", ev.Reason, wantReason)
}
if strings.Count(ev.Reason, "not judged:") != 1 {
t.Fatalf("Reason %q says not judged more than once", ev.Reason)
}
ev = ScopeEvidence{Reason: "keploy: a miss."}
ev.ChargeNotJudged(unattributed(1, mock))
if wantReason := "keploy: a miss. Also " + unattributed(1, mock).NotJudgedText() + "."; ev.Reason != wantReason {
t.Fatalf("Reason = %q, want %q", ev.Reason, wantReason)
}
for _, c := range []struct{ before, charged, want string }{
{mock, mock, mock}, // the same call named once
{"", mock, mock}, // no What before: the charged one alone
{real, "", real}, // none charged: the one before stands
{real + "; " + mock, real, real + "; " + mock}, // each named once
} {
ev = ScopeEvidence{}
ev.ChargeNotJudged(append(unattributed(1, c.before), CaptureLosses{{Kind: CaptureLossUnixSocket, Count: 1, What: "s"}}...))
ev.ChargeNotJudged(unattributed(1, c.charged))
if got := ev.CannotCapture[0].What; got != c.want {
t.Fatalf("What after %q then %q = %q, want %q", c.before, c.charged, got, c.want)
}
if got := ev.CannotCapture[1].What; got != "s" {
t.Fatalf("another kind's What = %q, want it untouched", got)
}
}
}