189 lines
5.5 KiB
Go
189 lines
5.5 KiB
Go
//go:build mysql
|
|
|
|
package mysql
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/tinode/chat/server/store/types"
|
|
)
|
|
|
|
func TestCanUseManagedReportsRequiresAllOrganizationVisibility(t *testing.T) {
|
|
if !canUseManagedReports(organizationVisibility{Scope: orgVisibilityAll}) {
|
|
t.Fatal("all visibility should be allowed to use managed report scope")
|
|
}
|
|
for _, scope := range []string{orgVisibilitySelf, orgVisibilityFriends, ""} {
|
|
if canUseManagedReports(organizationVisibility{Scope: scope}) {
|
|
t.Fatalf("scope %q should not be allowed to use managed report scope", scope)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReportMemberScopeWhereIncludesDepartmentAndVisibility(t *testing.T) {
|
|
where, args := reportMemberScopeWhere(organizationVisibility{Scope: orgVisibilityFriends, UserID: 42}, "dep_ops")
|
|
joined := strings.Join(where, " AND ")
|
|
if !strings.Contains(joined, "m.department_id=?") {
|
|
t.Fatalf("department filter missing from where: %s", joined)
|
|
}
|
|
if !strings.Contains(joined, "im_friendships") {
|
|
t.Fatalf("friend visibility filter missing from where: %s", joined)
|
|
}
|
|
if len(args) != 4 || args[0] != "dep_ops" || args[1] != int64(42) || args[2] != int64(42) || args[3] != types.RelationshipFriend {
|
|
t.Fatalf("unexpected args: %#v", args)
|
|
}
|
|
}
|
|
|
|
func TestGroupAdminAccessModeDoesNotGrantOwner(t *testing.T) {
|
|
mode := groupAdminAccessMode()
|
|
if !mode.IsAdmin() {
|
|
t.Fatalf("admin mode should grant admin access: %s", mode.String())
|
|
}
|
|
if mode.IsOwner() {
|
|
t.Fatalf("admin mode must not grant owner access: %s", mode.String())
|
|
}
|
|
}
|
|
|
|
func TestMessageSearchDoesNotClearScannedHead(t *testing.T) {
|
|
source, err := os.ReadFile("tenant_adapter.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(source), "msg.Head = kvMapFromJSON(msg.Head)") {
|
|
t.Fatal("message search must not clear Head after sqlx StructScan has already decoded it")
|
|
}
|
|
}
|
|
|
|
func TestRuntimeOpenDoesNotAutoCreateOrMigrateTables(t *testing.T) {
|
|
source, err := os.ReadFile("adapter.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := string(source)
|
|
for _, tc := range []struct {
|
|
name string
|
|
function string
|
|
end string
|
|
}{
|
|
{
|
|
name: "Open",
|
|
function: "func (a *adapter) Open",
|
|
end: "\nfunc (a *adapter) Close",
|
|
},
|
|
{
|
|
name: "TenantAdapterFor",
|
|
function: "func (a *adapter) TenantAdapterFor",
|
|
end: "\nfunc (a *adapter) tenantByID",
|
|
},
|
|
} {
|
|
fn := sourceBetween(t, body, tc.function, tc.end)
|
|
for _, forbidden := range []string{
|
|
"ensureIMRegistrationInviteControlTables",
|
|
"ensureIMTenantEntryControlTables",
|
|
"ensureIMGroupBusinessTables",
|
|
"ensureIMHTTPInfrastructureTables",
|
|
"ensureIMAdminGovernanceTables",
|
|
"ensureIMFileReviewTables",
|
|
"ensureIMOrganizationVisibilityTables",
|
|
"ensureIMAttendanceTables",
|
|
"ensureIMMeetingNotesTables",
|
|
"ensureSnowflakePrimaryKeys",
|
|
"ensureUserIdentitySchema",
|
|
"ensureIMRegistrationInviteUseTable",
|
|
"CREATE DATABASE",
|
|
"CREATE TABLE",
|
|
"ALTER TABLE",
|
|
} {
|
|
if strings.Contains(fn, forbidden) {
|
|
t.Fatalf("%s must not auto-create or auto-migrate schema; found %q", tc.name, forbidden)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func sourceBetween(t *testing.T, source, startMarker, endMarker string) string {
|
|
t.Helper()
|
|
start := strings.Index(source, startMarker)
|
|
if start < 0 {
|
|
t.Fatalf("missing source marker %q", startMarker)
|
|
}
|
|
end := strings.Index(source[start:], endMarker)
|
|
if end < 0 {
|
|
t.Fatalf("missing source marker %q after %q", endMarker, startMarker)
|
|
}
|
|
return source[start : start+end]
|
|
}
|
|
|
|
func TestSearchPaginationCursorUsesLastReturnedRow(t *testing.T) {
|
|
source, err := os.ReadFile("tenant_adapter.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := string(source)
|
|
for _, tc := range []struct {
|
|
name string
|
|
function string
|
|
forbidden string
|
|
required string
|
|
}{
|
|
{
|
|
name: "p2p message search",
|
|
function: "func (a *adapter) TenantMessageSearch",
|
|
forbidden: "nextCursor = strconv.FormatInt(msg.SeqId, 10)",
|
|
required: "nextCursor = strconv.FormatInt(lastSeq, 10)",
|
|
},
|
|
{
|
|
name: "group message search",
|
|
function: "func (a *adapter) tenantGroupMessageSearch",
|
|
forbidden: "nextCursor = strconv.FormatInt(msg.SeqId, 10)",
|
|
required: "nextCursor = strconv.FormatInt(lastSeq, 10)",
|
|
},
|
|
{
|
|
name: "favorite list",
|
|
function: "func (a *adapter) TenantFavoriteList",
|
|
forbidden: "nextCursor = strconv.FormatInt(item.ID, 10)",
|
|
required: "nextCursor = strconv.FormatInt(lastID, 10)",
|
|
},
|
|
} {
|
|
fn := functionSource(t, body, tc.function)
|
|
if strings.Contains(fn, tc.forbidden) {
|
|
t.Fatalf("%s must not use the extra fetched row as next cursor", tc.name)
|
|
}
|
|
if !strings.Contains(fn, tc.required) {
|
|
t.Fatalf("%s must use the last returned row as next cursor", tc.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTenantFileAccessIncludesAvatarAudienceRules(t *testing.T) {
|
|
source, err := os.ReadFile("tenant_adapter.go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := string(source)
|
|
for _, want := range []string{
|
|
"INNER JOIN im_friendships AS fs",
|
|
"fs.user_id=? AND fs.peer_id=fml.userid AND fs.status=?",
|
|
"INNER JOIN im_group_members AS gm",
|
|
"gm.topic=fml.topic AND gm.user_id=? AND gm.member_status=?",
|
|
} {
|
|
if !strings.Contains(body, want) {
|
|
t.Fatalf("file ACL must include avatar audience rule %q", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func functionSource(t *testing.T, source, marker string) string {
|
|
t.Helper()
|
|
start := strings.Index(source, marker)
|
|
if start < 0 {
|
|
t.Fatalf("function %q not found", marker)
|
|
}
|
|
next := strings.Index(source[start+len(marker):], "\nfunc ")
|
|
if next < 0 {
|
|
return source[start:]
|
|
}
|
|
return source[start : start+len(marker)+next]
|
|
}
|