2019-02-15 09:05:29 -05:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2019-11-29 06:59:40 -05:00
// See LICENSE.txt for license information.
2019-02-15 09:05:29 -05:00
2021-05-19 07:30:26 -04:00
package config
2019-02-15 09:05:29 -05:00
import (
2020-10-29 18:54:39 -04:00
"encoding/json"
2022-06-14 05:28:15 -04:00
"errors"
2019-02-15 09:05:29 -05:00
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2023-06-11 01:24:35 -04:00
"github.com/mattermost/mattermost/server/public/model"
2019-02-15 09:05:29 -05:00
)
2020-02-06 09:15:18 -05:00
func getDsn ( driver string , source string ) string {
return source
}
2019-03-06 15:06:45 -05:00
func setupConfigDatabase ( t * testing . T , cfg * model . Config , files map [ string ] [ ] byte ) ( string , func ( ) ) {
2020-03-02 11:13:39 -05:00
if testing . Short ( ) {
t . SkipNow ( )
}
2019-02-15 09:05:29 -05:00
t . Helper ( )
os . Clearenv ( )
truncateTables ( t )
2021-05-19 07:30:26 -04:00
cfgData , err := marshalConfig ( cfg )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
2022-03-08 07:07:37 -05:00
ds := & DatabaseStore {
driverName : * mainHelper . GetSQLSettings ( ) . DriverName ,
2024-12-10 08:57:19 -05:00
db : mainHelper . GetSQLStore ( ) . GetMaster ( ) . DB ,
2022-03-08 07:07:37 -05:00
dataSourceName : * mainHelper . Settings . DataSource ,
}
err = ds . initializeConfigurationsTable ( )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
id := model . NewId ( )
2022-07-05 02:46:50 -04:00
_ , err = ds . db . NamedExec ( "INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)" , map [ string ] any {
2019-02-15 09:05:29 -05:00
"Id" : id ,
"Value" : cfgData ,
"CreateAt" : model . GetMillis ( ) ,
} )
require . NoError ( t , err )
2019-03-06 15:06:45 -05:00
for name , data := range files {
2022-07-05 02:46:50 -04:00
params := map [ string ] any {
2019-03-06 15:06:45 -05:00
"name" : name ,
"data" : data ,
"create_at" : model . GetMillis ( ) ,
"update_at" : model . GetMillis ( ) ,
}
2022-03-08 07:07:37 -05:00
_ , err = ds . db . NamedExec ( "INSERT INTO ConfigurationFiles (Name, Data, CreateAt, UpdateAt) VALUES (:name, :data, :create_at, :update_at)" , params )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
}
2019-02-15 09:05:29 -05:00
return id , func ( ) {
truncateTables ( t )
}
}
// getActualDatabaseConfig returns the active configuration in the database without relying on a config store.
2019-03-06 15:06:45 -05:00
func getActualDatabaseConfig ( t * testing . T ) ( string , * model . Config ) {
2019-02-15 09:05:29 -05:00
t . Helper ( )
2020-02-06 09:15:18 -05:00
if * mainHelper . GetSQLSettings ( ) . DriverName == "postgres" {
var actual struct {
ID string ` db:"id" `
Value [ ] byte ` db:"value" `
}
2024-12-10 08:57:19 -05:00
err := mainHelper . GetSQLStore ( ) . GetMaster ( ) . Get ( & actual , "SELECT Id, Value FROM Configurations WHERE Active" )
2020-02-06 09:15:18 -05:00
require . NoError ( t , err )
2020-10-29 18:54:39 -04:00
var actualCfg * model . Config
err = json . Unmarshal ( actual . Value , & actualCfg )
2021-04-12 06:51:31 -04:00
require . NoError ( t , err )
2020-02-06 09:15:18 -05:00
return actual . ID , actualCfg
}
2019-03-06 15:06:45 -05:00
var actual struct {
2020-01-07 11:00:56 -05:00
ID string ` db:"Id" `
2019-03-06 15:06:45 -05:00
Value [ ] byte ` db:"Value" `
}
2024-12-10 08:57:19 -05:00
err := mainHelper . GetSQLStore ( ) . GetMaster ( ) . Get ( & actual , "SELECT Id, Value FROM Configurations WHERE Active" )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
2020-10-29 18:54:39 -04:00
var actualCfg * model . Config
err = json . Unmarshal ( actual . Value , & actualCfg )
2021-04-12 06:51:31 -04:00
require . NoError ( t , err )
2020-01-07 11:00:56 -05:00
return actual . ID , actualCfg
2019-02-15 09:05:29 -05:00
}
// assertDatabaseEqualsConfig verifies the active in-database configuration equals the given config.
func assertDatabaseEqualsConfig ( t * testing . T , expectedCfg * model . Config ) {
t . Helper ( )
2019-03-06 15:06:45 -05:00
_ , actualCfg := getActualDatabaseConfig ( t )
2019-02-15 09:05:29 -05:00
assert . Equal ( t , expectedCfg , actualCfg )
}
// assertDatabaseNotEqualsConfig verifies the in-database configuration does not equal the given config.
func assertDatabaseNotEqualsConfig ( t * testing . T , expectedCfg * model . Config ) {
t . Helper ( )
2019-03-06 15:06:45 -05:00
_ , actualCfg := getActualDatabaseConfig ( t )
2019-02-15 09:05:29 -05:00
assert . NotEqual ( t , expectedCfg , actualCfg )
}
2021-05-19 07:30:26 -04:00
func newTestDatabaseStore ( customDefaults * model . Config ) ( * Store , error ) {
2020-10-29 18:54:39 -04:00
sqlSettings := mainHelper . GetSQLSettings ( )
2021-05-19 07:30:26 -04:00
dss , err := NewDatabaseStore ( getDsn ( * sqlSettings . DriverName , * sqlSettings . DataSource ) )
2021-02-09 12:52:27 -05:00
if err != nil {
return nil , err
}
2020-10-29 18:54:39 -04:00
2021-05-19 07:30:26 -04:00
cStore , err := NewStoreFromBacking ( dss , customDefaults , false )
2021-02-09 12:52:27 -05:00
if err != nil {
return nil , err
}
2020-10-29 18:54:39 -04:00
return cStore , nil
}
2019-02-15 09:05:29 -05:00
func TestDatabaseStoreNew ( t * testing . T ) {
2020-03-02 11:13:39 -05:00
if testing . Short ( ) {
t . SkipNow ( )
}
2020-01-20 06:32:20 -05:00
sqlSettings := mainHelper . GetSQLSettings ( )
2019-02-19 09:20:11 -05:00
2019-02-15 09:05:29 -05:00
t . Run ( "no existing configuration - initialization required" , func ( t * testing . T ) {
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2019-06-06 13:57:01 -04:00
assert . Equal ( t , "" , * ds . Get ( ) . ServiceSettings . SiteURL )
2019-02-15 09:05:29 -05:00
} )
2020-11-16 09:25:48 -05:00
t . Run ( "no existing configuration with custom defaults" , func ( t * testing . T ) {
truncateTables ( t )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( customConfigDefaults )
2020-11-16 09:25:48 -05:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , * customConfigDefaults . ServiceSettings . SiteURL , * ds . Get ( ) . ServiceSettings . SiteURL )
} )
2019-02-15 09:05:29 -05:00
t . Run ( "existing config, initialization required" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , "http://TestStoreNew" , * ds . Get ( ) . ServiceSettings . SiteURL )
assertDatabaseNotEqualsConfig ( t , testConfig )
} )
2020-11-16 09:25:48 -05:00
t . Run ( "existing config with custom defaults, initialization required" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( customConfigDefaults )
2020-11-16 09:25:48 -05:00
require . NoError ( t , err )
defer ds . Close ( )
// already existing value should not be overwritten by the
// custom default value
assert . Equal ( t , "http://TestStoreNew" , * ds . Get ( ) . ServiceSettings . SiteURL )
// not existing value should be overwritten by the custom
// default value
assertDatabaseNotEqualsConfig ( t , testConfig )
} )
2019-02-15 09:05:29 -05:00
t . Run ( "already minimally configured" , func ( t * testing . T ) {
2021-02-03 15:03:09 -05:00
_ , tearDown := setupConfigDatabase ( t , minimalConfigNoFF , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , "http://minimal" , * ds . Get ( ) . ServiceSettings . SiteURL )
2021-02-03 15:03:09 -05:00
assertDatabaseEqualsConfig ( t , minimalConfigNoFF )
2019-02-15 09:05:29 -05:00
} )
2020-11-16 09:25:48 -05:00
t . Run ( "already minimally configured with custom defaults" , func ( t * testing . T ) {
2021-02-03 15:03:09 -05:00
_ , tearDown := setupConfigDatabase ( t , minimalConfigNoFF , nil )
2020-11-16 09:25:48 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( customConfigDefaults )
2020-11-16 09:25:48 -05:00
require . NoError ( t , err )
defer ds . Close ( )
// as the whole config has default values already, custom
// defaults should have no effect
assert . Equal ( t , "http://minimal" , * ds . Get ( ) . ServiceSettings . SiteURL )
2021-02-03 15:03:09 -05:00
assertDatabaseEqualsConfig ( t , minimalConfigNoFF )
2020-11-16 09:25:48 -05:00
} )
2019-02-15 09:05:29 -05:00
t . Run ( "invalid url" , func ( t * testing . T ) {
2021-05-19 07:30:26 -04:00
_ , err := NewDatabaseStore ( "" )
2019-02-15 09:05:29 -05:00
require . Error ( t , err )
2020-03-03 11:42:17 -05:00
Remove vestigial MySQL support (#34865)
* Remove legacy quoteColumnName() utility
Since Mattermost only supports PostgreSQL, the quoteColumnName() helper
that was designed to handle database-specific column quoting is no longer
needed. The function was a no-op that simply returned the column name
unchanged.
Remove the function from utils.go and update status_store.go to use
the "Manual" column name directly.
* Remove legacy driver checks from store.go
Since Mattermost only supports PostgreSQL, remove conditional checks
for different database drivers:
- Simplify specialSearchChars() to always return PostgreSQL-compatible chars
- Remove driver check from computeBinaryParam()
- Remove driver check from computeDefaultTextSearchConfig()
- Simplify GetDbVersion() to use PostgreSQL syntax directly
- Remove switch statement from ensureMinimumDBVersion()
- Remove unused driver parameter from versionString()
* Remove MySQL alternatives for batch delete operations
Since Mattermost only supports PostgreSQL, remove the MySQL-specific
DELETE...LIMIT syntax and keep only the PostgreSQL array-based approach:
- reaction_store.go: Use PostgreSQL array syntax for PermanentDeleteBatch
- file_info_store.go: Use PostgreSQL array syntax for PermanentDeleteBatch
- preference_store.go: Use PostgreSQL tuple IN subquery for DeleteInvalidVisibleDmsGms
* Remove MySQL alternatives for UPDATE...FROM syntax
Since Mattermost only supports PostgreSQL, remove the MySQL-specific
UPDATE syntax that joins tables differently:
- thread_store.go: Use PostgreSQL UPDATE...FROM syntax in
MarkAllAsReadByChannels and MarkAllAsReadByTeam
- post_store.go: Use PostgreSQL UPDATE...FROM syntax in deleteThreadFiles
* Remove MySQL alternatives for JSON and subquery operations
Since Mattermost only supports PostgreSQL, remove the MySQL-specific
JSON and subquery syntax:
- thread_store.go: Use PostgreSQL JSONB operators for updating participants
- access_control_policy_store.go: Use PostgreSQL JSONB @> operator for
querying JSON imports
- session_store.go: Use PostgreSQL subquery syntax for Cleanup
- job_store.go: Use PostgreSQL subquery syntax for Cleanup
* Remove MySQL alternatives for CTE queries
Since Mattermost only supports PostgreSQL, simplify code that
uses CTEs (Common Table Expressions):
- channel_store.go: Remove MySQL CASE-based fallback in
UpdateLastViewedAt and use PostgreSQL CTE exclusively
- draft_store.go: Remove driver checks in DeleteEmptyDraftsByCreateAtAndUserId,
DeleteOrphanDraftsByCreateAtAndUserId, and determineMaxDraftSize
* Remove driver checks in migrate.go and schema_dump.go
Simplify migration code to use PostgreSQL driver directly since
PostgreSQL is the only supported database.
* Remove driver checks in sqlx_wrapper.go
Always apply lowercase named parameter transformation since PostgreSQL
is the only supported database.
* Remove driver checks in user_store.go
Simplify user store functions to use PostgreSQL-only code paths:
- Remove isPostgreSQL parameter from helper functions
- Use LEFT JOIN pattern instead of subqueries for bot filtering
- Always use case-insensitive LIKE with lower() for search
- Remove MySQL-specific role filtering alternatives
* Remove driver checks in post_store.go
Simplify post_store.go to use PostgreSQL-only code paths:
- Inline getParentsPostsPostgreSQL into getParentsPosts
- Use PostgreSQL TO_CHAR/TO_TIMESTAMP for date formatting in analytics
- Use PostgreSQL array syntax for batch deletes
- Simplify determineMaxPostSize to always use information_schema
- Use PostgreSQL jsonb subtraction for thread participants
- Always execute RefreshPostStats (PostgreSQL materialized views)
- Use materialized views for AnalyticsPostCountsByDay
- Simplify AnalyticsPostCountByTeam to always use countByTeam
* Remove driver checks in channel_store.go
Simplify channel_store.go to use PostgreSQL-only code paths:
- Always use sq.Dollar.ReplacePlaceholders for UNION queries
- Use PostgreSQL LEFT JOIN for retention policy exclusion
- Use PostgreSQL jsonb @> operator for access control policy imports
- Simplify buildLIKEClause to always use LOWER() for case-insensitive search
- Simplify buildFulltextClauseX to always use PostgreSQL to_tsvector/to_tsquery
- Simplify searchGroupChannelsQuery to use ARRAY_TO_STRING/ARRAY_AGG
* Remove driver checks in file_info_store.go
Simplify file_info_store.go to use PostgreSQL-only code paths:
- Always use PostgreSQL to_tsvector/to_tsquery for file search
- Use file_stats materialized view for CountAll()
- Use file_stats materialized view for GetStorageUsage() when not including deleted
- Always execute RefreshFileStats() for materialized view refresh
* Remove driver checks in attributes_store.go
Simplify attributes_store.go to use PostgreSQL-only code paths:
- Always execute RefreshAttributes() for materialized view refresh
- Remove isPostgreSQL parameter from generateSearchQueryForExpression
- Always use PostgreSQL LOWER() LIKE LOWER() syntax for case-insensitive search
* Remove driver checks in retention_policy_store.go
Simplify retention_policy_store.go to use PostgreSQL-only code paths:
- Remove isPostgres parameter from scanRetentionIdsForDeletion
- Always use pq.Array for scanning retention IDs
- Always use pq.Array for inserting retention IDs
- Remove unused json import
* Remove driver checks in property stores
Simplify property_field_store.go and property_value_store.go to use
PostgreSQL-only code paths:
- Always use PostgreSQL type casts (::text, ::jsonb, ::bigint, etc.)
- Remove isPostgres variable and conditionals
* Remove driver checks in channel_member_history_store.go
Simplify PermanentDeleteBatch to use PostgreSQL-only code path:
- Always use ctid-based subquery for DELETE with LIMIT
* Remove remaining driver checks in user_store.go
Simplify user_store.go to use PostgreSQL-only code paths:
- Use LEFT JOIN for bot exclusion in AnalyticsActiveCountForPeriod
- Use LEFT JOIN for bot exclusion in IsEmpty
* Simplify fulltext search by consolidating buildFulltextClause functions
Remove convertMySQLFullTextColumnsToPostgres and consolidate
buildFulltextClause and buildFulltextClauseX into a single function
that takes variadic column arguments and returns sq.Sqlizer.
* Simplify SQL stores leveraging PostgreSQL-only support
- Simplify UpdateMembersRole in channel_store.go and team_store.go
to use UPDATE...RETURNING instead of SELECT + UPDATE
- Simplify GetPostReminders in post_store.go to use DELETE...RETURNING
- Simplify DeleteOrphanedRows queries by removing MySQL workarounds
for subquery locking issues
- Simplify UpdateUserLastSyncAt to use UPDATE...FROM...RETURNING
instead of fetching user first then updating
- Remove MySQL index hint workarounds in ORDER BY clauses
- Update outdated comments referencing MySQL
- Consolidate buildFulltextClause and remove convertMySQLFullTextColumnsToPostgres
* Remove MySQL-specific test artifacts
- Delete unused MySQLStopWords variable and stop_word.go file
- Remove redundant testSearchEmailAddressesWithQuotes test
(already covered by testSearchEmailAddresses)
- Update comment that referenced MySQL query planning
* Remove MySQL references from server code outside sqlstore
- Update config example and DSN parsing docs to reflect PostgreSQL-only support
- Remove mysql:// scheme check from IsDatabaseDSN
- Simplify SanitizeDataSource to only handle PostgreSQL
- Remove outdated MySQL comments from model and plugin code
* Remove MySQL references from test files
- Update test DSNs to use PostgreSQL format
- Remove dead mysql-replica flag and replicaFlag variable
- Simplify tests that had MySQL/PostgreSQL branches
* Update docs and test config to use PostgreSQL
- Update mmctl config set example to use postgres driver
- Update test-config.json to use PostgreSQL DSN format
* Remove MySQL migration scripts, test data, and docker image
Delete MySQL-related files that are no longer needed:
- ESR upgrade scripts (esr.*.mysql.*.sql)
- MySQL schema dumps (mattermost-mysql-*.sql)
- MySQL replication test scripts (replica-*.sh, mysql-migration-test.sh)
- MySQL test warmup data (mysql_migration_warmup.sql)
- MySQL docker image reference from mirror-docker-images.json
* Remove MySQL references from webapp
- Simplify minimumHashtagLength description to remove MySQL-specific configuration note
- Remove unused HIDE_MYSQL_STATS_NOTIFICATION preference constant
- Update en.json i18n source file
* clean up e2e-tests
* rm server/tests/template.load
* Use teamMemberSliceColumns() in UpdateMembersRole RETURNING clause
Refactor to use the existing helper function instead of hardcoding
the column names, ensuring consistency if the columns are updated.
* u.id -> u.Id
* address code review feedback
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-01-20 16:01:59 -05:00
_ , err = NewDatabaseStore ( "postgres" )
2020-03-03 11:42:17 -05:00
require . Error ( t , err )
2019-02-15 09:05:29 -05:00
} )
t . Run ( "unsupported scheme" , func ( t * testing . T ) {
2021-05-19 07:30:26 -04:00
_ , err := NewDatabaseStore ( "invalid" )
2019-02-15 09:05:29 -05:00
require . Error ( t , err )
} )
2019-09-20 00:22:40 -04:00
t . Run ( "unsupported scheme with valid data source" , func ( t * testing . T ) {
2021-05-19 07:30:26 -04:00
_ , err := NewDatabaseStore ( fmt . Sprintf ( "invalid://%s" , * sqlSettings . DataSource ) )
2019-09-20 00:22:40 -04:00
require . Error ( t , err )
} )
2019-02-15 09:05:29 -05:00
}
func TestDatabaseStoreGet ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
cfg := ds . Get ( )
assert . Equal ( t , "http://TestStoreNew" , * cfg . ServiceSettings . SiteURL )
cfg2 := ds . Get ( )
assert . Equal ( t , "http://TestStoreNew" , * cfg . ServiceSettings . SiteURL )
assert . True ( t , cfg == cfg2 , "Get() returned different configuration instances" )
}
2022-02-28 04:31:00 -05:00
func TestDatabaseStoreGetEnvironmentOverrides ( t * testing . T ) {
2019-07-05 18:10:48 -04:00
t . Run ( "get override for a string variable" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2019-02-15 09:05:29 -05:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2020-11-16 09:25:48 -05:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , "http://TestStoreNew" , * ds . Get ( ) . ServiceSettings . SiteURL )
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
os . Setenv ( "MM_SERVICESETTINGS_SITEURL" , "http://override" )
defer os . Unsetenv ( "MM_SERVICESETTINGS_SITEURL" )
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( nil )
2020-11-16 09:25:48 -05:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , "http://override" , * ds . Get ( ) . ServiceSettings . SiteURL )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "ServiceSettings" : map [ string ] any { "SiteURL" : true } } , ds . GetEnvironmentOverrides ( ) )
2020-11-16 09:25:48 -05:00
} )
t . Run ( "get override for a string variable with a custom default value" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( customConfigDefaults )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
2019-02-15 09:05:29 -05:00
2019-07-05 18:10:48 -04:00
assert . Equal ( t , "http://TestStoreNew" , * ds . Get ( ) . ServiceSettings . SiteURL )
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
2019-02-15 09:05:29 -05:00
2019-07-05 18:10:48 -04:00
os . Setenv ( "MM_SERVICESETTINGS_SITEURL" , "http://override" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SERVICESETTINGS_SITEURL" )
2019-02-15 09:05:29 -05:00
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( customConfigDefaults )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
2020-11-16 09:25:48 -05:00
// environment override should take priority over the custom default value
2019-07-05 18:10:48 -04:00
assert . Equal ( t , "http://override" , * ds . Get ( ) . ServiceSettings . SiteURL )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "ServiceSettings" : map [ string ] any { "SiteURL" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "get override for a bool variable" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , false , * ds . Get ( ) . PluginSettings . EnableUploads )
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
os . Setenv ( "MM_PLUGINSETTINGS_ENABLEUPLOADS" , "true" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_PLUGINSETTINGS_ENABLEUPLOADS" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , true , * ds . Get ( ) . PluginSettings . EnableUploads )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "PluginSettings" : map [ string ] any { "EnableUploads" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "get override for an int variable" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
2021-07-12 14:05:36 -04:00
assert . Equal ( t , model . TeamSettingsDefaultMaxUsersPerTeam , * ds . Get ( ) . TeamSettings . MaxUsersPerTeam )
2019-07-05 18:10:48 -04:00
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
os . Setenv ( "MM_TEAMSETTINGS_MAXUSERSPERTEAM" , "3000" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_TEAMSETTINGS_MAXUSERSPERTEAM" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , 3000 , * ds . Get ( ) . TeamSettings . MaxUsersPerTeam )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "TeamSettings" : map [ string ] any { "MaxUsersPerTeam" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "get override for an int64 variable" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , int64 ( 63072000 ) , * ds . Get ( ) . ServiceSettings . TLSStrictTransportMaxAge )
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
os . Setenv ( "MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE" , "123456" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , int64 ( 123456 ) , * ds . Get ( ) . ServiceSettings . TLSStrictTransportMaxAge )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "ServiceSettings" : map [ string ] any { "TLSStrictTransportMaxAge" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "get override for a slice variable - one value" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , [ ] string { } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
os . Setenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" , "user:pwd@db:5432/test-db" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "SqlSettings" : map [ string ] any { "DataSourceReplicas" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "get override for a slice variable - three values" , func ( t * testing . T ) {
// This should work, but Viper (or we) don't parse environment variables to turn strings with spaces into slices.
t . Skip ( "not implemented yet" )
_ , tearDown := setupConfigDatabase ( t , testConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
2019-02-15 09:05:29 -05:00
2019-07-05 18:10:48 -04:00
assert . Equal ( t , [ ] string { } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
assert . Empty ( t , ds . GetEnvironmentOverrides ( ) )
os . Setenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" , "user:pwd@db:5432/test-db user:pwd@db2:5433/test-db2 user:pwd@db3:5434/test-db3" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err = newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" , "user:pwd@db2:5433/test-db2" , "user:pwd@db3:5434/test-db3" } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "SqlSettings" : map [ string ] any { "DataSourceReplicas" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
} )
2019-02-15 09:05:29 -05:00
}
func TestDatabaseStoreSet ( t * testing . T ) {
2020-03-02 11:13:39 -05:00
if testing . Short ( ) {
t . SkipNow ( )
}
2019-02-19 09:20:11 -05:00
2019-02-15 09:05:29 -05:00
t . Run ( "set same pointer value" , func ( t * testing . T ) {
t . Skip ( "not yet implemented" )
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-02-15 09:05:29 -05:00
if assert . Error ( t , err ) {
assert . EqualError ( t , err , "old configuration modified instead of cloning" )
}
} )
t . Run ( "defaults required" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
newCfg := & model . Config { }
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
2019-06-06 13:57:01 -04:00
assert . Equal ( t , "" , * ds . Get ( ) . ServiceSettings . SiteURL )
2019-02-15 09:05:29 -05:00
} )
t . Run ( "desanitization required" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , ldapConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
newCfg := & model . Config { }
2024-08-05 23:45:00 -04:00
newCfg . LdapSettings . BindPassword = model . NewPointer ( model . FakeSetting )
2019-02-15 09:05:29 -05:00
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
assert . Equal ( t , "password" , * ds . Get ( ) . LdapSettings . BindPassword )
} )
t . Run ( "invalid" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
newCfg := & model . Config { }
2024-08-05 23:45:00 -04:00
newCfg . ServiceSettings . SiteURL = model . NewPointer ( "invalid" )
2019-02-15 09:05:29 -05:00
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-02-15 09:05:29 -05:00
if assert . Error ( t , err ) {
2022-04-13 12:50:46 -04:00
assert . EqualError ( t , err , "new configuration is invalid: Config.IsValid: model.config.is_valid.site_url.app_error, parse \"invalid\": invalid URI for request" )
2019-02-15 09:05:29 -05:00
}
2019-06-06 13:57:01 -04:00
assert . Equal ( t , "" , * ds . Get ( ) . ServiceSettings . SiteURL )
2019-02-15 09:05:29 -05:00
} )
2019-03-06 15:06:45 -05:00
t . Run ( "duplicate ignored" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
2020-01-07 11:00:56 -05:00
beforeID , _ := getActualDatabaseConfig ( t )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
2020-01-07 11:00:56 -05:00
afterID , _ := getActualDatabaseConfig ( t )
assert . Equal ( t , beforeID , afterID , "new record should not have been written" )
2019-03-06 15:06:45 -05:00
} )
2019-02-15 09:05:29 -05:00
t . Run ( "read-only ignored" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , readOnlyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
newCfg := & model . Config {
ServiceSettings : model . ServiceSettings {
2024-08-05 23:45:00 -04:00
SiteURL : model . NewPointer ( "http://new" ) ,
2019-02-15 09:05:29 -05:00
} ,
}
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
assert . Equal ( t , "http://new" , * ds . Get ( ) . ServiceSettings . SiteURL )
} )
2019-02-28 10:51:42 -05:00
t . Run ( "set with automatic save" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
2019-02-28 10:51:42 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-28 10:51:42 -05:00
require . NoError ( t , err )
defer ds . Close ( )
newCfg := & model . Config {
ServiceSettings : model . ServiceSettings {
2024-08-05 23:45:00 -04:00
SiteURL : model . NewPointer ( "http://new" ) ,
2019-02-28 10:51:42 -05:00
} ,
}
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-02-28 10:51:42 -05:00
require . NoError ( t , err )
err = ds . Load ( )
require . NoError ( t , err )
assert . Equal ( t , "http://new" , * ds . Get ( ) . ServiceSettings . SiteURL )
} )
2019-02-15 09:05:29 -05:00
t . Run ( "persist failed" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2024-12-10 08:57:19 -05:00
_ , err = mainHelper . GetSQLStore ( ) . GetMaster ( ) . Exec ( "DROP TABLE Configurations" )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
2021-05-19 07:30:26 -04:00
newCfg := minimalConfig
2019-02-15 09:05:29 -05:00
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-09-27 08:10:38 -04:00
require . Error ( t , err )
assert . True ( t , strings . HasPrefix ( err . Error ( ) , "failed to persist: failed to query active configuration" ) , "unexpected error: " + err . Error ( ) )
2019-02-15 09:05:29 -05:00
2019-06-06 13:57:01 -04:00
assert . Equal ( t , "" , * ds . Get ( ) . ServiceSettings . SiteURL )
2019-02-15 09:05:29 -05:00
} )
t . Run ( "listeners notified" , func ( t * testing . T ) {
2020-01-07 11:00:56 -05:00
activeID , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
called := make ( chan bool , 1 )
callback := func ( oldfg , newCfg * model . Config ) {
called <- true
}
ds . AddListener ( callback )
2021-05-19 07:30:26 -04:00
newCfg := minimalConfig
2019-02-15 09:05:29 -05:00
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( newCfg )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
2019-03-06 15:06:45 -05:00
id , _ := getActualDatabaseConfig ( t )
2020-01-07 11:00:56 -05:00
assert . NotEqual ( t , activeID , id , "new record should have been written" )
2019-03-06 15:06:45 -05:00
2019-11-13 03:11:26 -05:00
require . True ( t , wasCalled ( called , 5 * time . Second ) , "callback should have been called when config written" )
2019-02-15 09:05:29 -05:00
} )
2021-02-03 15:03:09 -05:00
t . Run ( "setting config without persistent feature flag" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2021-02-03 15:03:09 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2021-05-19 07:30:26 -04:00
ds . SetReadOnlyFF ( true )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( minimalConfig )
2021-02-03 15:03:09 -05:00
require . NoError ( t , err )
assert . Equal ( t , "http://minimal" , * ds . Get ( ) . ServiceSettings . SiteURL )
assertDatabaseEqualsConfig ( t , minimalConfigNoFF )
} )
t . Run ( "setting config with persistent feature flags" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2021-02-03 15:03:09 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2021-05-19 07:30:26 -04:00
ds . SetReadOnlyFF ( false )
2021-02-03 15:03:09 -05:00
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( minimalConfig )
2021-02-03 15:03:09 -05:00
require . NoError ( t , err )
assert . Equal ( t , "http://minimal" , * ds . Get ( ) . ServiceSettings . SiteURL )
assertDatabaseEqualsConfig ( t , minimalConfig )
} )
2019-02-15 09:05:29 -05:00
}
func TestDatabaseStoreLoad ( t * testing . T ) {
2020-03-02 11:13:39 -05:00
if testing . Short ( ) {
t . SkipNow ( )
}
2019-02-19 09:20:11 -05:00
2019-02-15 09:05:29 -05:00
t . Run ( "active configuration no longer exists" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
truncateTables ( t )
err = ds . Load ( )
require . NoError ( t , err )
assertDatabaseNotEqualsConfig ( t , emptyConfig )
} )
t . Run ( "honour environment" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2019-03-26 16:28:41 -04:00
assert . Equal ( t , "http://minimal" , * ds . Get ( ) . ServiceSettings . SiteURL )
2019-02-15 09:05:29 -05:00
os . Setenv ( "MM_SERVICESETTINGS_SITEURL" , "http://override" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SERVICESETTINGS_SITEURL" )
2019-02-15 09:05:29 -05:00
err = ds . Load ( )
require . NoError ( t , err )
assert . Equal ( t , "http://override" , * ds . Get ( ) . ServiceSettings . SiteURL )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "ServiceSettings" : map [ string ] any { "SiteURL" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-02-15 09:05:29 -05:00
} )
2019-07-05 18:10:48 -04:00
t . Run ( "do not persist environment variables - string" , func ( t * testing . T ) {
2019-03-26 16:28:41 -04:00
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
os . Setenv ( "MM_SERVICESETTINGS_SITEURL" , "http://overridePersistEnvVariables" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SERVICESETTINGS_SITEURL" )
2019-03-26 16:28:41 -04:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-26 16:28:41 -04:00
require . NoError ( t , err )
defer ds . Close ( )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-03-26 16:28:41 -04:00
require . NoError ( t , err )
assert . Equal ( t , "http://overridePersistEnvVariables" , * ds . Get ( ) . ServiceSettings . SiteURL )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "ServiceSettings" : map [ string ] any { "SiteURL" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-03-26 16:28:41 -04:00
// check that in DB config does not include overwritten variable
_ , actualConfig := getActualDatabaseConfig ( t )
assert . Equal ( t , "http://minimal" , * actualConfig . ServiceSettings . SiteURL )
} )
2019-07-05 18:10:48 -04:00
t . Run ( "do not persist environment variables - boolean" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
os . Setenv ( "MM_PLUGINSETTINGS_ENABLEUPLOADS" , "true" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_PLUGINSETTINGS_ENABLEUPLOADS" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , true , * ds . Get ( ) . PluginSettings . EnableUploads )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
assert . Equal ( t , true , * ds . Get ( ) . PluginSettings . EnableUploads )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "PluginSettings" : map [ string ] any { "EnableUploads" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
// check that in DB config does not include overwritten variable
_ , actualConfig := getActualDatabaseConfig ( t )
assert . Equal ( t , false , * actualConfig . PluginSettings . EnableUploads )
} )
t . Run ( "do not persist environment variables - int" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
os . Setenv ( "MM_TEAMSETTINGS_MAXUSERSPERTEAM" , "3000" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_TEAMSETTINGS_MAXUSERSPERTEAM" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , 3000 , * ds . Get ( ) . TeamSettings . MaxUsersPerTeam )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
assert . Equal ( t , 3000 , * ds . Get ( ) . TeamSettings . MaxUsersPerTeam )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "TeamSettings" : map [ string ] any { "MaxUsersPerTeam" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
// check that in DB config does not include overwritten variable
_ , actualConfig := getActualDatabaseConfig ( t )
2021-07-12 14:05:36 -04:00
assert . Equal ( t , model . TeamSettingsDefaultMaxUsersPerTeam , * actualConfig . TeamSettings . MaxUsersPerTeam )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "do not persist environment variables - int64" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
os . Setenv ( "MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE" , "123456" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , int64 ( 123456 ) , * ds . Get ( ) . ServiceSettings . TLSStrictTransportMaxAge )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
assert . Equal ( t , int64 ( 123456 ) , * ds . Get ( ) . ServiceSettings . TLSStrictTransportMaxAge )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "ServiceSettings" : map [ string ] any { "TLSStrictTransportMaxAge" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
// check that in DB config does not include overwritten variable
_ , actualConfig := getActualDatabaseConfig ( t )
assert . Equal ( t , int64 ( 63072000 ) , * actualConfig . ServiceSettings . TLSStrictTransportMaxAge )
} )
t . Run ( "do not persist environment variables - string slice beginning with default" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
os . Setenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" , "user:pwd@db:5432/test-db" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "SqlSettings" : map [ string ] any { "DataSourceReplicas" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
// check that in DB config does not include overwritten variable
_ , actualConfig := getActualDatabaseConfig ( t )
2020-04-29 17:06:09 -04:00
assert . Equal ( t , [ ] string { } , actualConfig . SqlSettings . DataSourceReplicas )
2019-07-05 18:10:48 -04:00
} )
t . Run ( "do not persist environment variables - string slice beginning with slice of three" , func ( t * testing . T ) {
modifiedMinimalConfig := minimalConfig . Clone ( )
modifiedMinimalConfig . SqlSettings . DataSourceReplicas = [ ] string { "user:pwd@db:5432/test-db" , "user:pwd@db2:5433/test-db2" , "user:pwd@db3:5434/test-db3" }
_ , tearDown := setupConfigDatabase ( t , modifiedMinimalConfig , nil )
defer tearDown ( )
os . Setenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" , "user:pwd@db:5432/test-db" )
2019-08-09 11:33:59 -04:00
defer os . Unsetenv ( "MM_SQLSETTINGS_DATASOURCEREPLICAS" )
2019-07-05 18:10:48 -04:00
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
defer ds . Close ( )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
2021-05-21 03:04:39 -04:00
_ , _ , err = ds . Set ( ds . Get ( ) )
2019-07-05 18:10:48 -04:00
require . NoError ( t , err )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" } , ds . Get ( ) . SqlSettings . DataSourceReplicas )
2022-07-05 02:46:50 -04:00
assert . Equal ( t , map [ string ] any { "SqlSettings" : map [ string ] any { "DataSourceReplicas" : true } } , ds . GetEnvironmentOverrides ( ) )
2019-07-05 18:10:48 -04:00
// check that in DB config does not include overwritten variable
_ , actualConfig := getActualDatabaseConfig ( t )
assert . Equal ( t , [ ] string { "user:pwd@db:5432/test-db" , "user:pwd@db2:5433/test-db2" , "user:pwd@db3:5434/test-db3" } , actualConfig . SqlSettings . DataSourceReplicas )
} )
2019-02-15 09:05:29 -05:00
t . Run ( "invalid" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
2021-05-19 07:30:26 -04:00
cfgData , err := marshalConfig ( invalidConfig )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
truncateTables ( t )
id := model . NewId ( )
2024-12-10 08:57:19 -05:00
_ , err = mainHelper . GetSQLStore ( ) . GetMaster ( ) . NamedExec ( "INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:id, :value, :createat, TRUE)" , map [ string ] any {
2022-03-15 10:19:19 -04:00
"id" : id ,
"value" : cfgData ,
"createat" : model . GetMillis ( ) ,
2019-02-15 09:05:29 -05:00
} )
2022-03-15 10:19:19 -04:00
t . Logf ( "%v\n" , err )
require . NoErrorf ( t , err , "what is - %v" , err )
2019-02-15 09:05:29 -05:00
err = ds . Load ( )
if assert . Error ( t , err ) {
2022-06-14 05:28:15 -04:00
var appErr * model . AppError
require . True ( t , errors . As ( err , & appErr ) )
assert . Equal ( t , appErr . Id , "model.config.is_valid.site_url.app_error" )
2019-02-15 09:05:29 -05:00
}
} )
t . Run ( "fixes required" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , fixesRequiredConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
err = ds . Load ( )
require . NoError ( t , err )
assertDatabaseNotEqualsConfig ( t , fixesRequiredConfig )
assert . Equal ( t , "http://trailingslash" , * ds . Get ( ) . ServiceSettings . SiteURL )
} )
2022-02-28 04:31:00 -05:00
t . Run ( "listeners notified on change" , func ( t * testing . T ) {
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
defer ds . Close ( )
called := make ( chan bool , 1 )
callback := func ( oldfg , newCfg * model . Config ) {
called <- true
}
ds . AddListener ( callback )
2021-05-19 07:30:26 -04:00
newCfg := minimalConfig . Clone ( )
dbStore , ok := ds . backingStore . ( * DatabaseStore )
require . True ( t , ok )
err = dbStore . persist ( newCfg )
require . NoError ( t , err )
2019-02-15 09:05:29 -05:00
err = ds . Load ( )
require . NoError ( t , err )
2021-05-19 07:30:26 -04:00
require . True ( t , wasCalled ( called , 5 * time . Second ) , "callback should have been called when config changed on load" )
2019-02-15 09:05:29 -05:00
} )
}
2019-03-06 15:06:45 -05:00
func TestDatabaseGetFile ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , map [ string ] [ ] byte {
2019-10-28 09:08:08 -04:00
"empty-file" : { } ,
2019-03-06 15:06:45 -05:00
"test-file" : [ ] byte ( "test" ) ,
} )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
t . Run ( "get empty filename" , func ( t * testing . T ) {
_ , err := ds . GetFile ( "" )
require . Error ( t , err )
} )
t . Run ( "get non-existent file" , func ( t * testing . T ) {
_ , err := ds . GetFile ( "unknown" )
require . Error ( t , err )
} )
t . Run ( "get empty file" , func ( t * testing . T ) {
data , err := ds . GetFile ( "empty-file" )
require . NoError ( t , err )
require . Empty ( t , data )
} )
t . Run ( "get non-empty file" , func ( t * testing . T ) {
data , err := ds . GetFile ( "test-file" )
require . NoError ( t , err )
require . Equal ( t , [ ] byte ( "test" ) , data )
} )
}
func TestDatabaseSetFile ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
t . Run ( "set new file" , func ( t * testing . T ) {
err := ds . SetFile ( "new" , [ ] byte ( "new file" ) )
require . NoError ( t , err )
data , err := ds . GetFile ( "new" )
require . NoError ( t , err )
require . Equal ( t , [ ] byte ( "new file" ) , data )
} )
t . Run ( "overwrite existing file" , func ( t * testing . T ) {
err := ds . SetFile ( "existing" , [ ] byte ( "existing file" ) )
require . NoError ( t , err )
err = ds . SetFile ( "existing" , [ ] byte ( "overwritten file" ) )
require . NoError ( t , err )
data , err := ds . GetFile ( "existing" )
require . NoError ( t , err )
require . Equal ( t , [ ] byte ( "overwritten file" ) , data )
} )
}
func TestDatabaseHasFile ( t * testing . T ) {
t . Run ( "has non-existent" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
has , err := ds . HasFile ( "non-existent" )
require . NoError ( t , err )
require . False ( t , has )
} )
t . Run ( "has existing" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
err = ds . SetFile ( "existing" , [ ] byte ( "existing file" ) )
require . NoError ( t , err )
has , err := ds . HasFile ( "existing" )
require . NoError ( t , err )
require . True ( t , has )
} )
t . Run ( "has manually created file" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , map [ string ] [ ] byte {
"manual" : [ ] byte ( "manual file" ) ,
} )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
has , err := ds . HasFile ( "manual" )
require . NoError ( t , err )
require . True ( t , has )
} )
2019-06-05 17:51:59 -04:00
t . Run ( "has non-existent empty string" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-06-05 17:51:59 -04:00
require . NoError ( t , err )
defer ds . Close ( )
has , err := ds . HasFile ( "" )
require . NoError ( t , err )
require . False ( t , has )
} )
2019-03-06 15:06:45 -05:00
}
func TestDatabaseRemoveFile ( t * testing . T ) {
t . Run ( "remove non-existent" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
err = ds . RemoveFile ( "non-existent" )
require . NoError ( t , err )
} )
t . Run ( "remove existing" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , nil )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
err = ds . SetFile ( "existing" , [ ] byte ( "existing file" ) )
require . NoError ( t , err )
err = ds . RemoveFile ( "existing" )
require . NoError ( t , err )
has , err := ds . HasFile ( "existing" )
require . NoError ( t , err )
require . False ( t , has )
_ , err = ds . GetFile ( "existing" )
require . Error ( t , err )
} )
t . Run ( "remove manually created file" , func ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , minimalConfig , map [ string ] [ ] byte {
"manual" : [ ] byte ( "manual file" ) ,
} )
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-03-06 15:06:45 -05:00
require . NoError ( t , err )
defer ds . Close ( )
err = ds . RemoveFile ( "manual" )
require . NoError ( t , err )
has , err := ds . HasFile ( "manual" )
require . NoError ( t , err )
require . False ( t , has )
_ , err = ds . GetFile ( "manual" )
require . Error ( t , err )
} )
}
2019-02-15 09:05:29 -05:00
func TestDatabaseStoreString ( t * testing . T ) {
2020-03-02 11:13:39 -05:00
if testing . Short ( ) {
t . SkipNow ( )
}
2019-03-06 15:06:45 -05:00
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
2019-02-15 09:05:29 -05:00
defer tearDown ( )
2021-02-09 12:52:27 -05:00
ds , err := newTestDatabaseStore ( nil )
2019-02-15 09:05:29 -05:00
require . NoError ( t , err )
2019-09-20 00:22:40 -04:00
require . NotNil ( t , ds )
2019-02-15 09:05:29 -05:00
defer ds . Close ( )
Remove vestigial MySQL support (#34865)
* Remove legacy quoteColumnName() utility
Since Mattermost only supports PostgreSQL, the quoteColumnName() helper
that was designed to handle database-specific column quoting is no longer
needed. The function was a no-op that simply returned the column name
unchanged.
Remove the function from utils.go and update status_store.go to use
the "Manual" column name directly.
* Remove legacy driver checks from store.go
Since Mattermost only supports PostgreSQL, remove conditional checks
for different database drivers:
- Simplify specialSearchChars() to always return PostgreSQL-compatible chars
- Remove driver check from computeBinaryParam()
- Remove driver check from computeDefaultTextSearchConfig()
- Simplify GetDbVersion() to use PostgreSQL syntax directly
- Remove switch statement from ensureMinimumDBVersion()
- Remove unused driver parameter from versionString()
* Remove MySQL alternatives for batch delete operations
Since Mattermost only supports PostgreSQL, remove the MySQL-specific
DELETE...LIMIT syntax and keep only the PostgreSQL array-based approach:
- reaction_store.go: Use PostgreSQL array syntax for PermanentDeleteBatch
- file_info_store.go: Use PostgreSQL array syntax for PermanentDeleteBatch
- preference_store.go: Use PostgreSQL tuple IN subquery for DeleteInvalidVisibleDmsGms
* Remove MySQL alternatives for UPDATE...FROM syntax
Since Mattermost only supports PostgreSQL, remove the MySQL-specific
UPDATE syntax that joins tables differently:
- thread_store.go: Use PostgreSQL UPDATE...FROM syntax in
MarkAllAsReadByChannels and MarkAllAsReadByTeam
- post_store.go: Use PostgreSQL UPDATE...FROM syntax in deleteThreadFiles
* Remove MySQL alternatives for JSON and subquery operations
Since Mattermost only supports PostgreSQL, remove the MySQL-specific
JSON and subquery syntax:
- thread_store.go: Use PostgreSQL JSONB operators for updating participants
- access_control_policy_store.go: Use PostgreSQL JSONB @> operator for
querying JSON imports
- session_store.go: Use PostgreSQL subquery syntax for Cleanup
- job_store.go: Use PostgreSQL subquery syntax for Cleanup
* Remove MySQL alternatives for CTE queries
Since Mattermost only supports PostgreSQL, simplify code that
uses CTEs (Common Table Expressions):
- channel_store.go: Remove MySQL CASE-based fallback in
UpdateLastViewedAt and use PostgreSQL CTE exclusively
- draft_store.go: Remove driver checks in DeleteEmptyDraftsByCreateAtAndUserId,
DeleteOrphanDraftsByCreateAtAndUserId, and determineMaxDraftSize
* Remove driver checks in migrate.go and schema_dump.go
Simplify migration code to use PostgreSQL driver directly since
PostgreSQL is the only supported database.
* Remove driver checks in sqlx_wrapper.go
Always apply lowercase named parameter transformation since PostgreSQL
is the only supported database.
* Remove driver checks in user_store.go
Simplify user store functions to use PostgreSQL-only code paths:
- Remove isPostgreSQL parameter from helper functions
- Use LEFT JOIN pattern instead of subqueries for bot filtering
- Always use case-insensitive LIKE with lower() for search
- Remove MySQL-specific role filtering alternatives
* Remove driver checks in post_store.go
Simplify post_store.go to use PostgreSQL-only code paths:
- Inline getParentsPostsPostgreSQL into getParentsPosts
- Use PostgreSQL TO_CHAR/TO_TIMESTAMP for date formatting in analytics
- Use PostgreSQL array syntax for batch deletes
- Simplify determineMaxPostSize to always use information_schema
- Use PostgreSQL jsonb subtraction for thread participants
- Always execute RefreshPostStats (PostgreSQL materialized views)
- Use materialized views for AnalyticsPostCountsByDay
- Simplify AnalyticsPostCountByTeam to always use countByTeam
* Remove driver checks in channel_store.go
Simplify channel_store.go to use PostgreSQL-only code paths:
- Always use sq.Dollar.ReplacePlaceholders for UNION queries
- Use PostgreSQL LEFT JOIN for retention policy exclusion
- Use PostgreSQL jsonb @> operator for access control policy imports
- Simplify buildLIKEClause to always use LOWER() for case-insensitive search
- Simplify buildFulltextClauseX to always use PostgreSQL to_tsvector/to_tsquery
- Simplify searchGroupChannelsQuery to use ARRAY_TO_STRING/ARRAY_AGG
* Remove driver checks in file_info_store.go
Simplify file_info_store.go to use PostgreSQL-only code paths:
- Always use PostgreSQL to_tsvector/to_tsquery for file search
- Use file_stats materialized view for CountAll()
- Use file_stats materialized view for GetStorageUsage() when not including deleted
- Always execute RefreshFileStats() for materialized view refresh
* Remove driver checks in attributes_store.go
Simplify attributes_store.go to use PostgreSQL-only code paths:
- Always execute RefreshAttributes() for materialized view refresh
- Remove isPostgreSQL parameter from generateSearchQueryForExpression
- Always use PostgreSQL LOWER() LIKE LOWER() syntax for case-insensitive search
* Remove driver checks in retention_policy_store.go
Simplify retention_policy_store.go to use PostgreSQL-only code paths:
- Remove isPostgres parameter from scanRetentionIdsForDeletion
- Always use pq.Array for scanning retention IDs
- Always use pq.Array for inserting retention IDs
- Remove unused json import
* Remove driver checks in property stores
Simplify property_field_store.go and property_value_store.go to use
PostgreSQL-only code paths:
- Always use PostgreSQL type casts (::text, ::jsonb, ::bigint, etc.)
- Remove isPostgres variable and conditionals
* Remove driver checks in channel_member_history_store.go
Simplify PermanentDeleteBatch to use PostgreSQL-only code path:
- Always use ctid-based subquery for DELETE with LIMIT
* Remove remaining driver checks in user_store.go
Simplify user_store.go to use PostgreSQL-only code paths:
- Use LEFT JOIN for bot exclusion in AnalyticsActiveCountForPeriod
- Use LEFT JOIN for bot exclusion in IsEmpty
* Simplify fulltext search by consolidating buildFulltextClause functions
Remove convertMySQLFullTextColumnsToPostgres and consolidate
buildFulltextClause and buildFulltextClauseX into a single function
that takes variadic column arguments and returns sq.Sqlizer.
* Simplify SQL stores leveraging PostgreSQL-only support
- Simplify UpdateMembersRole in channel_store.go and team_store.go
to use UPDATE...RETURNING instead of SELECT + UPDATE
- Simplify GetPostReminders in post_store.go to use DELETE...RETURNING
- Simplify DeleteOrphanedRows queries by removing MySQL workarounds
for subquery locking issues
- Simplify UpdateUserLastSyncAt to use UPDATE...FROM...RETURNING
instead of fetching user first then updating
- Remove MySQL index hint workarounds in ORDER BY clauses
- Update outdated comments referencing MySQL
- Consolidate buildFulltextClause and remove convertMySQLFullTextColumnsToPostgres
* Remove MySQL-specific test artifacts
- Delete unused MySQLStopWords variable and stop_word.go file
- Remove redundant testSearchEmailAddressesWithQuotes test
(already covered by testSearchEmailAddresses)
- Update comment that referenced MySQL query planning
* Remove MySQL references from server code outside sqlstore
- Update config example and DSN parsing docs to reflect PostgreSQL-only support
- Remove mysql:// scheme check from IsDatabaseDSN
- Simplify SanitizeDataSource to only handle PostgreSQL
- Remove outdated MySQL comments from model and plugin code
* Remove MySQL references from test files
- Update test DSNs to use PostgreSQL format
- Remove dead mysql-replica flag and replicaFlag variable
- Simplify tests that had MySQL/PostgreSQL branches
* Update docs and test config to use PostgreSQL
- Update mmctl config set example to use postgres driver
- Update test-config.json to use PostgreSQL DSN format
* Remove MySQL migration scripts, test data, and docker image
Delete MySQL-related files that are no longer needed:
- ESR upgrade scripts (esr.*.mysql.*.sql)
- MySQL schema dumps (mattermost-mysql-*.sql)
- MySQL replication test scripts (replica-*.sh, mysql-migration-test.sh)
- MySQL test warmup data (mysql_migration_warmup.sql)
- MySQL docker image reference from mirror-docker-images.json
* Remove MySQL references from webapp
- Simplify minimumHashtagLength description to remove MySQL-specific configuration note
- Remove unused HIDE_MYSQL_STATS_NOTIFICATION preference constant
- Update en.json i18n source file
* clean up e2e-tests
* rm server/tests/template.load
* Use teamMemberSliceColumns() in UpdateMembersRole RETURNING clause
Refactor to use the existing helper function instead of hardcoding
the column names, ensuring consistency if the columns are updated.
* u.id -> u.Id
* address code review feedback
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-01-20 16:01:59 -05:00
maskedDSN := ds . String ( )
assert . True ( t , strings . HasPrefix ( maskedDSN , "postgres://" ) )
assert . False ( t , strings . Contains ( maskedDSN , "mmuser" ) )
assert . False ( t , strings . Contains ( maskedDSN , "mostest" ) )
2019-02-15 09:05:29 -05:00
}
2022-04-15 03:31:10 -04:00
func TestCleanUp ( t * testing . T ) {
_ , tearDown := setupConfigDatabase ( t , emptyConfig , nil )
defer tearDown ( )
ds , err := newTestDatabaseStore ( nil )
require . NoError ( t , err )
require . NotNil ( t , ds )
defer ds . Close ( )
2025-10-29 08:24:42 -04:00
t . Run ( "should keep last 5 configurations regardless" , func ( t * testing . T ) {
dbs , ok := ds . backingStore . ( * DatabaseStore )
require . True ( t , ok , "should be a DatabaseStore instance" )
2022-04-15 03:31:10 -04:00
2025-10-29 08:24:42 -04:00
b , err := marshalConfig ( ds . config )
require . NoError ( t , err )
2022-04-15 03:31:10 -04:00
2025-10-29 08:24:42 -04:00
ds . config . JobSettings . CleanupConfigThresholdDays = model . NewPointer ( 30 ) // we set 30 days as threshold
var initialCount int
row := dbs . db . QueryRow ( "SELECT COUNT(*) FROM Configurations" )
err = row . Scan ( & initialCount )
require . NoError ( t , err )
require . Less ( t , initialCount , 5 , "should have less than 5 configurations before test" )
now := time . Now ( )
for i := range 10 {
// we are simulating that each config was created 40 days apart
// so all but last 5 should be deleted
m := - 1 * i * 24 * 40
params := map [ string ] any {
"id" : model . NewId ( ) ,
"value" : string ( b ) ,
"create_at" : model . GetMillisForTime ( now . Add ( time . Duration ( m ) * time . Hour ) ) ,
}
_ , err = dbs . db . NamedExec ( "INSERT INTO Configurations (Id, Value, CreateAt) VALUES (:id, :value, :create_at)" , params )
require . NoError ( t , err )
}
var beforeCleanup int
row = dbs . db . QueryRow ( "SELECT COUNT(*) FROM Configurations" )
err = row . Scan ( & beforeCleanup )
require . NoError ( t , err )
require . Equal ( t , 10 + initialCount , beforeCleanup , "should have more than 10 configurations before cleanup" )
err = ds . CleanUp ( )
require . NoError ( t , err )
var count int
row = dbs . db . QueryRow ( "SELECT COUNT(*) FROM Configurations" )
err = row . Scan ( & count )
require . NoError ( t , err )
require . Equal ( t , 5 , count , "should have only 5 configurations left" )
} )
t . Run ( "should keep the active configuration regardless" , func ( t * testing . T ) {
b , err := marshalConfig ( ds . config )
require . NoError ( t , err )
dbs , ok := ds . backingStore . ( * DatabaseStore )
require . True ( t , ok , "should be a DatabaseStore instance" )
// remove all other configurations
_ , err = dbs . db . Exec ( "DELETE FROM Configurations" )
require . NoError ( t , err )
2022-04-15 03:31:10 -04:00
2022-07-05 02:46:50 -04:00
params := map [ string ] any {
2025-10-29 08:24:42 -04:00
"id" : model . NewId ( ) ,
"value" : string ( b ) ,
// we set the create_at to 100 days ago so it wouldn't be deleted if it is active
"create_at" : model . GetMillisForTime ( time . Now ( ) . Add ( time . Duration ( - 1 * 100 * 24 ) * time . Hour ) ) ,
2022-04-15 03:31:10 -04:00
}
_ , err = dbs . db . NamedExec ( "INSERT INTO Configurations (Id, Value, CreateAt) VALUES (:id, :value, :create_at)" , params )
require . NoError ( t , err )
2025-10-29 08:24:42 -04:00
err = ds . CleanUp ( )
require . NoError ( t , err )
2022-04-15 03:31:10 -04:00
2025-10-29 08:24:42 -04:00
var count int
row := dbs . db . QueryRow ( "SELECT COUNT(*) FROM Configurations" )
err = row . Scan ( & count )
require . NoError ( t , err )
require . Equal ( t , 1 , count , "should have only 1 configuration left" )
} )
2022-04-15 03:31:10 -04:00
}