snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,474 @@
package tests
import (
"context"
"fmt"
"strings"
"testing"
"time"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/pkg/id"
"github.com/cortezaproject/corteza/server/pkg/rand"
"github.com/cortezaproject/corteza/server/store"
"github.com/cortezaproject/corteza/server/system/types"
_ "github.com/joho/godotenv/autoload"
"github.com/stretchr/testify/require"
)
func testUsers(t *testing.T, s store.Users) {
var (
dummyID uint64 = 0
ctx = context.Background()
makeNew = func(nn ...string) *types.User {
// minimum data set for new user
name := strings.Join(nn, "")
dummyID = dummyID + 1
return &types.User{
ID: dummyID,
CreatedAt: time.Now(),
Email: "user-crud+" + name + "@crust.test",
Username: "username_" + name,
Handle: "handle_" + name,
}
}
truncAndCreate = func(t *testing.T) (*require.Assertions, *types.User) {
req := require.New(t)
req.NoError(s.TruncateUsers(ctx))
user := makeNew()
req.NoError(s.CreateUser(ctx, user))
return req, user
}
truncAndFill = func(t *testing.T, l int) (*require.Assertions, types.UserSet) {
req := require.New(t)
req.NoError(s.TruncateUsers(ctx))
set := make([]*types.User, l)
for i := 0; i < l; i++ {
set[i] = makeNew(string(rand.Bytes(10)))
}
req.NoError(s.CreateUser(ctx, set...))
return req, set
}
stringifySetRange = func(set types.UserSet) string {
if len(set) == 0 {
return ""
}
var out = set[0].Handle[7:]
if len(set) > 1 {
out += ".." + set[len(set)-1].Handle[7:]
}
return out
}
// in case we need some quick old-school debugging
//dbg := func(uu ...*types.User) {
// for i, u := range uu {
// fmt.Printf(" => #%2d %s\n", i+1, u.Handle)
// }
//}
)
t.Run("create", func(t *testing.T) {
req := require.New(t)
req.NoError(s.TruncateUsers(ctx))
req.NoError(store.CreateUser(ctx, s, makeNew()))
})
t.Run("create with duplicate email", func(t *testing.T) {
req := require.New(t)
req.NoError(s.TruncateUsers(ctx))
req.NoError(s.CreateUser(ctx, makeNew("a")))
req.EqualError(s.CreateUser(ctx, makeNew("a")), store.ErrNotUnique.Error())
})
t.Run("create with duplicate ID", func(t *testing.T) {
req := require.New(t)
req.NoError(s.TruncateUsers(ctx))
user := makeNew("a")
req.NoError(s.CreateUser(ctx, user))
req.EqualError(s.CreateUser(ctx, user), store.ErrNotUnique.Error())
})
t.Run("lookup by ID", func(t *testing.T) {
req, user := truncAndCreate(t)
fetched, err := store.LookupUserByID(ctx, s, user.ID)
req.NoError(err)
req.Equal(user.Email, fetched.Email)
req.Equal(user.Username, fetched.Username)
req.Equal(user.Handle, fetched.Handle)
req.Equal(user.ID, fetched.ID)
req.NotNil(fetched.CreatedAt)
req.Nil(fetched.UpdatedAt)
req.Nil(fetched.DeletedAt)
req.Nil(fetched.SuspendedAt)
})
t.Run("update", func(t *testing.T) {
req, user := truncAndCreate(t)
req.NoError(s.UpdateUser(ctx, user))
})
t.Run("lookup by email", func(t *testing.T) {
req, user := truncAndCreate(t)
fetched, err := store.LookupUserByEmail(ctx, s, user.Email)
req.NoError(err)
req.Equal(user.Email, fetched.Email)
})
t.Run("lookup by handle", func(t *testing.T) {
req, user := truncAndCreate(t)
fetched, err := store.LookupUserByHandle(ctx, s, user.Handle)
req.NoError(err)
req.Equal(user.ID, fetched.ID)
})
t.Run("lookup by nonexisting handle", func(t *testing.T) {
req, _ := truncAndCreate(t)
fetched, err := store.LookupUserByHandle(ctx, s, "no such handle")
req.EqualError(err, "not found")
req.Nil(fetched)
})
t.Run("lookup by username", func(t *testing.T) {
req, user := truncAndCreate(t)
fetched, err := store.LookupUserByUsername(ctx, s, user.Username)
req.NoError(err)
req.Equal(user.ID, fetched.ID)
})
t.Run("create and update deleted with existing email", func(t *testing.T) {
req, user := truncAndCreate(t)
deletedUser := makeNew("copy")
deletedUser.DeletedAt = now()
deletedUser.Email = user.Email
req.NoError(store.CreateUser(ctx, s, deletedUser))
req.NoError(store.UpdateUser(ctx, s, deletedUser))
})
t.Run("search", func(t *testing.T) {
t.Run("by ID", func(t *testing.T) {
req, prefill := truncAndFill(t, 5)
set, f, err := store.SearchUsers(ctx, s, types.UserFilter{UserID: id.Strings(prefill[0].ID)})
req.NoError(err)
req.Equal(id.Strings(prefill[0].ID), f.UserID)
req.Len(set, 1)
//req.Equal(set[0].ID, user.ID)
})
t.Run("by email", func(t *testing.T) {
req, prefill := truncAndFill(t, 5)
set, _, err := store.SearchUsers(ctx, s, types.UserFilter{Email: prefill[0].Email})
req.NoError(err)
req.Len(set, 1)
})
t.Run("by username", func(t *testing.T) {
req, prefill := truncAndFill(t, 5)
set, _, err := store.SearchUsers(ctx, s, types.UserFilter{Username: prefill[0].Username})
req.NoError(err)
req.Len(set, 1)
})
t.Run("by query", func(t *testing.T) {
req, prefill := truncAndFill(t, 5)
set, _, err := store.SearchUsers(ctx, s, types.UserFilter{Query: prefill[0].Handle})
req.NoError(err)
req.Len(set, 1)
})
t.Run("by username", func(t *testing.T) {
req, _ := truncAndFill(t, 5)
set, _, err := store.SearchUsers(ctx, s, types.UserFilter{Username: "no such username"})
req.NoError(err)
req.Len(set, 0)
})
t.Run("with check", func(t *testing.T) {
req, prefill := truncAndFill(t, 5)
set, _, err := store.SearchUsers(ctx, s, types.UserFilter{
Check: func(user *types.User) (bool, error) {
// simple check that matches with the first user from prefill
return user.ID == prefill[0].ID, nil
},
})
req.NoError(err)
req.Len(set, 1)
req.Equal(prefill[0].ID, set[0].ID)
})
})
t.Run("paging and sorting", func(t *testing.T) {
require.NoError(t, s.TruncateUsers(ctx))
require.NoError(t, s.CreateUser(ctx,
makeNew("01"),
makeNew("02"),
makeNew("03"),
makeNew("04"),
makeNew("05"),
makeNew("06"),
makeNew("07"),
makeNew("08"),
makeNew("09"),
makeNew("10"),
))
{
tcc := []struct {
sort string
rval string
}{
{"email", "01..10"},
{"email DESC", "10..01"},
{"id, email", "01..10"},
{"id DESC, email DESC", "10..01"},
{"id DESC, email", "10..01"},
{"id, email DESC", "01..10"},
}
for _, tc := range tcc {
t.Run(tc.sort, func(t *testing.T) {
var (
req = require.New(t)
f = types.UserFilter{}
set types.UserSet
err error
)
f.Sort.Set(tc.sort)
set, f, err = store.SearchUsers(ctx, s, f)
req.NoError(err)
req.Equal(tc.rval, stringifySetRange(set))
req.Nil(f.PrevPage)
req.Nil(f.NextPage)
})
}
}
{
prevCur := 1
nextCur := 2
bothCur := prevCur | nextCur
// tests if cursors are properly set/unset by inspecting req. bits
testCursors := func(req *require.Assertions, b int, f types.UserFilter) {
if b&prevCur == 0 {
req.Nil(f.PrevPage)
} else {
req.NotNil(f.PrevPage)
}
if b&nextCur == 0 {
req.Nil(f.NextPage)
} else {
req.NotNil(f.NextPage)
}
}
tcc := []struct {
// how data is sorted
sort string
// expected data
rval []string
// how cursors should be set when moving forward/backward
curr []int
}{
{
"id",
[]string{"01..03", "04..06", "07..09", "10"},
[]int{nextCur, bothCur, bothCur, prevCur},
},
{
"id DESC",
[]string{"10..08", "07..05", "04..02", "01"},
[]int{nextCur, bothCur, bothCur, prevCur},
},
{
"email",
[]string{"01..03", "04..06", "07..09", "10"},
[]int{nextCur, bothCur, bothCur, prevCur},
},
{
"email DESC",
[]string{"10..08", "07..05", "04..02", "01"},
[]int{nextCur, bothCur, bothCur, prevCur},
},
{
"id DESC, email",
[]string{"10..08", "07..05", "04..02", "01"},
[]int{nextCur, bothCur, bothCur, prevCur},
},
}
for _, tc := range tcc {
t.Run("crawling: "+tc.sort+"x", func(t *testing.T) {
var (
req = require.New(t)
f = types.UserFilter{}
set types.UserSet
err error
)
f.Sort.Set(tc.sort)
f.Limit = 3 // 3, 3, 3, 1
t.Log("going from page 1 to 4")
for p := 0; p < 4; p++ {
t.Logf("0123 next page cursor: %35s", f.PageCursor)
set, f, err = store.SearchUsers(ctx, s, f)
req.NoError(err)
req.True(tc.sort == f.Sort.String() || strings.HasPrefix(f.Sort.String(), tc.sort+","))
req.Equal(tc.rval[p], stringifySetRange(set))
testCursors(req, tc.curr[p], f)
// advance to next page
f.PageCursor = f.NextPage
}
t.Log("and back from 3 to 1")
f.PageCursor = f.PrevPage
for p := 2; p >= 0; p-- {
f.Sort = nil
t.Logf("_210 next page cursor: %35s", f.PageCursor)
set, f, err = store.SearchUsers(ctx, s, f)
t.Log(stringifySetRange(set))
req.NoError(err)
//req.True(tc.sort == f.Sort.String() || strings.HasPrefix(f.Sort.String(), tc.sort+","), "search should not return altered sort on filter")
req.Equal(tc.rval[p], stringifySetRange(set))
testCursors(req, tc.curr[p], f)
// reverse to previous page
f.PageCursor = f.PrevPage
}
t.Log("and again all the way to 4th page")
f.PageCursor = f.NextPage
for p := 1; p < 4; p++ {
set, f, err = store.SearchUsers(ctx, s, f)
req.NoError(err)
req.True(tc.sort == f.Sort.String() || strings.HasPrefix(f.Sort.String(), tc.sort+","))
req.Equal(tc.rval[p], stringifySetRange(set))
testCursors(req, tc.curr[p], f)
// advance to next page
f.PageCursor = f.NextPage
}
})
}
}
t.Run("with incompatible sort", func(t *testing.T) {
req := require.New(t)
req.NoError(s.TruncateUsers(ctx))
set := []*types.User{
makeNew("01"),
makeNew("02"),
makeNew("03"),
makeNew("04"),
makeNew("05"),
}
req.NoError(s.CreateUser(ctx, set...))
f := types.UserFilter{}
f.Sort = filter.SortExprSet{&filter.SortExpr{Column: "email", Descending: true}, &filter.SortExpr{Column: "handle", Descending: true}}
f.Limit = uint(len(set))
set, f, err := store.SearchUsers(ctx, s, f)
req.Len(set, int(f.Limit))
req.NoError(err)
f.Limit = 1
set, f, err = store.SearchUsers(ctx, s, f)
req.NoError(err)
// go to next page with different sorting
f.PageCursor = f.NextPage
f.Sort = filter.SortExprSet{&filter.SortExpr{Column: "username", Descending: false}}
set, f, err = store.SearchUsers(ctx, s, f)
req.EqualError(err, filter.ErrIncompatibleSort.Error())
})
})
t.Run("count", func(t *testing.T) {
var (
req = require.New(t)
f = types.UserFilter{}
c1, c2 uint
err error
user = &types.User{ID: id.Next(), CreatedAt: time.Now(), Email: fmt.Sprintf("user-crud+%s@crust.test", time.Now().String())}
)
c1, err = store.CountUsers(ctx, s, f)
req.NoError(err)
req.NoError(s.CreateUser(ctx, user))
c2, err = store.CountUsers(ctx, s, f)
req.NoError(err)
req.Equal(c1+1, c2)
req.NoError(s.DeleteUserByID(ctx, user.ID))
c2, err = store.CountUsers(ctx, s, f)
req.NoError(err)
req.Equal(c1, c2)
})
t.Run("metrics", func(t *testing.T) {
var (
req = require.New(t)
oct, _ = time.Parse(time.RFC3339, "2020-10-02T10:01:10Z")
nov, _ = time.Parse(time.RFC3339, "2020-11-02T20:02:10Z")
octu = uint(oct.Truncate(time.Hour * 24).Unix())
novu = uint(nov.Truncate(time.Hour * 24).Unix())
e = &types.UserMetrics{
Total: 8,
Valid: 2,
Deleted: 3,
Suspended: 3,
DailyCreated: []uint{octu, 7, novu, 1},
DailyUpdated: []uint{octu, 2},
DailySuspended: []uint{octu, 2, novu, 1},
DailyDeleted: []uint{novu, 3},
}
)
req.NoError(s.TruncateUsers(ctx))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-1@crust.test", UpdatedAt: &oct}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-2@crust.test", UpdatedAt: &oct}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-3@crust.test", SuspendedAt: &oct}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-4@crust.test", SuspendedAt: &oct}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-5@crust.test", SuspendedAt: &nov}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-6@crust.test", DeletedAt: &nov}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: oct, Email: "user-metrics-7@crust.test", DeletedAt: &nov}))
req.NoError(s.CreateUser(ctx, &types.User{ID: id.Next(), CreatedAt: nov, Email: "user-metrics-8@crust.test", DeletedAt: &nov}))
m, err := store.UserMetrics(ctx, s)
req.NoError(err)
req.Equal(e, m)
})
}