Unverified Commit 3848cb7e authored by JoramWilander's avatar JoramWilander

Merge branch 'master' into plugins-2

parents 88c5e469 2362a5b7
......@@ -154,7 +154,11 @@ func getUserByUsername(c *Context, w http.ResponseWriter, r *http.Request) {
if c.HandleEtag(etag, "Get User", w, r) {
return
} else {
c.App.SanitizeProfile(user, c.IsSystemAdmin())
if c.Session.UserId == user.Id {
user.Sanitize(map[string]bool{})
} else {
c.App.SanitizeProfile(user, c.IsSystemAdmin())
}
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
w.Write([]byte(user.ToJson()))
return
......
......@@ -411,7 +411,7 @@ func TestGetUserByUsername(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowEmailAddress = false })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowFullName = false })
ruser, resp = Client.GetUserByUsername(user.Username, "")
ruser, resp = Client.GetUserByUsername(th.BasicUser2.Username, "")
CheckNoError(t, resp)
if ruser.Email != "" {
......@@ -424,6 +424,12 @@ func TestGetUserByUsername(t *testing.T) {
t.Fatal("last name should be blank")
}
ruser, resp = Client.GetUserByUsername(th.BasicUser.Username, "")
CheckNoError(t, resp)
if len(ruser.NotifyProps) == 0 {
t.Fatal("notify props should be sent")
}
Client.Logout()
_, resp = Client.GetUserByUsername(user.Username, "")
CheckUnauthorizedStatus(t, resp)
......
......@@ -297,13 +297,14 @@ func (a *App) trackConfig() {
})
a.SendDiagnostic(TRACK_CONFIG_SQL, map[string]interface{}{
"driver_name": *cfg.SqlSettings.DriverName,
"trace": cfg.SqlSettings.Trace,
"max_idle_conns": *cfg.SqlSettings.MaxIdleConns,
"max_open_conns": *cfg.SqlSettings.MaxOpenConns,
"data_source_replicas": len(cfg.SqlSettings.DataSourceReplicas),
"data_source_search_replicas": len(cfg.SqlSettings.DataSourceSearchReplicas),
"query_timeout": *cfg.SqlSettings.QueryTimeout,
"driver_name": *cfg.SqlSettings.DriverName,
"trace": cfg.SqlSettings.Trace,
"max_idle_conns": *cfg.SqlSettings.MaxIdleConns,
"conn_max_lifetime_milliseconds": *cfg.SqlSettings.ConnMaxLifetimeMilliseconds,
"max_open_conns": *cfg.SqlSettings.MaxOpenConns,
"data_source_replicas": len(cfg.SqlSettings.DataSourceReplicas),
"data_source_search_replicas": len(cfg.SqlSettings.DataSourceSearchReplicas),
"query_timeout": *cfg.SqlSettings.QueryTimeout,
})
a.SendDiagnostic(TRACK_CONFIG_LOG, map[string]interface{}{
......
......@@ -1062,10 +1062,24 @@ func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *mod
}
var roles string
isSchemeUser := true
isSchemeAdmin := false
if tdata.Roles == nil {
roles = model.TEAM_USER_ROLE_ID
isSchemeUser = true
} else {
roles = *tdata.Roles
rawRoles := *tdata.Roles
explicitRoles := []string{}
for _, role := range strings.Fields(rawRoles) {
if role == model.TEAM_USER_ROLE_ID {
isSchemeUser = true
} else if role == model.TEAM_ADMIN_ROLE_ID {
isSchemeAdmin = true
} else {
explicitRoles = append(explicitRoles, role)
}
}
roles = strings.Join(explicitRoles, " ")
}
var member *model.TeamMember
......@@ -1073,12 +1087,16 @@ func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *mod
return err
}
if member.Roles != roles {
if member.ExplicitRoles != roles {
if _, err := a.UpdateTeamMemberRoles(team.Id, user.Id, roles); err != nil {
return err
}
}
if member.SchemeAdmin != isSchemeAdmin || member.SchemeUser != isSchemeUser {
a.UpdateTeamMemberSchemeRoles(team.Id, user.Id, isSchemeUser, isSchemeAdmin)
}
if defaultChannel, err := a.GetChannelByName(model.DEFAULT_CHANNEL, team.Id); err != nil {
return err
} else if _, err = a.addUserToChannel(user, defaultChannel, member); err != nil {
......@@ -1108,10 +1126,24 @@ func (a *App) ImportUserChannels(user *model.User, team *model.Team, teamMember
}
var roles string
isSchemeUser := true
isSchemeAdmin := false
if cdata.Roles == nil {
roles = model.CHANNEL_USER_ROLE_ID
isSchemeUser = true
} else {
roles = *cdata.Roles
rawRoles := *cdata.Roles
explicitRoles := []string{}
for _, role := range strings.Fields(rawRoles) {
if role == model.CHANNEL_USER_ROLE_ID {
isSchemeUser = true
} else if role == model.CHANNEL_ADMIN_ROLE_ID {
isSchemeAdmin = true
} else {
explicitRoles = append(explicitRoles, role)
}
}
roles = strings.Join(explicitRoles, " ")
}
var member *model.ChannelMember
......@@ -1123,12 +1155,16 @@ func (a *App) ImportUserChannels(user *model.User, team *model.Team, teamMember
}
}
if member.Roles != roles {
if member.ExplicitRoles != roles {
if _, err := a.UpdateChannelMemberRoles(channel.Id, user.Id, roles); err != nil {
return err
}
}
if member.SchemeAdmin != isSchemeAdmin || member.SchemeUser != isSchemeUser {
a.UpdateChannelMemberSchemeRoles(channel.Id, user.Id, isSchemeUser, isSchemeAdmin)
}
if cdata.NotifyProps != nil {
notifyProps := member.NotifyProps
......@@ -1200,7 +1236,7 @@ func validateUserImportData(data *UserImportData) *model.AppError {
}
if data.Password != nil && len(*data.Password) == 0 {
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.pasword_length.error", nil, "", http.StatusBadRequest)
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.password_length.error", nil, "", http.StatusBadRequest)
}
if data.Password != nil && len(*data.Password) > model.USER_PASSWORD_MAX_LENGTH {
......
......@@ -2598,6 +2598,126 @@ func TestImportImportUser(t *testing.T) {
checkNotifyProp(t, user, model.CHANNEL_MENTIONS_NOTIFY_PROP, "false")
checkNotifyProp(t, user, model.COMMENTS_NOTIFY_PROP, model.COMMENTS_NOTIFY_ANY)
checkNotifyProp(t, user, model.MENTION_KEYS_NOTIFY_PROP, "misc")
// Test importing a user with roles set to a team and a channel which are affected by an override scheme.
// The import subsystem should translate `channel_admin/channel_user/team_admin/team_user`
// to the appropriate scheme-managed-role booleans.
// Mark the phase 2 permissions migration as completed.
<-th.App.Srv.Store.System().Save(&model.System{Name: model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, Value: "true"})
defer func() {
<-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2)
}()
teamSchemeData := &SchemeImportData{
Name: ptrStr(model.NewId()),
DisplayName: ptrStr(model.NewId()),
Scope: ptrStr("team"),
DefaultTeamUserRole: &RoleImportData{
Name: ptrStr(model.NewId()),
DisplayName: ptrStr(model.NewId()),
},
DefaultTeamAdminRole: &RoleImportData{
Name: ptrStr(model.NewId()),
DisplayName: ptrStr(model.NewId()),
},
DefaultChannelUserRole: &RoleImportData{
Name: ptrStr(model.NewId()),
DisplayName: ptrStr(model.NewId()),
},
DefaultChannelAdminRole: &RoleImportData{
Name: ptrStr(model.NewId()),
DisplayName: ptrStr(model.NewId()),
},
Description: ptrStr("description"),
}
if err := th.App.ImportScheme(teamSchemeData, false); err != nil {
t.Fatalf("Should have succeeded.")
}
var teamScheme *model.Scheme
if res := <-th.App.Srv.Store.Scheme().GetByName(*teamSchemeData.Name); res.Err != nil {
t.Fatalf("Failed to import scheme: %v", res.Err)
} else {
teamScheme = res.Data.(*model.Scheme)
}
teamData := &TeamImportData{
Name: ptrStr(model.NewId()),
DisplayName: ptrStr("Display Name"),
Type: ptrStr("O"),
Description: ptrStr("The team description."),
AllowOpenInvite: ptrBool(true),
Scheme: &teamScheme.Name,
}
if err := th.App.ImportTeam(teamData, false); err != nil {
t.Fatalf("Import should have succeeded: %v", err.Error())
}
team, err = th.App.GetTeamByName(teamName)
if err != nil {
t.Fatalf("Failed to get team from database.")
}
channelData := &ChannelImportData{
Team: &teamName,
Name: ptrStr(model.NewId()),
DisplayName: ptrStr("Display Name"),
Type: ptrStr("O"),
Header: ptrStr("Channe Header"),
Purpose: ptrStr("Channel Purpose"),
}
if err := th.App.ImportChannel(channelData, false); err != nil {
t.Fatalf("Import should have succeeded.")
}
channel, err = th.App.GetChannelByName(*channelData.Name, team.Id)
if err != nil {
t.Fatalf("Failed to get channel from database: %v", err.Error())
}
// Test with a valid team & valid channel name in apply mode.
userData := &UserImportData{
Username: &username,
Email: ptrStr(model.NewId() + "@example.com"),
Teams: &[]UserTeamImportData{
{
Name: &team.Name,
Roles: ptrStr("team_user team_admin"),
Channels: &[]UserChannelImportData{
{
Name: &channel.Name,
Roles: ptrStr("channel_admin channel_user"),
},
},
},
},
}
if err := th.App.ImportUser(userData, false); err != nil {
t.Fatalf("Should have succeeded.")
}
user, err = th.App.GetUserByUsername(*userData.Username)
if err != nil {
t.Fatalf("Failed to get user from database.")
}
teamMember, err := th.App.GetTeamMember(team.Id, user.Id)
if err != nil {
t.Fatalf("Failed to get the team member")
}
assert.True(t, teamMember.SchemeAdmin)
assert.True(t, teamMember.SchemeUser)
assert.Equal(t, "", teamMember.ExplicitRoles)
channelMember, err := th.App.GetChannelMember(channel.Id, user.Id)
if err != nil {
t.Fatalf("Failed to get the channel member")
}
assert.True(t, channelMember.SchemeAdmin)
assert.True(t, channelMember.SchemeUser)
assert.Equal(t, "", channelMember.ExplicitRoles)
}
func AssertAllPostsCount(t *testing.T, a *App, initialCount int64, change int64, teamName string) {
......
......@@ -92,15 +92,23 @@ func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second
func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
if r.Host == "" {
http.Error(w, "Not Found", http.StatusNotFound)
// golang.org/x/crypto/acme/autocert/autocert.go
func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
target := "https://" + stripPort(r.Host) + r.URL.RequestURI()
http.Redirect(w, r, target, http.StatusFound)
}
url := r.URL
url.Host = r.Host
url.Scheme = "https"
http.Redirect(w, r, url.String(), http.StatusFound)
// golang.org/x/crypto/acme/autocert/autocert.go
func stripPort(hostport string) string {
host, _, err := net.SplitHostPort(hostport)
if err != nil {
return hostport
}
return net.JoinHostPort(host, "443")
}
func (a *App) StartServer() error {
......@@ -182,7 +190,7 @@ func (a *App) StartServer() error {
defer redirectListener.Close()
server := &http.Server{
Handler: handler,
Handler: http.HandlerFunc(handleHTTPRedirect),
ErrorLog: a.Log.StdLog(mlog.String("source", "forwarder_server")),
}
server.Serve(redirectListener)
......
......@@ -227,6 +227,9 @@ func (a *App) AttachDeviceId(sessionId string, deviceId string, expiresAt int64)
func (a *App) UpdateLastActivityAtIfNeeded(session model.Session) {
now := model.GetMillis()
a.UpdateWebConnUserActivity(session, now)
if now-session.LastActivityAt < model.SESSION_ACTIVITY_TIMEOUT {
return
}
......
......@@ -161,6 +161,22 @@ func (a *App) GetUserStatusesByIds(userIds []string) ([]*model.Status, *model.Ap
return statusMap, nil
}
// SetStatusLastActivityAt sets the last activity at for a user on the local app server and updates
// status to away if needed. Used by the WS to set status to away if an 'online' device disconnects
// while an 'away' device is still connected
func (a *App) SetStatusLastActivityAt(userId string, activityAt int64) {
var status *model.Status
var err *model.AppError
if status, err = a.GetStatus(userId); err != nil {
return
}
status.LastActivityAt = activityAt
a.AddStatusCacheSkipClusterSend(status)
a.SetStatusAwayIfNeeded(userId, false)
}
func (a *App) SetStatusOnline(userId string, sessionId string, manual bool) {
if !*a.Config().ServiceSettings.EnableUserStatuses {
return
......
......@@ -33,6 +33,7 @@ type WebConn struct {
Send chan model.WebSocketMessage
sessionToken atomic.Value
session atomic.Value
LastUserActivityAt int64
UserId string
T goi18n.TranslateFunc
Locale string
......@@ -52,14 +53,15 @@ func (a *App) NewWebConn(ws *websocket.Conn, session model.Session, t goi18n.Tra
}
wc := &WebConn{
App: a,
Send: make(chan model.WebSocketMessage, SEND_QUEUE_SIZE),
WebSocket: ws,
UserId: session.UserId,
T: t,
Locale: locale,
endWritePump: make(chan struct{}, 2),
pumpFinished: make(chan struct{}, 1),
App: a,
Send: make(chan model.WebSocketMessage, SEND_QUEUE_SIZE),
WebSocket: ws,
LastUserActivityAt: model.GetMillis(),
UserId: session.UserId,
T: t,
Locale: locale,
endWritePump: make(chan struct{}, 2),
pumpFinished: make(chan struct{}, 1),
}
wc.SetSession(&session)
......
......@@ -23,6 +23,12 @@ const (
DEADLOCK_WARN = (BROADCAST_QUEUE_SIZE * 99) / 100 // number of buffered messages before printing stack trace
)
type WebConnActivityMessage struct {
UserId string
SessionToken string
ActivityAt int64
}
type Hub struct {
// connectionCount should be kept first.
// See https://github.com/mattermost/mattermost-server/pull/7281
......@@ -35,6 +41,7 @@ type Hub struct {
stop chan struct{}
didStop chan struct{}
invalidateUser chan string
activity chan *WebConnActivityMessage
ExplicitStop bool
goroutineId int
}
......@@ -48,6 +55,7 @@ func (a *App) NewWebHub() *Hub {
stop: make(chan struct{}),
didStop: make(chan struct{}),
invalidateUser: make(chan string),
activity: make(chan *WebConnActivityMessage),
ExplicitStop: false,
}
}
......@@ -330,6 +338,13 @@ func (a *App) InvalidateWebConnSessionCacheForUser(userId string) {
}
}
func (a *App) UpdateWebConnUserActivity(session model.Session, activityAt int64) {
hub := a.GetHubForUserId(session.UserId)
if hub != nil {
hub.UpdateActivity(session.UserId, session.Token, activityAt)
}
}
func (h *Hub) Register(webConn *WebConn) {
h.register <- webConn
......@@ -355,6 +370,10 @@ func (h *Hub) InvalidateUser(userId string) {
h.invalidateUser <- userId
}
func (h *Hub) UpdateActivity(userId, sessionToken string, activityAt int64) {
h.activity <- &WebConnActivityMessage{UserId: userId, SessionToken: sessionToken, ActivityAt: activityAt}
}
func getGoroutineId() int {
var buf [64]byte
n := runtime.Stack(buf[:], false)
......@@ -395,15 +414,34 @@ func (h *Hub) Start() {
continue
}
if len(connections.ForUser(webCon.UserId)) == 0 {
conns := connections.ForUser(webCon.UserId)
if len(conns) == 0 {
h.app.Go(func() {
h.app.SetStatusOffline(webCon.UserId, false)
})
} else {
var latestActivity int64 = 0
for _, conn := range conns {
if conn.LastUserActivityAt > latestActivity {
latestActivity = conn.LastUserActivityAt
}
}
if h.app.IsUserAway(latestActivity) {
h.app.Go(func() {
h.app.SetStatusLastActivityAt(webCon.UserId, latestActivity)
})
}
}
case userId := <-h.invalidateUser:
for _, webCon := range connections.ForUser(userId) {
webCon.InvalidateCache()
}
case activity := <-h.activity:
for _, webCon := range connections.ForUser(activity.UserId) {
if webCon.GetSessionToken() == activity.SessionToken {
webCon.LastUserActivityAt = activity.ActivityAt
}
}
case msg := <-h.broadcast:
candidates := connections.All()
if msg.Broadcast.UserId != "" {
......
......@@ -3,27 +3,15 @@ dist: | check-style test package
build-linux:
@echo Build Linux amd64
ifeq ($(BUILDER_GOOS_GOARCH),"linux_amd64")
env GOOS=linux GOARCH=amd64 $(GO) install -i $(GOFLAGS) $(GO_LINKER_FLAGS) ./...
else
env GOOS=linux GOARCH=amd64 $(GO) install -i $(GOFLAGS) $(GO_LINKER_FLAGS) ./...
endif
build-osx:
@echo Build OSX amd64
ifeq ($(BUILDER_GOOS_GOARCH),"darwin_amd64")
env GOOS=darwin GOARCH=amd64 $(GO) install -i $(GOFLAGS) $(GO_LINKER_FLAGS) ./...
else
env GOOS=darwin GOARCH=amd64 $(GO) install -i $(GOFLAGS) $(GO_LINKER_FLAGS) ./...
endif
build-windows:
@echo Build Windows amd64
ifeq ($(BUILDER_GOOS_GOARCH),"windows_amd64")
env GOOS=windows GOARCH=amd64 $(GO) install -i $(GOFLAGS) $(GO_LINKER_FLAGS) ./...
else
env GOOS=windows GOARCH=amd64 $(GO) install -i $(GOFLAGS) $(GO_LINKER_FLAGS) ./...
endif
build: build-linux build-windows build-osx
......
......@@ -119,6 +119,7 @@
"DataSourceReplicas": [],
"DataSourceSearchReplicas": [],
"MaxIdleConns": 20,
"ConnMaxLifetimeMilliseconds": 3600000,
"MaxOpenConns": 300,
"Trace": false,
"AtRestEncryptKey": "",
......
This diff is collapsed.
......@@ -59,7 +59,7 @@ func (scheduler *Scheduler) ScheduleJob(cfg *model.Config, pendingJobs bool, las
if state == MIGRATION_STATE_IN_PROGRESS {
// Check the migration job isn't wedged.
if job != nil && job.LastActivityAt < model.GetMillis()-MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS {
if job != nil && job.LastActivityAt < model.GetMillis()-MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS && job.CreateAt < model.GetMillis()-MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS {
mlog.Warn("Job appears to be wedged. Rescheduling another instance.", mlog.String("scheduler", scheduler.Name()), mlog.String("wedged_job_id", job.Id), mlog.String("migration_key", key))
if err := scheduler.App.Jobs.SetJobError(job, nil); err != nil {
mlog.Error("Worker: Failed to set job error", mlog.String("scheduler", scheduler.Name()), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
......
......@@ -605,15 +605,16 @@ type SSOSettings struct {
}
type SqlSettings struct {
DriverName *string
DataSource *string
DataSourceReplicas []string
DataSourceSearchReplicas []string
MaxIdleConns *int
MaxOpenConns *int
Trace bool
AtRestEncryptKey string
QueryTimeout *int
DriverName *string
DataSource *string
DataSourceReplicas []string
DataSourceSearchReplicas []string
MaxIdleConns *int
ConnMaxLifetimeMilliseconds *int
MaxOpenConns *int
Trace bool
AtRestEncryptKey string
QueryTimeout *int
}
func (s *SqlSettings) SetDefaults() {
......@@ -637,6 +638,10 @@ func (s *SqlSettings) SetDefaults() {
s.MaxOpenConns = NewInt(300)
}
if s.ConnMaxLifetimeMilliseconds == nil {
s.ConnMaxLifetimeMilliseconds = NewInt(3600000)
}
if s.QueryTimeout == nil {
s.QueryTimeout = NewInt(30)
}
......@@ -2069,6 +2074,10 @@ func (ss *SqlSettings) isValid() *AppError {
return NewAppError("Config.IsValid", "model.config.is_valid.sql_idle.app_error", nil, "", http.StatusBadRequest)
}
if *ss.ConnMaxLifetimeMilliseconds < 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.sql_conn_max_lifetime_milliseconds.app_error", nil, "", http.StatusBadRequest)
}
if *ss.QueryTimeout <= 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.sql_query_timeout.app_error", nil, "", http.StatusBadRequest)
}
......
......@@ -1617,6 +1617,8 @@ func (s SqlChannelStore) buildFulltextClause(term string) (fulltextClause, fullt
// Prepare the FULLTEXT portion of the query.
if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
fulltextTerm = strings.Replace(fulltextTerm, "|", "", -1)
splitTerm := strings.Fields(fulltextTerm)
for i, t := range strings.Fields(fulltextTerm) {
if i == len(splitTerm)-1 {
......
......@@ -28,7 +28,6 @@ import (
const (
INDEX_TYPE_FULL_TEXT = "full_text"
INDEX_TYPE_DEFAULT = "default"
MAX_DB_CONN_LIFETIME = 60
DB_PING_ATTEMPTS = 18
DB_PING_TIMEOUT_SECS = 10
)
......@@ -218,7 +217,7 @@ func setupConnection(con_type string, dataSource string, settings *model.SqlSett
db.SetMaxIdleConns(*settings.MaxIdleConns)
db.SetMaxOpenConns(*settings.MaxOpenConns)
db.SetConnMaxLifetime(time.Duration(MAX_DB_CONN_LIFETIME) * time.Minute)
db.SetConnMaxLifetime(time.Duration(*settings.ConnMaxLifetimeMilliseconds) * time.Millisecond)
var dbmap *gorp.DbMap
......
......@@ -73,17 +73,19 @@ func TestGetReplica(t *testing.T) {
driverName := model.DATABASE_DRIVER_SQLITE
dataSource := ":memory:"
maxIdleConns := 1
connMaxLifetimeMilliseconds := 3600000
maxOpenConns := 1
queryTimeout := 5
settings := model.SqlSettings{
DriverName: &driverName,
DataSource: &dataSource,
MaxIdleConns: &maxIdleConns,
MaxOpenConns: &maxOpenConns,
QueryTimeout: &queryTimeout,
DataSourceReplicas: testCase.DataSourceReplicas,
DataSourceSearchReplicas: testCase.DataSourceSearchReplicas,
DriverName: &driverName,
DataSource: &dataSource,
MaxIdleConns: &maxIdleConns,
ConnMaxLifetimeMilliseconds: &connMaxLifetimeMilliseconds,
MaxOpenConns: &maxOpenConns,
QueryTimeout: &queryTimeout,
DataSourceReplicas: testCase.DataSourceReplicas,
DataSourceSearchReplicas: testCase.DataSourceSearchReplicas,
}
supplier := sqlstore.NewSqlSupplier(settings, nil)
......@@ -209,17 +211,19 @@ func TestGetAllConns(t *testing.T) {
driverName := model.DATABASE_DRIVER_SQLITE
dataSource := ":memory:"
maxIdleConns := 1
connMaxLifetimeMilliseconds := 3600000
maxOpenConns := 1