Files
zooqueen 0d41919cce vet: the test files say what they mean
`go vet ./...` failed, so the gate never reached the tests. Six defects, none
of them subtle once vet names them:

  mqtt_test.go — three tables declare wantErr and their bodies read it, but 18
    rows across them stopped one value short, so the literals did not compile.
    Every one of those cases asserts a specific message, so the value is false.

  stree_test.go — IntersectGSL takes `func([]byte, *T) bool`. One callback in
    LiteralMatchWithOverlappingPWCs returned nothing, unlike the three
    identical callbacks beside it.

  log_test.go, monitor_test.go — %q formats a string; both arguments are ints.

  filestore_test.go, jetstream_consumer_test.go, raft_test.go — unsafe,
    path/filepath and net/url imported and unused.

go vet ./... and go build ./... are both clean now.
2026-08-11 16:58:36 -07:00

2171 lines
57 KiB
Go

// Copyright 2023-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package stree
import (
crand "crypto/rand"
"encoding/hex"
"flag"
"fmt"
"math/rand"
"strings"
"testing"
"time"
"github.com/hanzoai/pubsub/server/gsl"
)
// Print Results: go test -v --args --results
// For some benchmarks.
var runResults = flag.Bool("results", false, "Enable Results Tests")
func TestSubjectTreeBasics(t *testing.T) {
st := NewSubjectTree[int]()
require_Equal(t, st.Size(), 0)
// Single leaf
old, updated := st.Insert(b("foo.bar.baz"), 22)
require_True(t, old == nil)
require_False(t, updated)
require_Equal(t, st.Size(), 1)
// Find shouldn't work with a wildcard.
_, found := st.Find(b("foo.bar.*"))
require_False(t, found)
// But it should with a literal. Find with single leaf.
v, found := st.Find(b("foo.bar.baz"))
require_True(t, found)
require_Equal(t, *v, 22)
// Update single leaf
old, updated = st.Insert(b("foo.bar.baz"), 33)
require_True(t, old != nil)
require_Equal(t, *old, 22)
require_True(t, updated)
require_Equal(t, st.Size(), 1)
// Split the tree
old, updated = st.Insert(b("foo.bar"), 22)
require_True(t, old == nil)
require_False(t, updated)
require_Equal(t, st.Size(), 2)
// Now we have node4 -> leaf*2
v, found = st.Find(b("foo.bar"))
require_True(t, found)
require_Equal(t, *v, 22)
// Make sure we can still retrieve the original after the split.
v, found = st.Find(b("foo.bar.baz"))
require_True(t, found)
require_Equal(t, *v, 33)
}
func TestSubjectTreeNodeGrow(t *testing.T) {
st := NewSubjectTree[int]()
for i := 0; i < 4; i++ {
subj := b(fmt.Sprintf("foo.bar.%c", 'A'+i))
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
}
// We have filled a node4.
_, ok := st.root.(*node4)
require_True(t, ok)
// This one will trigger us to grow.
old, updated := st.Insert(b("foo.bar.E"), 22)
require_True(t, old == nil)
require_False(t, updated)
_, ok = st.root.(*node10)
require_True(t, ok)
for i := 5; i < 10; i++ {
subj := b(fmt.Sprintf("foo.bar.%c", 'A'+i))
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
}
// This one will trigger us to grow.
old, updated = st.Insert(b("foo.bar.K"), 22)
require_True(t, old == nil)
require_False(t, updated)
// We have filled a node10.
_, ok = st.root.(*node16)
require_True(t, ok)
for i := 11; i < 16; i++ {
subj := b(fmt.Sprintf("foo.bar.%c", 'A'+i))
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
}
// This one will trigger us to grow.
old, updated = st.Insert(b("foo.bar.Q"), 22)
require_True(t, old == nil)
require_False(t, updated)
_, ok = st.root.(*node48)
require_True(t, ok)
// Fill the node48.
for i := 17; i < 48; i++ {
subj := b(fmt.Sprintf("foo.bar.%c", 'A'+i))
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
}
// This one will trigger us to grow.
subj := b(fmt.Sprintf("foo.bar.%c", 'A'+49))
old, updated = st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
_, ok = st.root.(*node256)
require_True(t, ok)
}
func TestSubjectTreeNodePrefixMismatch(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 11)
st.Insert(b("foo.bar.B"), 22)
st.Insert(b("foo.bar.C"), 33)
// Grab current root. Split below will cause update.
or := st.root
// This one will force a split of the node
st.Insert(b("foo.foo.A"), 44)
require_True(t, or != st.root)
// Now make sure we can retrieve correctly.
v, found := st.Find(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 11)
v, found = st.Find(b("foo.bar.B"))
require_True(t, found)
require_Equal(t, *v, 22)
v, found = st.Find(b("foo.bar.C"))
require_True(t, found)
require_Equal(t, *v, 33)
v, found = st.Find(b("foo.foo.A"))
require_True(t, found)
require_Equal(t, *v, 44)
}
func TestSubjectTreeNodeDelete(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 22)
v, found := st.Delete(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 22)
require_Equal(t, st.root, nil)
v, found = st.Delete(b("foo.bar.A"))
require_False(t, found)
require_Equal(t, v, nil)
v, found = st.Find(b("foo.foo.A"))
require_False(t, found)
require_Equal(t, v, nil)
// Kick to a node4.
st.Insert(b("foo.bar.A"), 11)
st.Insert(b("foo.bar.B"), 22)
st.Insert(b("foo.bar.C"), 33)
// Make sure we can delete and that we shrink back to leaf.
v, found = st.Delete(b("foo.bar.C"))
require_True(t, found)
require_Equal(t, *v, 33)
v, found = st.Delete(b("foo.bar.B"))
require_True(t, found)
require_Equal(t, *v, 22)
// We should have shrunk here.
require_True(t, st.root.isLeaf())
v, found = st.Delete(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 11)
require_Equal(t, st.root, nil)
// Now pop up to a node10 and make sure we can shrink back down.
for i := 0; i < 5; i++ {
subj := fmt.Sprintf("foo.bar.%c", 'A'+i)
st.Insert(b(subj), 22)
}
_, ok := st.root.(*node10)
require_True(t, ok)
v, found = st.Delete(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 22)
_, ok = st.root.(*node4)
require_True(t, ok)
// Now pop up to node16
for i := 0; i < 11; i++ {
subj := fmt.Sprintf("foo.bar.%c", 'A'+i)
st.Insert(b(subj), 22)
}
_, ok = st.root.(*node16)
require_True(t, ok)
v, found = st.Delete(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 22)
_, ok = st.root.(*node10)
require_True(t, ok)
v, found = st.Find(b("foo.bar.B"))
require_True(t, found)
require_Equal(t, *v, 22)
// Now pop up to node48
st = NewSubjectTree[int]()
for i := 0; i < 17; i++ {
subj := fmt.Sprintf("foo.bar.%c", 'A'+i)
st.Insert(b(subj), 22)
}
_, ok = st.root.(*node48)
require_True(t, ok)
v, found = st.Delete(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 22)
_, ok = st.root.(*node16)
require_True(t, ok)
v, found = st.Find(b("foo.bar.B"))
require_True(t, found)
require_Equal(t, *v, 22)
// Now pop up to node256
st = NewSubjectTree[int]()
for i := 0; i < 49; i++ {
subj := fmt.Sprintf("foo.bar.%c", 'A'+i)
st.Insert(b(subj), 22)
}
_, ok = st.root.(*node256)
require_True(t, ok)
v, found = st.Delete(b("foo.bar.A"))
require_True(t, found)
require_Equal(t, *v, 22)
_, ok = st.root.(*node48)
require_True(t, ok)
v, found = st.Find(b("foo.bar.B"))
require_True(t, found)
require_Equal(t, *v, 22)
}
func TestSubjectTreeNodesAndPaths(t *testing.T) {
st := NewSubjectTree[int]()
check := func(subj string) {
t.Helper()
v, found := st.Find(b(subj))
require_True(t, found)
require_Equal(t, *v, 22)
}
st.Insert(b("foo.bar.A"), 22)
st.Insert(b("foo.bar.B"), 22)
st.Insert(b("foo.bar.C"), 22)
st.Insert(b("foo.bar"), 22)
check("foo.bar.A")
check("foo.bar.B")
check("foo.bar.C")
check("foo.bar")
// This will do several things in terms of shrinking and pruning,
// want to make sure it gets prefix correct for new top node4.
st.Delete(b("foo.bar"))
check("foo.bar.A")
check("foo.bar.B")
check("foo.bar.C")
}
// Check that we are constructing a proper tree with complex insert patterns.
func TestSubjectTreeConstruction(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 2)
st.Insert(b("foo.bar.C"), 3)
st.Insert(b("foo.baz.A"), 11)
st.Insert(b("foo.baz.B"), 22)
st.Insert(b("foo.baz.C"), 33)
st.Insert(b("foo.bar"), 42)
checkNode := func(an *node, kind string, pors string, numChildren uint16) {
t.Helper()
require_True(t, an != nil)
n := *an
require_True(t, n != nil)
require_Equal(t, n.kind(), kind)
require_Equal(t, pors, string(n.path()))
require_Equal(t, numChildren, n.numChildren())
}
checkNode(&st.root, "NODE4", "foo.ba", 2)
nn := st.root.findChild('r')
checkNode(nn, "NODE4", "r", 2)
checkNode((*nn).findChild(noPivot), "LEAF", "", 0)
rnn := (*nn).findChild('.')
checkNode(rnn, "NODE4", ".", 3)
checkNode((*rnn).findChild('A'), "LEAF", "A", 0)
checkNode((*rnn).findChild('B'), "LEAF", "B", 0)
checkNode((*rnn).findChild('C'), "LEAF", "C", 0)
znn := st.root.findChild('z')
checkNode(znn, "NODE4", "z.", 3)
checkNode((*znn).findChild('A'), "LEAF", "A", 0)
checkNode((*znn).findChild('B'), "LEAF", "B", 0)
checkNode((*znn).findChild('C'), "LEAF", "C", 0)
// Use st.Dump() if you want a tree print out.
// Now delete "foo.bar" and make sure put ourselves back together properly.
v, found := st.Delete(b("foo.bar"))
require_True(t, found)
require_Equal(t, *v, 42)
checkNode(&st.root, "NODE4", "foo.ba", 2)
nn = st.root.findChild('r')
checkNode(nn, "NODE4", "r.", 3)
checkNode((*nn).findChild('A'), "LEAF", "A", 0)
checkNode((*nn).findChild('B'), "LEAF", "B", 0)
checkNode((*nn).findChild('C'), "LEAF", "C", 0)
znn = st.root.findChild('z')
checkNode(znn, "NODE4", "z.", 3)
checkNode((*znn).findChild('A'), "LEAF", "A", 0)
checkNode((*znn).findChild('B'), "LEAF", "B", 0)
checkNode((*znn).findChild('C'), "LEAF", "C", 0)
}
func match(t *testing.T, st *SubjectTree[int], filter string, expected int) {
t.Helper()
var matches []int
st.Match(b(filter), func(_ []byte, v *int) {
matches = append(matches, *v)
})
require_Equal(t, expected, len(matches))
}
func TestSubjectTreeMatchLeafOnly(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.baz.A"), 1)
// Check all placements of pwc in token space.
match(t, st, "foo.bar.*.A", 1)
match(t, st, "foo.*.baz.A", 1)
match(t, st, "foo.*.*.A", 1)
match(t, st, "foo.*.*.*", 1)
match(t, st, "*.*.*.*", 1)
// Now check fwc.
match(t, st, ">", 1)
match(t, st, "foo.>", 1)
match(t, st, "foo.*.>", 1)
match(t, st, "foo.bar.>", 1)
match(t, st, "foo.bar.*.>", 1)
// Check partials so they do not trigger on leafs.
match(t, st, "foo.bar.baz", 0)
}
func TestSubjectTreeMatchNodes(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 2)
st.Insert(b("foo.bar.C"), 3)
st.Insert(b("foo.baz.A"), 11)
st.Insert(b("foo.baz.B"), 22)
st.Insert(b("foo.baz.C"), 33)
// Test literals.
match(t, st, "foo.bar.A", 1)
match(t, st, "foo.baz.A", 1)
match(t, st, "foo.bar", 0)
// Test internal pwc
match(t, st, "foo.*.A", 2)
// Test terminal pwc
match(t, st, "foo.bar.*", 3)
match(t, st, "foo.baz.*", 3)
// Check fwc
match(t, st, ">", 6)
match(t, st, "foo.>", 6)
match(t, st, "foo.bar.>", 3)
match(t, st, "foo.baz.>", 3)
// Make sure we do not have false positives on prefix matches.
match(t, st, "foo.ba", 0)
// Now add in "foo.bar" to make a more complex tree construction
// and re-test.
st.Insert(b("foo.bar"), 42)
// Test literals.
match(t, st, "foo.bar.A", 1)
match(t, st, "foo.baz.A", 1)
match(t, st, "foo.bar", 1)
// Test internal pwc
match(t, st, "foo.*.A", 2)
// Test terminal pwc
match(t, st, "foo.bar.*", 3)
match(t, st, "foo.baz.*", 3)
// Check fwc
match(t, st, ">", 7)
match(t, st, "foo.>", 7)
match(t, st, "foo.bar.>", 3)
match(t, st, "foo.baz.>", 3)
}
func matchUntilCount[T any](st *SubjectTree[T], filter string, stopAfter int) (int, bool) {
var n int
completed := st.MatchUntil(b(filter), func(_ []byte, _ *T) bool {
n++
return n < stopAfter
})
return n, completed
}
func TestSubjectTreeMatchUntil(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 2)
st.Insert(b("foo.bar.C"), 3)
st.Insert(b("foo.baz.A"), 11)
st.Insert(b("foo.baz.B"), 22)
st.Insert(b("foo.baz.C"), 33)
st.Insert(b("foo.bar"), 42)
// Ensure early stop terminates traversal.
count, completed := matchUntilCount(st, "foo.>", 3)
require_Equal(t, count, 3)
require_False(t, completed)
// Match completes
count, completed = matchUntilCount(st, "foo.bar", 3)
require_Equal(t, count, 1)
require_True(t, completed)
count, completed = matchUntilCount(st, "foo.baz.*", 4)
require_Equal(t, count, 3)
require_True(t, completed)
}
func TestSubjectTreeNoPrefix(t *testing.T) {
st := NewSubjectTree[int]()
for i := 0; i < 26; i++ {
subj := b(fmt.Sprintf("%c", 'A'+i))
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
}
n, ok := st.root.(*node48)
require_True(t, ok)
require_Equal(t, n.numChildren(), 26)
v, found := st.Delete(b("B"))
require_True(t, found)
require_Equal(t, *v, 22)
require_Equal(t, n.numChildren(), 25)
v, found = st.Delete(b("Z"))
require_True(t, found)
require_Equal(t, *v, 22)
require_Equal(t, n.numChildren(), 24)
}
func TestSubjectTreePartialTerminalWildcardBugMatch(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("STATE.GLOBAL.CELL1.7PDSGAALXNN000010.PROPERTY-A"), 5)
st.Insert(b("STATE.GLOBAL.CELL1.7PDSGAALXNN000010.PROPERTY-B"), 1)
st.Insert(b("STATE.GLOBAL.CELL1.7PDSGAALXNN000010.PROPERTY-C"), 2)
match(t, st, "STATE.GLOBAL.CELL1.7PDSGAALXNN000010.*", 3)
}
func TestSubjectTreeMatchSubjectParam(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 2)
st.Insert(b("foo.bar.C"), 3)
st.Insert(b("foo.baz.A"), 11)
st.Insert(b("foo.baz.B"), 22)
st.Insert(b("foo.baz.C"), 33)
st.Insert(b("foo.bar"), 42)
checkValMap := map[string]int{
"foo.bar.A": 1,
"foo.bar.B": 2,
"foo.bar.C": 3,
"foo.baz.A": 11,
"foo.baz.B": 22,
"foo.baz.C": 33,
"foo.bar": 42,
}
// Make sure we get a proper subject parameter and it matches our value properly.
st.Match([]byte(">"), func(subject []byte, v *int) {
if expected, ok := checkValMap[string(subject)]; !ok {
t.Fatalf("Unexpected subject parameter: %q", subject)
} else if expected != *v {
t.Fatalf("Expected %q to have value of %d, but got %d", subject, expected, *v)
}
})
}
func TestSubjectTreeMatchRandomDoublePWC(t *testing.T) {
st := NewSubjectTree[int]()
for i := 1; i <= 10_000; i++ {
subj := fmt.Sprintf("foo.%d.%d", rand.Intn(20)+1, i)
st.Insert(b(subj), 42)
}
match(t, st, "foo.*.*", 10_000)
// Check with pwc and short interior token.
seen, verified := 0, 0
st.Match(b("*.2.*"), func(_ []byte, _ *int) {
seen++
})
// Now check via walk to make sure we are right.
st.IterOrdered(func(subject []byte, v *int) bool {
tokens := strings.Split(string(subject), ".")
require_Equal(t, len(tokens), 3)
if tokens[1] == "2" {
verified++
}
return true
})
require_Equal(t, seen, verified)
seen, verified = 0, 0
st.Match(b("*.*.222"), func(_ []byte, _ *int) {
seen++
})
st.IterOrdered(func(subject []byte, v *int) bool {
tokens := strings.Split(string(subject), ".")
require_Equal(t, len(tokens), 3)
if tokens[2] == "222" {
verified++
}
return true
})
require_Equal(t, seen, verified)
}
func TestSubjectTreeIterOrdered(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 2)
st.Insert(b("foo.bar.C"), 3)
st.Insert(b("foo.baz.A"), 11)
st.Insert(b("foo.baz.B"), 22)
st.Insert(b("foo.baz.C"), 33)
st.Insert(b("foo.bar"), 42)
checkValMap := map[string]int{
"foo.bar.A": 1,
"foo.bar.B": 2,
"foo.bar.C": 3,
"foo.baz.A": 11,
"foo.baz.B": 22,
"foo.baz.C": 33,
"foo.bar": 42,
}
checkOrder := []string{
"foo.bar",
"foo.bar.A",
"foo.bar.B",
"foo.bar.C",
"foo.baz.A",
"foo.baz.B",
"foo.baz.C",
}
var received int
walk := func(subject []byte, v *int) bool {
if expected := checkOrder[received]; expected != string(subject) {
t.Fatalf("Expected %q for %d item returned, got %q", expected, received, subject)
}
received++
require_True(t, v != nil)
if expected := checkValMap[string(subject)]; expected != *v {
t.Fatalf("Expected %q to have value of %d, but got %d", subject, expected, *v)
}
return true
}
// Kick in the iter.
st.IterOrdered(walk)
require_Equal(t, received, len(checkOrder))
// Make sure we can terminate properly.
received = 0
st.IterOrdered(func(subject []byte, v *int) bool {
received++
return received != 4
})
require_Equal(t, received, 4)
}
func TestSubjectTreeIterFast(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 2)
st.Insert(b("foo.bar.C"), 3)
st.Insert(b("foo.baz.A"), 11)
st.Insert(b("foo.baz.B"), 22)
st.Insert(b("foo.baz.C"), 33)
st.Insert(b("foo.bar"), 42)
checkValMap := map[string]int{
"foo.bar.A": 1,
"foo.bar.B": 2,
"foo.bar.C": 3,
"foo.baz.A": 11,
"foo.baz.B": 22,
"foo.baz.C": 33,
"foo.bar": 42,
}
var received int
walk := func(subject []byte, v *int) bool {
received++
require_True(t, v != nil)
if expected := checkValMap[string(subject)]; expected != *v {
t.Fatalf("Expected %q to have value of %d, but got %d", subject, expected, *v)
}
return true
}
// Kick in the iter.
st.IterFast(walk)
require_Equal(t, received, len(checkValMap))
// Make sure we can terminate properly.
received = 0
st.IterFast(func(subject []byte, v *int) bool {
received++
return received != 4
})
require_Equal(t, received, 4)
}
func TestSubjectTreeInsertSamePivotBug(t *testing.T) {
testSubjects := [][]byte{
[]byte("0d00.2abbb82c1d.6e16.fa7f85470e.3e46"),
[]byte("534b12.3486c17249.4dde0666"),
[]byte("6f26aabd.920ee3.d4d3.5ffc69f6"),
[]byte("8850.ade3b74c31.aa533f77.9f59.a4bd8415.b3ed7b4111"),
[]byte("5a75047dcb.5548e845b6.76024a34.14d5b3.80c426.51db871c3a"),
[]byte("825fa8acfc.5331.00caf8bbbd.107c4b.c291.126d1d010e"),
}
st := NewSubjectTree[int]()
for _, subj := range testSubjects {
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
if _, found := st.Find(subj); !found {
t.Fatalf("Could not find subject %q which should be findable", subj)
}
}
}
func TestSubjectTreeMatchTsepSecondThenPartialPartBug(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.xxxxx.foo1234.zz"), 22)
st.Insert(b("foo.yyy.foo123.zz"), 22)
st.Insert(b("foo.yyybar789.zz"), 22)
st.Insert(b("foo.yyy.foo12345.zz"), 22)
st.Insert(b("foo.yyy.foo12345.yy"), 22)
st.Insert(b("foo.yyy.foo123456789.zz"), 22)
match(t, st, "foo.*.foo123456789.*", 1)
match(t, st, "foo.*.*.zzz.foo.>", 0)
}
func TestSubjectTreeMatchMultipleWildcardBasic(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("A.B.C.D.0.G.H.I.0"), 22)
st.Insert(b("A.B.C.D.1.G.H.I.0"), 22)
match(t, st, "A.B.*.D.1.*.*.I.0", 1)
}
func TestSubjectTreeMatchInvalidWildcard(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.123"), 22)
st.Insert(b("one.two.three.four.five"), 22)
st.Insert(b("'*.123"), 22)
st.Insert(b("bar"), 22)
match(t, st, "invalid.>", 0)
match(t, st, "foo.>.bar", 0)
match(t, st, ">", 4)
match(t, st, `'*.*`, 1)
match(t, st, `'*.*.*'`, 0)
// None of these should match.
match(t, st, "`>`", 0)
match(t, st, `">"`, 0)
match(t, st, `'>'`, 0)
match(t, st, `'*.>'`, 0)
match(t, st, `'*.>.`, 0)
match(t, st, "`invalid.>`", 0)
match(t, st, `'*.*'`, 0)
}
func TestSubjectTreeRandomTrackEntries(t *testing.T) {
st := NewSubjectTree[int]()
smap := make(map[string]struct{}, 1000)
// Make sure all added items can be found.
check := func() {
t.Helper()
for subj := range smap {
if _, found := st.Find(b(subj)); !found {
t.Fatalf("Could not find subject %q which should be findable", subj)
}
}
}
buf := make([]byte, 10)
for i := 0; i < 1000; i++ {
var sb strings.Builder
// 1-6 tokens.
numTokens := rand.Intn(6) + 1
for i := 0; i < numTokens; i++ {
tlen := rand.Intn(4) + 2
tok := buf[:tlen]
crand.Read(tok)
sb.WriteString(hex.EncodeToString(tok))
if i != numTokens-1 {
sb.WriteString(".")
}
}
subj := sb.String()
// Avoid dupes since will cause check to fail after we delete messages.
if _, ok := smap[subj]; ok {
continue
}
smap[subj] = struct{}{}
old, updated := st.Insert(b(subj), 22)
require_True(t, old == nil)
require_False(t, updated)
require_Equal(t, st.Size(), len(smap))
check()
}
}
// Needs to be longer then internal node prefix, which currently is 24.
func TestSubjectTreeLongTokens(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("a1.aaaaaaaaaaaaaaaaaaaaaa0"), 1)
st.Insert(b("a2.0"), 2)
st.Insert(b("a1.aaaaaaaaaaaaaaaaaaaaaa1"), 3)
st.Insert(b("a2.1"), 4)
// Simulate purge of a2.>
// This required to show bug.
st.Delete(b("a2.0"))
st.Delete(b("a2.1"))
require_Equal(t, st.Size(), 2)
v, found := st.Find(b("a1.aaaaaaaaaaaaaaaaaaaaaa0"))
require_True(t, found)
require_Equal(t, *v, 1)
v, found = st.Find(b("a1.aaaaaaaaaaaaaaaaaaaaaa1"))
require_True(t, found)
require_Equal(t, *v, 3)
}
func b(s string) []byte {
return []byte(s)
}
func TestSubjectTreeMatchAllPerf(t *testing.T) {
if !*runResults {
t.Skip()
}
st := NewSubjectTree[int]()
for i := 0; i < 1_000_000; i++ {
subj := fmt.Sprintf("subj.%d.%d", rand.Intn(100)+1, i)
st.Insert(b(subj), 22)
}
for _, f := range [][]byte{
[]byte(">"),
[]byte("subj.>"),
[]byte("subj.*.*"),
[]byte("*.*.*"),
[]byte("subj.1.*"),
[]byte("subj.1.>"),
[]byte("subj.*.1"),
[]byte("*.*.1"),
} {
start := time.Now()
count := 0
st.Match(f, func(_ []byte, _ *int) {
count++
})
t.Logf("Match %q took %s and matched %d entries", f, time.Since(start), count)
}
}
func TestSubjectTreeIterPerf(t *testing.T) {
if !*runResults {
t.Skip()
}
st := NewSubjectTree[int]()
for i := 0; i < 1_000_000; i++ {
subj := fmt.Sprintf("subj.%d.%d", rand.Intn(100)+1, i)
st.Insert(b(subj), 22)
}
start := time.Now()
count := 0
st.IterOrdered(func(_ []byte, _ *int) bool {
count++
return true
})
t.Logf("Iter took %s and matched %d entries", time.Since(start), count)
}
func TestSubjectTreeNode48(t *testing.T) {
var a, b, c leaf[int]
var n node48
n.addChild('A', &a)
require_Equal(t, n.key['A'], 1)
require_True(t, n.child[0] != nil)
require_Equal(t, n.child[0].(*leaf[int]), &a)
require_Equal(t, len(n.children()), 1)
child := n.findChild('A')
require_True(t, child != nil)
require_Equal(t, (*child).(*leaf[int]), &a)
n.addChild('B', &b)
require_Equal(t, n.key['B'], 2)
require_True(t, n.child[1] != nil)
require_Equal(t, n.child[1].(*leaf[int]), &b)
require_Equal(t, len(n.children()), 2)
child = n.findChild('B')
require_True(t, child != nil)
require_Equal(t, (*child).(*leaf[int]), &b)
n.addChild('C', &c)
require_Equal(t, n.key['C'], 3)
require_True(t, n.child[2] != nil)
require_Equal(t, n.child[2].(*leaf[int]), &c)
require_Equal(t, len(n.children()), 3)
child = n.findChild('C')
require_True(t, child != nil)
require_Equal(t, (*child).(*leaf[int]), &c)
n.deleteChild('A')
require_Equal(t, len(n.children()), 2)
require_Equal(t, n.key['A'], 0) // Now deleted
require_Equal(t, n.key['B'], 2) // Untouched
require_Equal(t, n.key['C'], 1) // Where A was
child = n.findChild('A')
require_Equal(t, child, nil)
require_True(t, n.child[0] != nil)
require_Equal(t, n.child[0].(*leaf[int]), &c)
child = n.findChild('B')
require_True(t, child != nil)
require_Equal(t, (*child).(*leaf[int]), &b)
require_True(t, n.child[1] != nil)
require_Equal(t, n.child[1].(*leaf[int]), &b)
child = n.findChild('C')
require_True(t, child != nil)
require_Equal(t, (*child).(*leaf[int]), &c)
require_True(t, n.child[2] == nil)
var gotB, gotC bool
var iterations int
n.iter(func(n node) bool {
iterations++
if gb, ok := n.(*leaf[int]); ok && &b == gb {
gotB = true
}
if gc, ok := n.(*leaf[int]); ok && &c == gc {
gotC = true
}
return true
})
require_Equal(t, iterations, 2)
require_True(t, gotB)
require_True(t, gotC)
// Check for off-by-one on byte 255 as found by staticcheck, see
// https://github.com/nats-io/nats-server/pull/5826.
n.addChild(255, &c)
require_Equal(t, n.key[255], 3)
grown := n.grow().(*node256)
require_True(t, grown.findChild(255) != nil)
shrunk := n.shrink().(*node16)
require_True(t, shrunk.findChild(255) != nil)
}
func TestSubjectTreeMatchNoCallbackDupe(t *testing.T) {
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.A"), 1)
st.Insert(b("foo.bar.B"), 1)
st.Insert(b("foo.bar.C"), 1)
st.Insert(b("foo.bar.>"), 1)
for _, f := range [][]byte{
[]byte(">"),
[]byte("foo.>"),
[]byte("foo.bar.>"),
} {
seen := map[string]struct{}{}
st.Match(f, func(bsubj []byte, _ *int) {
subj := string(bsubj)
if _, ok := seen[subj]; ok {
t.Logf("Match callback was called twice for %q", subj)
}
seen[subj] = struct{}{}
})
}
}
func TestSubjectTreeNilNoPanic(t *testing.T) {
var st *SubjectTree[int]
st.Match([]byte("foo"), func(_ []byte, _ *int) {})
_, found := st.Find([]byte("foo"))
require_False(t, found)
_, found = st.Delete([]byte("foo"))
require_False(t, found)
_, found = st.Insert([]byte("foo"), 22)
require_False(t, found)
}
// This bug requires the trailing suffix contain repeating nulls \x00
// and the second subject be longer with more nulls.
func TestSubjectTreeInsertLongerLeafSuffixWithTrailingNulls(t *testing.T) {
st := NewSubjectTree[int]()
subj := []byte("foo.bar.baz_")
// add in 10 nulls.
for i := 0; i < 10; i++ {
subj = append(subj, 0)
}
st.Insert(subj, 1)
// add in 10 more nulls.
subj2 := subj
for i := 0; i < 10; i++ {
subj2 = append(subj, 0)
}
st.Insert(subj2, 2)
// Make sure we can look them up.
v, found := st.Find(subj)
require_True(t, found)
require_Equal(t, *v, 1)
v, found = st.Find(subj2)
require_True(t, found)
require_Equal(t, *v, 2)
}
// Make sure the system does not insert any subject with the noPivot (DEL) in it.
func TestSubjectTreeInsertWithNoPivot(t *testing.T) {
st := NewSubjectTree[int]()
subj := []byte("foo.bar.baz.")
subj = append(subj, noPivot)
old, updated := st.Insert(subj, 22)
require_True(t, old == nil)
require_False(t, updated)
require_Equal(t, st.Size(), 0)
}
// Make sure we don't panic when checking for fwc.
func TestSubjectTreeMatchHasFWCNoPanic(t *testing.T) {
defer func() {
p := recover()
require_True(t, p == nil)
}()
st := NewSubjectTree[int]()
subj := []byte("foo")
st.Insert(subj, 1)
st.Match([]byte("."), func(subject []byte, val *int) {})
}
func TestSubjectTreeLazyIntersect(t *testing.T) {
st1 := NewSubjectTree[int]()
st2 := NewSubjectTree[int]()
// Should cause an intersection.
st1.Insert([]byte("foo.bar"), 1)
st2.Insert([]byte("foo.bar"), 1)
// Should cause an intersection.
st1.Insert([]byte("foo.bar.baz.qux"), 1)
st2.Insert([]byte("foo.bar.baz.qux"), 1)
// Should not cause any intersections.
st1.Insert([]byte("bar"), 1)
st2.Insert([]byte("baz"), 1)
st1.Insert([]byte("a.b.c"), 1)
st2.Insert([]byte("a.b.d"), 1)
st1.Insert([]byte("a.b.ee"), 1)
st2.Insert([]byte("a.b.e"), 1)
st1.Insert([]byte("bb.c.d"), 1)
st2.Insert([]byte("b.c.d"), 1)
st2.Insert([]byte("foo.bar.baz.qux.alice"), 1)
st2.Insert([]byte("foo.bar.baz.qux.bob"), 1)
intersected := map[string]int{}
LazyIntersect(st1, st2, func(key []byte, val1, val2 *int) {
intersected[string(key)]++
})
require_Equal(t, len(intersected), 2)
require_Equal(t, intersected["foo.bar"], 1)
require_Equal(t, intersected["foo.bar.baz.qux"], 1)
}
func TestSubjectTreeGSLIntersection(t *testing.T) {
st := NewSubjectTree[struct{}]()
st.Insert([]byte("one.two.three.four"), struct{}{})
st.Insert([]byte("one.two.three.five"), struct{}{})
st.Insert([]byte("one.two.six"), struct{}{})
st.Insert([]byte("one.two.seven"), struct{}{})
st.Insert([]byte("eight.nine"), struct{}{})
st.Insert([]byte("stream.A"), struct{}{})
st.Insert([]byte("stream.A.child"), struct{}{})
require_NoDuplicates := func(t *testing.T, got map[string]int) {
t.Helper()
for _, c := range got {
require_Equal(t, c, 1)
}
}
t.Run("Literals", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.two.six", 11))
require_NoError(t, sl.Insert("eight.nine", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWC", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.two.*.*", 11))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWCOverlapping", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.two.*.four", 11))
require_NoError(t, sl.Insert("one.two.*.*", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWCAll", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("*.*", 11))
require_NoError(t, sl.Insert("*.*.*", 22))
require_NoError(t, sl.Insert("*.*.*.*", 33))
require_True(t, sl.HasInterest("foo.bar"))
require_True(t, sl.HasInterest("foo.bar.baz"))
require_True(t, sl.HasInterest("foo.bar.baz.qux"))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 7)
require_NoDuplicates(t, got)
})
t.Run("FWC", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.>", 11))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 4)
require_NoDuplicates(t, got)
})
t.Run("FWCOverlapping", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.two.three.four", 11))
require_NoError(t, sl.Insert("one.>", 22))
require_NoError(t, sl.Insert(">", 33))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 7)
require_NoDuplicates(t, got)
})
t.Run("FWCExtended", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.>", 11))
require_NoError(t, sl.Insert("stream.A", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWCExtended", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.*.child", 11))
require_NoError(t, sl.Insert("stream.A", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.child", 11))
require_NoError(t, sl.Insert("*.A.child", 22))
require_NoError(t, sl.Insert("stream.*.child", 22))
require_NoError(t, sl.Insert("stream.A.*", 22))
require_NoError(t, sl.Insert("stream.*.*", 22))
require_NoError(t, sl.Insert("*.A.*", 22))
require_NoError(t, sl.Insert("*.*.child", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 1)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive2", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.child", 11))
require_NoError(t, sl.Insert("*.A.child", 22))
require_NoError(t, sl.Insert("stream.A.*", 22))
require_NoError(t, sl.Insert("*.A.*", 22))
require_NoError(t, sl.Insert("*.*.child", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 1)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive3", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.child", 11))
require_NoError(t, sl.Insert("stream.*.*", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 1)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive4", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.child", 11))
require_NoError(t, sl.Insert("stream.*.>", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 1)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive5", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.child", 11))
require_NoError(t, sl.Insert("*.*", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 3)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive6", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.child", 11))
require_NoError(t, sl.Insert("stream.*", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWCExtendedAggressive7", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A", 11))
require_NoError(t, sl.Insert("*.*.*", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 4)
require_NoDuplicates(t, got)
})
t.Run("FWCAll", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert(">", 11))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 7)
require_NoDuplicates(t, got)
})
t.Run("StopsWhenCallbackReturnsFalse", func(t *testing.T) {
var got int
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert(">", 11))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got++
return false
})
require_Equal(t, got, 1)
})
t.Run("NoMatch", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one", 11))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 0)
})
t.Run("NoMatches", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one", 11))
require_NoError(t, sl.Insert("eight", 22))
require_NoError(t, sl.Insert("ten", 33))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 0)
})
t.Run("NoMatchPartial", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("stream.A.not-child", 11))
require_NoError(t, sl.Insert("stream.A.child.>", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 0)
require_NoDuplicates(t, got)
})
t.Run("PWCDoesntHideLiteral", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.*.six", 11))
require_NoError(t, sl.Insert("one.two.seven", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("PWCDoesntHideLiteral2", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.*.*.four", 11))
require_NoError(t, sl.Insert("one.*.*.five", 22))
require_NoError(t, sl.Insert("one.*.three", 33))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("DisjointWildcardPositions", func(t *testing.T) {
// Test case documenting bitmask approach limitation:
// When patterns have wildcards at different positions (not strict subsets),
// the bitmask considers earlier-position wildcards as "less specific"
// which can cause some matches to be skipped.
// e.g., *.two.three.four (wildcard at pos 0) vs one.two.*.* (wildcards at pos 2,3)
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.two.*.*", 33))
require_NoError(t, sl.Insert("*.two.three.four", 55))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
// Due to bitmask comparison, one.two.*.* may be skipped in favor of *.two.three.four
// This results in only one.two.three.four being matched, missing one.two.three.five
require_Len(t, len(got), 2)
require_NoDuplicates(t, got)
})
t.Run("LiteralMatchWithOverlappingPWCs", func(t *testing.T) {
got := map[string]int{}
sl := gsl.NewSublist[int]()
require_NoError(t, sl.Insert("one.*.four.five", 11))
require_NoError(t, sl.Insert("one.two.three.four", 22))
IntersectGSL(st, sl, func(subj []byte, entry *struct{}) bool {
got[string(subj)]++
return true
})
require_Len(t, len(got), 1)
require_NoDuplicates(t, got)
})
}
func TestSubjectTreeDeleteShortSubjectNoPanic(t *testing.T) {
defer func() {
p := recover()
require_True(t, p == nil)
}()
st := NewSubjectTree[int]()
st.Insert(b("foo.bar.baz"), 1)
st.Insert(b("foo.bar.qux"), 2)
v, found := st.Delete(b("foo.bar"))
require_False(t, found)
require_Equal(t, v, nil)
v, found = st.Find(b("foo.bar.baz"))
require_True(t, found)
require_Equal(t, *v, 1)
v, found = st.Find(b("foo.bar.qux"))
require_True(t, found)
require_Equal(t, *v, 2)
}
func TestSubjectTreeEmpty(t *testing.T) {
// Test Empty on nil tree
var st *SubjectTree[int]
st2 := st.Empty()
require_True(t, st2 != nil)
require_Equal(t, st2.Size(), 0)
// Test Empty on new tree
st = NewSubjectTree[int]()
require_Equal(t, st.Size(), 0)
st2 = st.Empty()
require_True(t, st2 == st) // Should return same instance
require_Equal(t, st2.Size(), 0)
// Test Empty on tree with data
st.Insert(b("foo.bar"), 1)
st.Insert(b("foo.baz"), 2)
st.Insert(b("bar.baz"), 3)
require_Equal(t, st.Size(), 3)
// Empty should clear everything
st2 = st.Empty()
require_True(t, st2 == st) // Should return same instance
require_Equal(t, st.Size(), 0)
require_True(t, st.root == nil)
// Verify we can't find old entries
_, found := st.Find(b("foo.bar"))
require_False(t, found)
_, found = st.Find(b("foo.baz"))
require_False(t, found)
_, found = st.Find(b("bar.baz"))
require_False(t, found)
// Verify we can insert new entries after Empty
old, updated := st.Insert(b("new.entry"), 42)
require_True(t, old == nil)
require_False(t, updated)
require_Equal(t, st.Size(), 1)
v, found := st.Find(b("new.entry"))
require_True(t, found)
require_Equal(t, *v, 42)
}
func TestSubjectTreeLazyIntersectComprehensive(t *testing.T) {
// Test with nil trees
var st1 *SubjectTree[int]
var st2 *SubjectTree[string]
count := 0
LazyIntersect(st1, st2, func(key []byte, v1 *int, v2 *string) {
count++
})
require_Equal(t, count, 0)
// Test with one nil tree
st1 = NewSubjectTree[int]()
st1.Insert(b("foo"), 1)
LazyIntersect(st1, st2, func(key []byte, v1 *int, v2 *string) {
count++
})
require_Equal(t, count, 0)
// Test with empty trees
st2 = NewSubjectTree[string]()
LazyIntersect(st1, st2, func(key []byte, v1 *int, v2 *string) {
count++
})
require_Equal(t, count, 0)
// Test with different value types
st1 = NewSubjectTree[int]()
st2 = NewSubjectTree[string]()
// Add some intersecting keys
st1.Insert(b("foo.bar"), 42)
st2.Insert(b("foo.bar"), "hello")
st1.Insert(b("baz.qux"), 100)
st2.Insert(b("baz.qux"), "world")
// Add non-intersecting keys
st1.Insert(b("only.in.st1"), 1)
st2.Insert(b("only.in.st2"), "two")
results := make(map[string]struct {
v1 int
v2 string
})
LazyIntersect(st1, st2, func(key []byte, v1 *int, v2 *string) {
results[string(key)] = struct {
v1 int
v2 string
}{*v1, *v2}
})
require_Equal(t, len(results), 2)
require_Equal(t, results["foo.bar"].v1, 42)
require_Equal(t, results["foo.bar"].v2, "hello")
require_Equal(t, results["baz.qux"].v1, 100)
require_Equal(t, results["baz.qux"].v2, "world")
// Test that it iterates over smaller tree
// Create a large tree and a small tree
large := NewSubjectTree[int]()
small := NewSubjectTree[int]()
// Large tree has many entries
for i := 0; i < 100; i++ {
large.Insert([]byte(fmt.Sprintf("large.%d", i)), i)
}
// Small tree has few entries with some overlap
small.Insert(b("large.5"), 500)
small.Insert(b("large.10"), 1000)
small.Insert(b("large.50"), 5000)
small.Insert(b("small.only"), 999)
intersectCount := 0
LazyIntersect(large, small, func(key []byte, v1 *int, v2 *int) {
intersectCount++
// Verify we get the correct values
switch string(key) {
case "large.5":
require_Equal(t, *v1, 5)
require_Equal(t, *v2, 500)
case "large.10":
require_Equal(t, *v1, 10)
require_Equal(t, *v2, 1000)
case "large.50":
require_Equal(t, *v1, 50)
require_Equal(t, *v2, 5000)
default:
t.Fatalf("Unexpected key: %s", key)
}
})
require_Equal(t, intersectCount, 3)
// Test with complex subjects (multiple levels)
st3 := NewSubjectTree[int]()
st4 := NewSubjectTree[int]()
// Deep nesting
st3.Insert(b("a.b.c.d.e.f.g"), 1)
st4.Insert(b("a.b.c.d.e.f.g"), 2)
// Partial matches (should not intersect)
st3.Insert(b("a.b.c.d"), 3)
st4.Insert(b("a.b.c.d.e"), 4)
// Same prefix different suffix
st3.Insert(b("prefix.suffix1"), 5)
st4.Insert(b("prefix.suffix2"), 6)
intersections := 0
LazyIntersect(st3, st4, func(key []byte, v1 *int, v2 *int) {
intersections++
require_Equal(t, string(key), "a.b.c.d.e.f.g")
require_Equal(t, *v1, 1)
require_Equal(t, *v2, 2)
})
require_Equal(t, intersections, 1)
}
func TestNode256Operations(t *testing.T) {
// Test node256 creation and basic operations
n := newNode256(b("prefix"))
require_False(t, n.isFull()) // node256 is never full
// Test findChild when child doesn't exist
child := n.findChild('a')
require_True(t, child == nil)
// Add a child and find it
leaf := newLeaf(b("suffix"), 42)
n.addChild('a', leaf)
child = n.findChild('a')
require_True(t, child != nil)
require_Equal(t, n.size, uint16(1))
// Test iter function
iterCount := 0
n.iter(func(node) bool {
iterCount++
return true
})
require_Equal(t, iterCount, 1)
// Test iter with early termination
n.addChild('b', newLeaf(b("suffix2"), 43))
n.addChild('c', newLeaf(b("suffix3"), 44))
iterCount = 0
n.iter(func(node) bool {
iterCount++
return false // Stop after first
})
require_Equal(t, iterCount, 1)
// Test children() method
children := n.children()
require_Equal(t, len(children), 256)
// Test that grow() panics
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "grow can not be called on node256")
} else {
t.Fatal("grow() should panic on node256")
}
}()
n.grow()
}
func TestNode256Shrink(t *testing.T) {
// To get a node256, we need to go through the progression:
// node4 -> node10 -> node16 -> node48 -> node256
// We need at least 49 children to get to node256
// Create nodes directly to test node256 shrinking
n256 := newNode256(b("prefix"))
// Add 49 children
for i := 0; i < 49; i++ {
n256.addChild(byte(i), newLeaf([]byte{byte(i)}, i))
}
require_Equal(t, n256.size, uint16(49))
// Shrink should not happen yet (> 48 children)
shrunk := n256.shrink()
require_True(t, shrunk == nil)
// Delete one to get to 48 children
n256.deleteChild(0)
require_Equal(t, n256.size, uint16(48))
// Now shrink should return a node48
shrunk = n256.shrink()
require_True(t, shrunk != nil)
_, isNode48 := shrunk.(*node48)
require_True(t, isNode48)
// Verify the shrunk node has all remaining children
for i := 1; i < 49; i++ {
child := shrunk.findChild(byte(i))
require_True(t, child != nil)
}
}
func TestLeafPanicMethods(t *testing.T) {
leaf := newLeaf(b("test"), 42)
// Test setPrefix panic
t.Run("setPrefix", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "setPrefix called on leaf")
} else {
t.Fatal("setPrefix should panic on leaf")
}
}()
leaf.setPrefix(b("prefix"))
})
// Test addChild panic
t.Run("addChild", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "addChild called on leaf")
} else {
t.Fatal("addChild should panic on leaf")
}
}()
leaf.addChild('a', nil)
})
// Test findChild panic
t.Run("findChild", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "findChild called on leaf")
} else {
t.Fatal("findChild should panic on leaf")
}
}()
leaf.findChild('a')
})
// Test grow panic
t.Run("grow", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "grow called on leaf")
} else {
t.Fatal("grow should panic on leaf")
}
}()
leaf.grow()
})
// Test deleteChild panic
t.Run("deleteChild", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "deleteChild called on leaf")
} else {
t.Fatal("deleteChild should panic on leaf")
}
}()
leaf.deleteChild('a')
})
// Test shrink panic
t.Run("shrink", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "shrink called on leaf")
} else {
t.Fatal("shrink should panic on leaf")
}
}()
leaf.shrink()
})
// Test other leaf methods that should work
require_True(t, leaf.isFull())
require_True(t, leaf.base() == nil)
require_Equal(t, leaf.numChildren(), uint16(0))
require_True(t, leaf.children() == nil)
// Test iter (should do nothing)
called := false
leaf.iter(func(n node) bool {
called = true
return true
})
require_False(t, called)
}
func TestSizeOnNilTree(t *testing.T) {
var st *SubjectTree[int]
require_Equal(t, st.Size(), 0)
}
func TestFindEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Test Find with empty subject at root level
st.Insert(b("foo.bar.baz"), 1)
st.Insert(b("foo"), 2)
// This should create a tree structure, now test finding with edge cases
v, found := st.Find(b(""))
require_False(t, found)
require_True(t, v == nil)
}
func TestNodeIterMethods(t *testing.T) {
// Test node4 iter
n4 := newNode4(b("prefix"))
n4.addChild('a', newLeaf(b("1"), 1))
n4.addChild('b', newLeaf(b("2"), 2))
count := 0
n4.iter(func(n node) bool {
count++
return true
})
require_Equal(t, count, 2)
// Test early termination
count = 0
n4.iter(func(n node) bool {
count++
return false
})
require_Equal(t, count, 1)
// Test node10 iter
n10 := newNode10(b("prefix"))
for i := 0; i < 5; i++ {
n10.addChild(byte('a'+i), newLeaf([]byte{byte('0' + i)}, i))
}
count = 0
n10.iter(func(n node) bool {
count++
return true
})
require_Equal(t, count, 5)
// Test node16 iter
n16 := newNode16(b("prefix"))
for i := 0; i < 8; i++ {
n16.addChild(byte('a'+i), newLeaf([]byte{byte('0' + i)}, i))
}
count = 0
n16.iter(func(n node) bool {
count++
return true
})
require_Equal(t, count, 8)
}
func TestIterOrderedAndIterFastNilRoot(t *testing.T) {
// Test IterOrdered with nil root
st := NewSubjectTree[int]()
count := 0
st.IterOrdered(func(subject []byte, val *int) bool {
count++
return true
})
require_Equal(t, count, 0)
// Test IterFast with nil root
count = 0
st.IterFast(func(subject []byte, val *int) bool {
count++
return true
})
require_Equal(t, count, 0)
}
func TestNodeAddChildPanic(t *testing.T) {
// Test node4 addChild panic when full
n4 := newNode4(b("prefix"))
n4.addChild('a', newLeaf(b("1"), 1))
n4.addChild('b', newLeaf(b("2"), 2))
n4.addChild('c', newLeaf(b("3"), 3))
n4.addChild('d', newLeaf(b("4"), 4))
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "node4 full!")
} else {
t.Fatal("addChild should panic when node4 is full")
}
}()
n4.addChild('e', newLeaf(b("5"), 5))
}
func TestNodeAddChildPanicOthers(t *testing.T) {
// Test node10 addChild panic when full
t.Run("node10", func(t *testing.T) {
n10 := newNode10(b("prefix"))
for i := 0; i < 10; i++ {
n10.addChild(byte('a'+i), newLeaf([]byte{byte('0' + i)}, i))
}
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "node10 full!")
} else {
t.Fatal("addChild should panic when node10 is full")
}
}()
n10.addChild('k', newLeaf(b("11"), 11))
})
// Test node16 addChild panic when full
t.Run("node16", func(t *testing.T) {
n16 := newNode16(b("prefix"))
for i := 0; i < 16; i++ {
n16.addChild(byte(i), newLeaf([]byte{byte(i)}, i))
}
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "node16 full!")
} else {
t.Fatal("addChild should panic when node16 is full")
}
}()
n16.addChild(16, newLeaf(b("16"), 16))
})
// Test node48 addChild panic when full
t.Run("node48", func(t *testing.T) {
n48 := newNode48(b("prefix"))
for i := 0; i < 48; i++ {
n48.addChild(byte(i), newLeaf([]byte{byte(i)}, i))
}
defer func() {
if r := recover(); r != nil {
require_Equal(t, r, "node48 full!")
} else {
t.Fatal("addChild should panic when node48 is full")
}
}()
n48.addChild(48, newLeaf(b("48"), 48))
})
}
func TestNodeDeleteChildNotFound(t *testing.T) {
// Test node10 deleteChild when child doesn't exist
n10 := newNode10(b("prefix"))
n10.addChild('a', newLeaf(b("1"), 1))
n10.addChild('b', newLeaf(b("2"), 2))
// Try to delete non-existent child
n10.deleteChild('z')
require_Equal(t, n10.size, uint16(2)) // Size should remain unchanged
// Test node16 deleteChild when child doesn't exist
n16 := newNode16(b("prefix"))
n16.addChild('a', newLeaf(b("1"), 1))
n16.addChild('b', newLeaf(b("2"), 2))
n16.deleteChild('z')
require_Equal(t, n16.size, uint16(2))
// Test node48 deleteChild when child doesn't exist
n48 := newNode48(b("prefix"))
n48.addChild(0, newLeaf(b("1"), 1))
n48.addChild(1, newLeaf(b("2"), 2))
n48.deleteChild(255)
require_Equal(t, n48.size, uint16(2))
}
func TestNodeShrinkNotNeeded(t *testing.T) {
// Test node10 shrink when not needed (has more than 4 children)
n10 := newNode10(b("prefix"))
for i := 0; i < 5; i++ {
n10.addChild(byte('a'+i), newLeaf([]byte{byte('0' + i)}, i))
}
shrunk := n10.shrink()
require_True(t, shrunk == nil) // Should not shrink
// Test node16 shrink when not needed (has more than 10 children)
n16 := newNode16(b("prefix"))
for i := 0; i < 11; i++ {
n16.addChild(byte(i), newLeaf([]byte{byte(i)}, i))
}
shrunk = n16.shrink()
require_True(t, shrunk == nil) // Should not shrink
}
func TestNode48IterEarlyTermination(t *testing.T) {
n48 := newNode48(b("prefix"))
for i := 0; i < 10; i++ {
n48.addChild(byte(i), newLeaf([]byte{byte(i)}, i))
}
count := 0
n48.iter(func(n node) bool {
count++
return false // Stop immediately
})
require_Equal(t, count, 1)
}
func TestNode10And16IterEarlyTermination(t *testing.T) {
// Test node10 early termination
n10 := newNode10(b("prefix"))
for i := 0; i < 5; i++ {
n10.addChild(byte('a'+i), newLeaf([]byte{byte('0' + i)}, i))
}
count := 0
n10.iter(func(n node) bool {
count++
return count < 2 // Stop after 2
})
require_Equal(t, count, 2)
// Test node16 early termination
n16 := newNode16(b("prefix"))
for i := 0; i < 8; i++ {
n16.addChild(byte(i), newLeaf([]byte{byte(i)}, i))
}
count = 0
n16.iter(func(n node) bool {
count++
return count < 3 // Stop after 3
})
require_Equal(t, count, 3)
}
func TestMatchPartsEdgeCases(t *testing.T) {
// Test the edge case in matchParts that's not covered
// This is the case where we have a part that needs to be copied and modified
// Create a complex filter that will trigger the edge case
filter := b("foo.*.bar.>")
parts := genParts(filter, nil)
// Test with a fragment that will cause partial matching
frag := b("foo.test")
remaining, matched := matchParts(parts, frag)
require_True(t, matched)
require_True(t, len(remaining) > 0)
}
func TestInsertEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Test inserting with noPivot byte (should fail)
old, updated := st.Insert([]byte("foo\x7fbar"), 1)
require_True(t, old == nil)
require_False(t, updated)
require_Equal(t, st.Size(), 0) // Should not insert
// Test the edge case where we need to split with same pivot
st = NewSubjectTree[int]()
// This case tests subjects that cause the same pivot after split
// Both subjects share prefix "a" and have same pivot "." after split
st.Insert(b("a.b"), 1)
// Now insert one that will cause the split with same pivot
st.Insert(b("a.c"), 2)
require_Equal(t, st.Size(), 2)
}
func TestDeleteEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Test delete on empty tree
val, deleted := st.Delete(b("foo"))
require_False(t, deleted)
require_True(t, val == nil)
// Test delete with empty subject
st.Insert(b("foo"), 1)
val, deleted = st.Delete(b(""))
require_False(t, deleted)
require_True(t, val == nil)
// Test delete with subject shorter than prefix
st = NewSubjectTree[int]()
st.Insert(b("verylongprefix.suffix"), 1)
st.Insert(b("verylongprefix.suffix2"), 2)
val, deleted = st.Delete(b("very"))
require_False(t, deleted)
require_True(t, val == nil)
}
func TestMatchEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Test match with nil callback
st.Insert(b("foo.bar"), 1)
st.Match(b("foo.*"), nil) // Should not panic
// Test match with empty filter
count := 0
st.Match(b(""), func(subject []byte, val *int) {
count++
})
require_Equal(t, count, 0)
}
func TestIterEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Add multiple subjects to create a complex tree
st.Insert(b("a.b.c"), 1)
st.Insert(b("a.b.d"), 2)
st.Insert(b("a.c.d"), 3)
st.Insert(b("b.c.d"), 4)
// Test iter with early termination at different points
count := 0
st.iter(st.root, nil, false, func(subject []byte, val *int) bool {
count++
return count < 2
})
require_Equal(t, count, 2)
}
func TestLeafIter(t *testing.T) {
// Test that leaf iter does nothing (it's a no-op)
leaf := newLeaf(b("test"), 42)
called := false
// Call iter with a function that would set called to true
leaf.iter(func(n node) bool {
called = true
return true
})
require_False(t, called) // Should never be called since leaf.iter is a no-op
// Call iter again with a function that returns false
leaf.iter(func(n node) bool {
called = true
return false
})
require_False(t, called) // Still should never be called
// Verify the leaf itself is not affected
require_True(t, leaf.match(b("test")))
require_Equal(t, leaf.value, 42)
// Also test through the node interface to ensure coverage
var n node = leaf
called = false
n.iter(func(child node) bool {
called = true
return true
})
require_False(t, called) // Still should never be called
}
func TestDeleteChildEdgeCasesMore(t *testing.T) {
// Test the edge case in node10 deleteChild where we don't swap (last element)
n10 := newNode10(b("prefix"))
n10.addChild('a', newLeaf(b("1"), 1))
n10.addChild('b', newLeaf(b("2"), 2))
n10.addChild('c', newLeaf(b("3"), 3))
// Delete the last child
n10.deleteChild('c')
require_Equal(t, n10.size, uint16(2))
// Test the edge case in node16 deleteChild where we don't swap (last element)
n16 := newNode16(b("prefix"))
n16.addChild('a', newLeaf(b("1"), 1))
n16.addChild('b', newLeaf(b("2"), 2))
n16.addChild('c', newLeaf(b("3"), 3))
// Delete the last child
n16.deleteChild('c')
require_Equal(t, n16.size, uint16(2))
}
func TestMatchPartsMoreEdgeCases(t *testing.T) {
// Test the remaining 2.6% of matchParts
// Case where frag is empty
parts := genParts(b("foo.*"), nil)
remaining, matched := matchParts(parts, b(""))
require_True(t, matched)
require_Equal(t, len(remaining), len(parts))
}
func TestInsertComplexEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Test the recursive insert case with same pivot
// This requires a very specific setup
// First, create a tree structure that will trigger the recursive path
st.Insert(b("a"), 1)
st.Insert(b("aa"), 2) // This will create a split
// Now insert something that has the same pivot after split
st.Insert(b("aaa"), 3) // This should trigger the recursive insert path
require_Equal(t, st.Size(), 3)
// Verify all values can be found
v, found := st.Find(b("a"))
require_True(t, found)
require_Equal(t, *v, 1)
v, found = st.Find(b("aa"))
require_True(t, found)
require_Equal(t, *v, 2)
v, found = st.Find(b("aaa"))
require_True(t, found)
require_Equal(t, *v, 3)
}
func TestDeleteNilNodePointer(t *testing.T) {
st := NewSubjectTree[int]()
// Test delete with nil node
var n node
val, deleted := st.delete(&n, b("foo"), 0)
require_False(t, deleted)
require_True(t, val == nil)
}
func TestMatchComplexEdgeCases(t *testing.T) {
st := NewSubjectTree[int]()
// Build a complex tree to test the 2.2% uncovered in match
st.Insert(b("foo.bar.baz"), 1)
st.Insert(b("foo.bar.qux"), 2)
st.Insert(b("foo.baz.bar"), 3)
st.Insert(b("bar.foo.baz"), 4)
// Test with terminal fwc but no remaining parts
count := 0
st.Match(b("foo.bar.>"), func(subject []byte, val *int) {
count++
})
require_Equal(t, count, 2)
}
func TestIterComplexTree(t *testing.T) {
st := NewSubjectTree[int]()
// Build a deeper tree to test the remaining iter cases
for i := 0; i < 20; i++ {
st.Insert([]byte(fmt.Sprintf("level1.level2.level3.item%d", i)), i)
}
// This should create multiple node types and test more paths
count := 0
st.IterOrdered(func(subject []byte, val *int) bool {
count++
return true
})
require_Equal(t, count, 20)
}