Files
upstream-mattermost/server/public/utils/array_test.go
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

500 lines
15 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package utils
import (
"reflect"
"slices"
"testing"
"time"
)
func TestDedup(t *testing.T) {
t.Run("strings", func(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{name: "nil stays nil", input: nil, expected: nil},
{name: "empty stays empty", input: []string{}, expected: []string{}},
{name: "no duplicates is unchanged", input: []string{"a", "b", "c"}, expected: []string{"a", "b", "c"}},
{name: "adjacent duplicates", input: []string{"a", "a", "b"}, expected: []string{"a", "b"}},
{name: "non-adjacent duplicates keep first occurrence order", input: []string{"b", "a", "b", "c", "a"}, expected: []string{"b", "a", "c"}},
{name: "all duplicates", input: []string{"a", "a", "a"}, expected: []string{"a"}},
{name: "empty string is a value like any other", input: []string{"", "a", ""}, expected: []string{"", "a"}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := Dedup(tc.input)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("Dedup(%v) = %v, want %v", tc.input, result, tc.expected)
}
if tc.input == nil && result != nil {
t.Errorf("Dedup(nil) should stay nil, got %v", result)
}
})
}
})
t.Run("integers", func(t *testing.T) {
result := Dedup([]int{3, 1, 3, 2, 1})
expected := []int{3, 1, 2}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Dedup() = %v, want %v", result, expected)
}
})
t.Run("structs", func(t *testing.T) {
type key struct {
Group string
ID int
}
result := Dedup([]key{{"a", 1}, {"a", 1}, {"a", 2}, {"b", 1}})
expected := []key{{"a", 1}, {"a", 2}, {"b", 1}}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Dedup() = %v, want %v", result, expected)
}
})
t.Run("does not modify the input", func(t *testing.T) {
input := []string{"a", "b", "a"}
Dedup(input)
if !reflect.DeepEqual(input, []string{"a", "b", "a"}) {
t.Errorf("Dedup mutated its input: %v", input)
}
})
}
func TestFindExclusives(t *testing.T) {
t.Run("integers", func(t *testing.T) {
tests := []struct {
name string
arr1, arr2 []int
expectedExclusive1 []int
expectedExclusive2 []int
expectedCommon []int
}{
// Basic test with non-overlapping elements
{
name: "No overlap",
arr1: []int{1, 2, 3},
arr2: []int{4, 5, 6},
expectedExclusive1: []int{1, 2, 3},
expectedExclusive2: []int{4, 5, 6},
expectedCommon: nil,
},
// Fully overlapping arrays
{
name: "Full overlap",
arr1: []int{1, 2, 3},
arr2: []int{1, 2, 3},
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: []int{1, 2, 3},
},
// Partial overlap
{
name: "Partial overlap",
arr1: []int{1, 2, 3, 4},
arr2: []int{3, 4, 5, 6},
expectedExclusive1: []int{1, 2},
expectedExclusive2: []int{5, 6},
expectedCommon: []int{3, 4},
},
// Duplicates within arrays
{
name: "Duplicates in arr1",
arr1: []int{1, 2, 2, 3},
arr2: []int{2, 4, 4},
expectedExclusive1: []int{1, 3},
expectedExclusive2: []int{4},
expectedCommon: []int{2},
},
{
name: "Duplicates in arr2",
arr1: []int{1, 2, 3},
arr2: []int{2, 2, 3, 3},
expectedExclusive1: []int{1},
expectedExclusive2: nil,
expectedCommon: []int{2, 3},
},
// Edge cases
{
name: "Both arrays nil",
arr1: nil,
arr2: nil,
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "Both arrays empty",
arr1: []int{},
arr2: []int{},
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "One empty array",
arr1: []int{1, 2, 3},
arr2: nil,
expectedExclusive1: []int{1, 2, 3},
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "One element in each array",
arr1: []int{1},
arr2: []int{2},
expectedExclusive1: []int{1},
expectedExclusive2: []int{2},
expectedCommon: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
exclusive1, exclusive2, common := FindExclusives(tt.arr1, tt.arr2)
slices.Sort(exclusive1)
slices.Sort(exclusive2)
slices.Sort(common)
slices.Sort(tt.expectedExclusive1)
slices.Sort(tt.expectedExclusive2)
slices.Sort(tt.expectedCommon)
if !reflect.DeepEqual(exclusive1, tt.expectedExclusive1) {
t.Errorf("Exclusive to arr1: expected %v, got %v", tt.expectedExclusive1, exclusive1)
}
if !reflect.DeepEqual(exclusive2, tt.expectedExclusive2) {
t.Errorf("Exclusive to arr2: expected %v, got %v", tt.expectedExclusive2, exclusive2)
}
if !reflect.DeepEqual(common, tt.expectedCommon) {
t.Errorf("Common elements: expected %v, got %v", tt.expectedCommon, common)
}
})
}
})
t.Run("strings", func(t *testing.T) {
tests := []struct {
name string
arr1, arr2 []string
expectedExclusive1 []string
expectedExclusive2 []string
expectedCommon []string
}{
// Basic test with non-overlapping elements
{
name: "No overlap",
arr1: []string{"a", "b", "c"},
arr2: []string{"d", "e", "f"},
expectedExclusive1: []string{"a", "b", "c"},
expectedExclusive2: []string{"d", "e", "f"},
expectedCommon: nil,
},
// Fully overlapping arrays
{
name: "Full overlap",
arr1: []string{"a", "b", "c"},
arr2: []string{"a", "b", "c"},
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: []string{"a", "b", "c"},
},
// Partial overlap
{
name: "Partial overlap",
arr1: []string{"a", "b", "c", "d"},
arr2: []string{"c", "d", "e", "f"},
expectedExclusive1: []string{"a", "b"},
expectedExclusive2: []string{"e", "f"},
expectedCommon: []string{"c", "d"},
},
// Duplicates within arrays
{
name: "Duplicates in arr1",
arr1: []string{"a", "b", "b", "c"},
arr2: []string{"b", "d", "d"},
expectedExclusive1: []string{"a", "c"},
expectedExclusive2: []string{"d"},
expectedCommon: []string{"b"},
},
{
name: "Duplicates in arr2",
arr1: []string{"a", "b", "c"},
arr2: []string{"b", "b", "c", "c"},
expectedExclusive1: []string{"a"},
expectedExclusive2: nil,
expectedCommon: []string{"b", "c"},
},
// Edge cases
{
name: "Both arrays nil",
arr1: nil,
arr2: nil,
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "Both arrays empty",
arr1: []string{},
arr2: []string{},
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "One empty array",
arr1: []string{"a", "b", "c"},
arr2: nil,
expectedExclusive1: []string{"a", "b", "c"},
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "One element in each array",
arr1: []string{"a"},
arr2: []string{"b"},
expectedExclusive1: []string{"a"},
expectedExclusive2: []string{"b"},
expectedCommon: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
exclusive1, exclusive2, common := FindExclusives(tt.arr1, tt.arr2)
slices.Sort(exclusive1)
slices.Sort(exclusive2)
slices.Sort(common)
slices.Sort(tt.expectedExclusive1)
slices.Sort(tt.expectedExclusive2)
slices.Sort(tt.expectedCommon)
if !reflect.DeepEqual(exclusive1, tt.expectedExclusive1) {
t.Errorf("Exclusive to arr1: expected %v, got %v", tt.expectedExclusive1, exclusive1)
}
if !reflect.DeepEqual(exclusive2, tt.expectedExclusive2) {
t.Errorf("Exclusive to arr2: expected %v, got %v", tt.expectedExclusive2, exclusive2)
}
if !reflect.DeepEqual(common, tt.expectedCommon) {
t.Errorf("Common elements: expected %v, got %v", tt.expectedCommon, common)
}
})
}
})
t.Run("dates", func(t *testing.T) {
tests := []struct {
name string
arr1, arr2 []time.Time
expectedExclusive1 []time.Time
expectedExclusive2 []time.Time
expectedCommon []time.Time
}{
// Basic test with non-overlapping elements
{
name: "No overlap",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
arr2: []time.Time{
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 5, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 6, 0, 0, 0, 0, time.UTC),
},
expectedExclusive1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
expectedExclusive2: []time.Time{
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 5, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 6, 0, 0, 0, 0, time.UTC),
},
expectedCommon: nil,
},
// Fully overlapping arrays
{
name: "Full overlap",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
arr2: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
},
// Partial overlap
{
name: "Partial overlap",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
},
arr2: []time.Time{
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 5, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 6, 0, 0, 0, 0, time.UTC),
},
expectedExclusive1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
},
expectedExclusive2: []time.Time{
time.Date(2023, 1, 5, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 6, 0, 0, 0, 0, time.UTC),
},
expectedCommon: []time.Time{
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
},
},
// Duplicates within arrays
{
name: "Duplicates in arr1",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
arr2: []time.Time{
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
},
expectedExclusive1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
expectedExclusive2: []time.Time{
time.Date(2023, 1, 4, 0, 0, 0, 0, time.UTC),
},
expectedCommon: []time.Time{
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
},
},
{
name: "Duplicates in arr2",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
arr2: []time.Time{
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
expectedExclusive1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
},
expectedExclusive2: nil,
expectedCommon: []time.Time{
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
},
// Edge cases
{
name: "Both arrays nil",
arr1: nil,
arr2: nil,
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "Both arrays empty",
arr1: []time.Time{},
arr2: []time.Time{},
expectedExclusive1: nil,
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "One empty array",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
arr2: nil,
expectedExclusive1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
time.Date(2023, 1, 3, 0, 0, 0, 0, time.UTC),
},
expectedExclusive2: nil,
expectedCommon: nil,
},
{
name: "One element in each array",
arr1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
},
arr2: []time.Time{
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
},
expectedExclusive1: []time.Time{
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
},
expectedExclusive2: []time.Time{
time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
},
expectedCommon: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
exclusive1, exclusive2, common := FindExclusives(tt.arr1, tt.arr2)
slices.SortFunc(exclusive1, func(a, b time.Time) int { return a.Compare(b) })
slices.SortFunc(exclusive2, func(a, b time.Time) int { return a.Compare(b) })
slices.SortFunc(common, func(a, b time.Time) int { return a.Compare(b) })
slices.SortFunc(tt.expectedExclusive1, func(a, b time.Time) int { return a.Compare(b) })
slices.SortFunc(tt.expectedExclusive2, func(a, b time.Time) int { return a.Compare(b) })
slices.SortFunc(tt.expectedCommon, func(a, b time.Time) int { return a.Compare(b) })
if !reflect.DeepEqual(exclusive1, tt.expectedExclusive1) {
t.Errorf("Exclusive to arr1: expected %v, got %v", tt.expectedExclusive1, exclusive1)
}
if !reflect.DeepEqual(exclusive2, tt.expectedExclusive2) {
t.Errorf("Exclusive to arr2: expected %v, got %v", tt.expectedExclusive2, exclusive2)
}
if !reflect.DeepEqual(common, tt.expectedCommon) {
t.Errorf("Common elements: expected %v, got %v", tt.expectedCommon, common)
}
})
}
})
}