mirror of
https://github.com/tinode/chat.git
synced 2026-05-07 20:12:42 +00:00
533 lines
12 KiB
Go
533 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/tinode/chat/server/store/types"
|
|
)
|
|
|
|
func slicesEqual(expected, gotten []string) bool {
|
|
if len(expected) != len(gotten) {
|
|
return false
|
|
}
|
|
for i, v := range expected {
|
|
if v != gotten[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func expectSlicesEqual(t *testing.T, name string, expected, gotten []string) {
|
|
if !slicesEqual(expected, gotten) {
|
|
e := "'" + strings.Join(expected, "','") + "'"
|
|
g := "'" + strings.Join(gotten, "','") + "'"
|
|
t.Errorf("%s: expected %+v, got %+v", name, e, g)
|
|
}
|
|
}
|
|
|
|
func TestStringSliceDelta(t *testing.T) {
|
|
// Case format:
|
|
// - inputs: old, new
|
|
// - expected outputs: added, removed, intersection
|
|
cases := [][5][]string{
|
|
{
|
|
{"abc", "def", "fff"}, {},
|
|
{}, {"abc", "def", "fff"}, {},
|
|
},
|
|
{
|
|
{}, {}, {}, {}, {},
|
|
},
|
|
{
|
|
{"aa", "xx", "bb", "aa", "bb"}, {"yy", "aa"},
|
|
{"yy"}, {"aa", "bb", "bb", "xx"}, {"aa"},
|
|
},
|
|
{
|
|
{"bb", "aa", "bb"}, {"yy", "aa", "bb", "zzz", "zzz", "cc"},
|
|
{"cc", "yy", "zzz", "zzz"}, {"bb"}, {"aa", "bb"},
|
|
},
|
|
{
|
|
{"aa", "aa", "aa"}, {"aa", "aa", "aa"},
|
|
{}, {}, {"aa", "aa", "aa"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
added, removed, both := stringSliceDelta(
|
|
tc[0], tc[1],
|
|
)
|
|
expectSlicesEqual(t, "added", tc[2], added)
|
|
expectSlicesEqual(t, "removed", tc[3], removed)
|
|
expectSlicesEqual(t, "both", tc[4], both)
|
|
|
|
}
|
|
}
|
|
|
|
func TestParseSearchQuery(t *testing.T) {
|
|
cases := []struct {
|
|
query string
|
|
expectedAnd []string
|
|
expectedOr []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
query: `tag1 tag2 tag3`,
|
|
expectedAnd: []string{"tag1", "tag2", "tag3"},
|
|
expectedOr: []string{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1,tag2,tag3`,
|
|
expectedAnd: []string{},
|
|
expectedOr: []string{"tag1", "tag2", "tag3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1 tag2,tag3`,
|
|
expectedAnd: []string{"tag1"},
|
|
expectedOr: []string{"tag2", "tag3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1 tag2,tag3 "tag4 tag5"`,
|
|
expectedAnd: []string{"tag1", "tag4 tag5"},
|
|
expectedOr: []string{"tag2", "tag3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1,tag2 tag3`,
|
|
expectedAnd: []string{"tag3"},
|
|
expectedOr: []string{"tag1", "tag2"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `"tag1 tag2" tag3,tag4`,
|
|
expectedAnd: []string{"tag1 tag2"},
|
|
expectedOr: []string{"tag3", "tag4"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1 "tag2 tag3"`,
|
|
expectedAnd: []string{"tag1", "tag2 tag3"},
|
|
expectedOr: []string{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1, tag2, tag3`,
|
|
expectedAnd: []string{},
|
|
expectedOr: []string{"tag1", "tag2", "tag3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1 , tag2 ,tag3`,
|
|
expectedAnd: []string{},
|
|
expectedOr: []string{"tag1", "tag2", "tag3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1 , tag2 tag3`,
|
|
expectedAnd: []string{"tag3"},
|
|
expectedOr: []string{"tag1", "tag2"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1 "unterminated quote`,
|
|
expectedAnd: nil,
|
|
expectedOr: nil,
|
|
expectError: true,
|
|
},
|
|
{
|
|
query: `tag1,,tag2`,
|
|
expectedAnd: nil,
|
|
expectedOr: nil,
|
|
expectError: true,
|
|
},
|
|
{
|
|
query: `tag1 "tag2" tag3`,
|
|
expectedAnd: []string{"tag1", "tag2", "tag3"},
|
|
expectedOr: []string{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
query: `tag1"tag2" quote in the middle`,
|
|
expectedAnd: nil,
|
|
expectedOr: nil,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
and, or, err := parseSearchQuery(tc.query)
|
|
if tc.expectError {
|
|
if err == nil {
|
|
t.Errorf("expected error for query '%s', got none", tc.query)
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("unexpected error for query '%s': %v", tc.query, err)
|
|
} else {
|
|
expectSlicesEqual(t, tc.query+" AND", tc.expectedAnd, and)
|
|
expectSlicesEqual(t, tc.query+" OR", tc.expectedOr, or)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeTags(t *testing.T) {
|
|
cases := []struct {
|
|
input []string
|
|
expected types.StringSlice
|
|
}{
|
|
{
|
|
input: []string{" Tag1 ", "tag2", "TAG3", "tag1"},
|
|
expected: types.StringSlice{"tag1", "tag2", "tag3"},
|
|
},
|
|
{
|
|
input: []string{" ", "tag2", "TAG3", "tag1"},
|
|
expected: types.StringSlice{"tag1", "tag2", "tag3"},
|
|
},
|
|
{
|
|
input: []string{"tag1"},
|
|
expected: types.StringSlice{"tag1"},
|
|
},
|
|
{
|
|
input: []string{"tag1", nullValue},
|
|
expected: []string{},
|
|
},
|
|
{
|
|
input: []string{},
|
|
expected: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := normalizeTags(tc.input, 16)
|
|
expectSlicesEqual(t, "normalizeTags", tc.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestRestrictedTagsEqual(t *testing.T) {
|
|
cases := []struct {
|
|
oldTags []string
|
|
newTags []string
|
|
namespaces map[string]bool
|
|
expected bool
|
|
}{
|
|
{
|
|
oldTags: []string{"ns1:tag1", "ns2:tag2"},
|
|
newTags: []string{"ns1:tag1", "ns2:tag2"},
|
|
namespaces: map[string]bool{"ns1": true, "ns2": true},
|
|
expected: true,
|
|
},
|
|
{
|
|
oldTags: []string{"ns1:tag1", "ns2:tag2"},
|
|
newTags: []string{"ns1:tag1", "ns2:tag3"},
|
|
namespaces: map[string]bool{"ns1": true, "ns2": true},
|
|
expected: false,
|
|
},
|
|
{
|
|
oldTags: []string{"ns1:tag1", "ns2:tag2"},
|
|
newTags: []string{"ns1:tag1"},
|
|
namespaces: map[string]bool{"ns1": true, "ns2": true},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := restrictedTagsEqual(tc.oldTags, tc.newTags, tc.namespaces)
|
|
if got != tc.expected {
|
|
t.Errorf("restrictedTagsEqual: expected %v, got %v", tc.expected, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsNullValue(t *testing.T) {
|
|
cases := []struct {
|
|
input any
|
|
expected bool
|
|
}{
|
|
{
|
|
input: nullValue,
|
|
expected: true,
|
|
},
|
|
{
|
|
input: "some string",
|
|
expected: false,
|
|
},
|
|
{
|
|
input: 123,
|
|
expected: false,
|
|
},
|
|
{
|
|
input: nil,
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := isNullValue(tc.input)
|
|
if got != tc.expected {
|
|
t.Errorf("isNullValue: expected %v, got %v", tc.expected, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseVersion(t *testing.T) {
|
|
cases := []struct {
|
|
input string
|
|
expected int
|
|
}{
|
|
{
|
|
input: "1.2.3",
|
|
expected: (1 << 16) | (2 << 8) | 3,
|
|
},
|
|
{
|
|
input: "1.2",
|
|
expected: (1 << 16) | (2 << 8),
|
|
},
|
|
{
|
|
input: "1",
|
|
expected: (1 << 16),
|
|
},
|
|
{
|
|
input: "v1.2.3",
|
|
expected: (1 << 16) | (2 << 8) | 3,
|
|
},
|
|
{
|
|
input: "v1.2",
|
|
expected: (1 << 16) | (2 << 8),
|
|
},
|
|
{
|
|
input: "v1",
|
|
expected: (1 << 16),
|
|
},
|
|
{
|
|
input: "1.2.3-rc1",
|
|
expected: (1 << 16) | (2 << 8) | 3,
|
|
},
|
|
{
|
|
input: "v1.2.3-rc1",
|
|
expected: (1 << 16) | (2 << 8) | 3,
|
|
},
|
|
{
|
|
input: "0.0.0",
|
|
expected: 0,
|
|
},
|
|
{
|
|
input: "v0.0.0",
|
|
expected: 0,
|
|
},
|
|
{
|
|
input: "1.2.8192", // 8192 is greater than 0x1fff, should be ignored
|
|
expected: (1 << 16) | (2 << 8),
|
|
},
|
|
{
|
|
input: "1.8192.3", // 8192 is greater than 0x1fff, should be ignored
|
|
expected: (1 << 16) | 3,
|
|
},
|
|
{
|
|
input: "8192.2.3", // 8192 is greater than 0x1fff, should be ignored
|
|
expected: (2 << 8) | 3,
|
|
},
|
|
{
|
|
input: "",
|
|
expected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := parseVersion(tc.input)
|
|
if got != tc.expected {
|
|
t.Errorf("parseVersion(%q): expected %d, got %d", tc.input, tc.expected, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMergeMaps(t *testing.T) {
|
|
cases := []struct {
|
|
dst map[string]any
|
|
src map[string]any
|
|
expected map[string]any
|
|
changed bool
|
|
}{
|
|
{
|
|
dst: map[string]any{"a": 1, "b": 2},
|
|
src: map[string]any{"b": 3, "c": 4},
|
|
expected: map[string]any{"a": 1, "b": 3, "c": 4},
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: map[string]any{"a": 1, "b": map[string]any{"x": 1}},
|
|
src: map[string]any{"b": map[string]any{"y": 2}},
|
|
expected: map[string]any{"a": 1, "b": map[string]any{"x": 1, "y": 2}},
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: map[string]any{"a": 1, "b": map[string]any{"x": 1}},
|
|
src: map[string]any{"b": map[string]any{"x": nullValue}},
|
|
expected: map[string]any{"a": 1, "b": map[string]any{}},
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: map[string]any{"a": 1, "b": 2},
|
|
src: map[string]any{},
|
|
expected: map[string]any{"a": 1, "b": 2},
|
|
changed: false,
|
|
},
|
|
{
|
|
dst: nil,
|
|
src: map[string]any{"a": 1},
|
|
expected: map[string]any{"a": 1},
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: map[string]any{"a": 1},
|
|
src: map[string]any{"a": nullValue},
|
|
expected: map[string]any{},
|
|
changed: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got, changed := mergeMaps(tc.dst, tc.src)
|
|
if !reflect.DeepEqual(got, tc.expected) || changed != tc.changed {
|
|
t.Errorf("mergeMaps(%v, %v): expected (%v, %v), got (%v, %v)", tc.dst, tc.src, tc.expected, tc.changed, got, changed)
|
|
}
|
|
}
|
|
}
|
|
func TestMergeInterfaces(t *testing.T) {
|
|
cases := []struct {
|
|
dst any
|
|
src any
|
|
expected any
|
|
changed bool
|
|
}{
|
|
{
|
|
dst: map[string]any{"a": 1, "b": map[string]any{"x": 1}},
|
|
src: map[string]any{"b": map[string]any{"y": 2}},
|
|
expected: map[string]any{"a": 1, "b": map[string]any{"x": 1, "y": 2}},
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: map[string]any{"a": 1, "b": map[string]any{"x": 1}},
|
|
src: map[string]any{"b": map[string]any{"x": nullValue}},
|
|
expected: map[string]any{"a": 1, "b": map[string]any{}},
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: map[string]any{"a": 1},
|
|
src: nullValue,
|
|
expected: nil,
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: "old string",
|
|
src: "new string",
|
|
expected: "new string",
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: "old string",
|
|
src: 12345,
|
|
expected: 12345,
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: "old string",
|
|
src: nullValue,
|
|
expected: nil,
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: 123,
|
|
src: 456,
|
|
expected: 456,
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: 123,
|
|
src: nil,
|
|
expected: 123,
|
|
changed: false,
|
|
},
|
|
{
|
|
dst: 123,
|
|
src: true,
|
|
expected: true,
|
|
changed: true,
|
|
},
|
|
{
|
|
dst: []string{"a", "b", "c"},
|
|
src: []string{"d", "e", "f"},
|
|
expected: []string{"d", "e", "f"},
|
|
changed: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got, changed := mergeInterfaces(tc.dst, tc.src)
|
|
if !reflect.DeepEqual(got, tc.expected) || changed != tc.changed {
|
|
t.Errorf("mergeInterfaces(%v, %v): expected (%v, %v), got (%v, %v)", tc.dst, tc.src, tc.expected, tc.changed, got, changed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFilterTags(t *testing.T) {
|
|
cases := []struct {
|
|
tags []string
|
|
ns map[string]bool
|
|
expected []string
|
|
}{
|
|
{
|
|
tags: []string{"ns1:tag1", "ns2:tag3", "nons", "inval::tag", ":tag3", "tag4:", "tag5: "},
|
|
ns: map[string]bool{"ns1": true, "ns2": false, "xtra": true},
|
|
expected: []string{"ns1:tag1"},
|
|
},
|
|
{
|
|
tags: []string{"ns1:tag1", "ns2:tag3", "nons", "inval::tag", ":tag3", "tag4:", "tag5: "},
|
|
ns: map[string]bool{},
|
|
expected: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := filterTags(tc.tags, tc.ns)
|
|
if !reflect.DeepEqual(got, tc.expected) {
|
|
t.Errorf("filterTags(%v, %v): expected (%v), got (%v)", tc.tags, tc.ns, tc.expected, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHasDuplicateNamespaceTags(t *testing.T) {
|
|
cases := []struct {
|
|
tags []string
|
|
ns string
|
|
expected bool
|
|
}{
|
|
{
|
|
tags: []string{"ns1:tag1", "ns2:tag3", "nons", "inval::tag", ":tag3", "tag4:", "tag5: "},
|
|
ns: "ns1",
|
|
expected: false,
|
|
},
|
|
{
|
|
tags: []string{"ns1:tag1", "ns2:tag3", "nons", "inval::tag", ":tag3", "tag4:", "tag5: ", "ns1:tag2"},
|
|
ns: "ns1",
|
|
expected: true,
|
|
},
|
|
{
|
|
tags: []string{"ns1:tag1", "ns2:tag3", "nons", "inval::tag", ":tag3", "tag4:", "tag5: "},
|
|
ns: "",
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := hasDuplicateNamespaceTags(tc.tags, tc.ns)
|
|
if !reflect.DeepEqual(got, tc.expected) {
|
|
t.Errorf("filterTags(%v, %v): expected (%v), got (%v)", tc.tags, tc.ns, tc.expected, got)
|
|
}
|
|
}
|
|
}
|