1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-12-24 13:43:12 +02:00

Add mariadb tests and update database migration for compatibility (#3929)

* Add mariadb tests and update database migration for compatibility

* add additional tests for migrating json fields

* remove unnecessary test

Co-authored-by: Scott Bishel <scott.bishel@mattermost.com>
This commit is contained in:
Miguel de la Cruz 2022-10-20 15:36:13 +02:00 committed by GitHub
parent 667abb3a55
commit 821e3e1c85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 45 deletions

View File

@ -9,7 +9,7 @@ on:
workflow_dispatch:
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
EXCLUDE_ENTERPRISE: true
jobs:
@ -22,6 +22,7 @@ jobs:
db:
- sqlite
- mysql
- mariadb
- postgres
steps:
@ -34,14 +35,14 @@ jobs:
continue-on-error: true
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref: ${{ env.BRANCH_NAME }}
- uses: actions/checkout@v3
if: steps.mattermostServer.outcome == 'failure'
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref : "master"
- name: Set up Go
@ -64,14 +65,14 @@ jobs:
continue-on-error: true
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref: ${{ env.BRANCH_NAME }}
- uses: actions/checkout@v3
if: steps.mattermostServer.outcome == 'failure'
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref : "master"
- name: npm ci
@ -122,14 +123,14 @@ jobs:
continue-on-error: true
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref: ${{ env.BRANCH_NAME }}
- uses: actions/checkout@v3
if: steps.mattermostServer.outcome == 'failure'
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref : "master"
@ -159,14 +160,14 @@ jobs:
continue-on-error: true
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref: ${{ env.BRANCH_NAME }}
- uses: actions/checkout@v3
if: steps.mattermostServer.outcome == 'failure'
with:
repository: "mattermost/mattermost-server"
fetch-depth: "20"
fetch-depth: "20"
path: "mattermost-server"
ref : "master"

View File

@ -126,7 +126,7 @@ watch-single-user: modd-precheck ## Run both server and webapp in single user mo
watch-server-test: modd-precheck ## Run server tests watching for changes
env FOCALBOARD_BUILD_TAGS='$(BUILD_TAGS)' modd -f modd-servertest.conf
server-test: server-test-sqlite server-test-mysql server-test-postgres ## Run server tests
server-test: server-test-sqlite server-test-mysql server-test-mariadb server-test-postgres ## Run server tests
server-test-sqlite: export FOCALBOARD_UNIT_TESTING=1
@ -153,6 +153,20 @@ server-test-mysql: setup-go-work ## Run server tests using mysql
cd mattermost-plugin/server; go tool cover -func plugin-mysql-profile.coverage
docker-compose -f ./docker-testing/docker-compose-mysql.yml down -v --remove-orphans
server-test-mariadb: export FOCALBOARD_UNIT_TESTING=1
server-test-mariadb: export FOCALBOARD_STORE_TEST_DB_TYPE=mariadb
server-test-mariadb: export FOCALBOARD_STORE_TEST_DOCKER_PORT=44445
server-test-mariadb: templates-archive ## Run server tests using mysql
@echo Starting docker container for mariadb
docker-compose -f ./docker-testing/docker-compose-mariadb.yml down -v --remove-orphans
docker-compose -f ./docker-testing/docker-compose-mariadb.yml run start_dependencies
cd server; go test -tags '$(BUILD_TAGS)' -race -v -coverpkg=./... -coverprofile=server-mariadb-profile.coverage -count=1 -timeout=30m ./...
cd server; go tool cover -func server-mariadb-profile.coverage
cd mattermost-plugin/server; go test -tags '$(BUILD_TAGS)' -race -v -coverpkg=./... -coverprofile=plugin-mariadb-profile.coverage -count=1 -timeout=30m ./...
cd mattermost-plugin/server; go tool cover -func plugin-mariadb-profile.coverage
docker-compose -f ./docker-testing/docker-compose-mariadb.yml down -v --remove-orphans
server-test-postgres: export FOCALBOARD_UNIT_TESTING=1
server-test-postgres: export FOCALBOARD_STORE_TEST_DB_TYPE=postgres
server-test-postgres: export FOCALBOARD_STORE_TEST_DOCKER_PORT=44446

View File

@ -0,0 +1,24 @@
version: '2.4'
services:
mariadb:
image: "mariadb:10.9.3"
restart: always
environment:
MARIADB_ROOT_HOST: "%"
MARIADB_ROOT_PASSWORD: mostest
MARIADB_PASSWORD: mostest
MARIADB_USER: mmuser
healthcheck:
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-u", "mmuser", "-pmostest"]
interval: 5s
timeout: 10s
retries: 3
tmpfs: /var/lib/mariadb
ports:
- 44445:3306
start_dependencies:
image: mattermost/mattermost-wait-for-dep:latest
depends_on:
- mariadb
command: mariadb:3306

View File

@ -13,15 +13,15 @@ ALTER TABLE {{.prefix}}blocks_history ADD COLUMN board_id VARCHAR(36);
{{- /* cleanup incorrect data format in column calculations */ -}}
{{- /* then move from 'board' type to 'view' type*/ -}}
{{if .mysql}}
UPDATE {{.prefix}}blocks SET fields = JSON_SET(fields, '$.columnCalculations', cast('{}' as json)) WHERE fields->'$.columnCalculations' = cast('[]' as json);
UPDATE {{.prefix}}blocks SET fields = JSON_SET(fields, '$.columnCalculations', JSON_OBJECT()) WHERE JSON_EXTRACT(fields, '$.columnCalculations') = JSON_ARRAY();
UPDATE {{.prefix}}blocks b
JOIN (
SELECT id, fields->'$.columnCalculations' as board_calculations from {{.prefix}}blocks
WHERE fields -> '$.columnCalculations' <> cast('{}' as json)
SELECT id, JSON_EXTRACT(fields, '$.columnCalculations') as board_calculations from {{.prefix}}blocks
WHERE JSON_EXTRACT(fields, '$.columnCalculations') <> JSON_OBJECT()
) AS s on s.id = b.root_id
SET fields = JSON_SET(fields, '$.columnCalculations', cast(s.board_calculations as json))
WHERE b.fields->'$.viewType' = 'table'
SET fields = JSON_SET(fields, '$.columnCalculations', JSON_ARRAY(s.board_calculations))
WHERE JSON_EXTRACT(b.fields, '$.viewType') = 'table'
AND b.type = 'view';
{{end}}
{{if .postgres}}
@ -166,10 +166,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
COALESCE(B.title, ''),
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.icon')),
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
COALESCE(B.fields->'$.templateVer', 0),
'{}', B.fields->'$.cardProperties', B.create_at,
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
'{}', JSON_EXTRACT(B.fields, '$.cardProperties'), B.create_at,
B.update_at, B.delete_at
FROM {{.prefix}}blocks AS B
INNER JOIN Channels AS C ON C.Id=B.channel_id
@ -180,10 +180,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
COALESCE(B.title, ''),
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.icon')),
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
COALESCE(B.fields->'$.templateVer', 0),
'{}', B.fields->'$.cardProperties', B.create_at,
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
'{}', JSON_EXTRACT(B.fields, '$.cardProperties'), B.create_at,
B.update_at, B.delete_at
FROM {{.prefix}}blocks_history AS B
INNER JOIN Channels AS C ON C.Id=B.channel_id
@ -225,10 +225,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
COALESCE(B.title, ''),
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
JSON_UNQUOTE(JSON_EXTRACT(fields,'$.icon')),
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
COALESCE(B.fields->'$.templateVer', 0),
'{}', fields->'$.cardProperties', create_at,
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
'{}', JSON_EXTRACT(fields, '$.cardProperties'), create_at,
update_at, delete_at
FROM {{.prefix}}blocks AS B
WHERE type='board'
@ -238,10 +238,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
COALESCE(B.title, ''),
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
JSON_UNQUOTE(JSON_EXTRACT(fields,'$.icon')),
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
COALESCE(B.fields->'$.templateVer', 0),
'{}', fields->'$.cardProperties', create_at,
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
'{}', JSON_EXTRACT(fields, '$.cardProperties'), create_at,
update_at, delete_at
FROM {{.prefix}}blocks_history AS B
WHERE type='board'

View File

@ -1,7 +1,10 @@
INSERT INTO Channels (Id, CreateAt, UpdateAt, DeleteAt, TeamId, Type, Name, CreatorId) VALUES ('chan-id', 123, 123, 0, 'team-id', 'O', 'channel', 'user-id');
INSERT INTO focalboard_blocks
(id, workspace_id, root_id, parent_id, created_by, modified_by, type, title, create_at, update_at, delete_at)
(id, workspace_id, root_id, parent_id, created_by, modified_by, type, title, create_at, update_at, delete_at, fields)
VALUES
('board-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'board', 'My Board', 123, 123, 0),
('card-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'card', 'A card', 123, 123, 0);
('board-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'board', 'My Board', 123, 123, 0, '{"columnCalculations": {"__title":"countUniqueValue"}}'),
('card-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'card', 'A card', 123, 123, 0, '{}'),
('view-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'view', 'A view', 123, 123, 0, '{"viewType":"table"}'),
('view-id2', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'view', 'A view2', 123, 123, 0, '{"viewType":"board"}'),
('board-id2', 'chan-id', 'board-id2', 'board-id2', 'user-id', 'user-id', 'board', 'My Board Two', 123, 123, 0, '{"description": "My Description","showDescription":true,"isTemplate":true,"templateVer":1,"columnCalculations":[]}');

View File

@ -1,6 +1,7 @@
package migrationstests
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
@ -15,22 +16,39 @@ func Test18AddTeamsAndBoardsSQLMigration(t *testing.T) {
ExecFile("./fixtures/test18AddTeamsAndBoardsSQLMigrationFixtures.sql")
board := struct {
ID string
Title string
Type string
ID string
Title string
Type string
Fields string
Description string
Show_Description bool
Is_Template bool
Template_Version int
}{}
// we check first that the board is inside the blocks table as
// a block of board type
err := th.f.DB().Get(&board, "SELECT id, title, type FROM focalboard_blocks WHERE id = 'board-id'")
// a block of board type and columnCalculations exists if Fields
err := th.f.DB().Get(&board, "SELECT id, title, type, fields FROM focalboard_blocks WHERE id = 'board-id'")
require.NoError(t, err)
require.Equal(t, "My Board", board.Title)
require.Equal(t, "board", board.Type)
require.Contains(t, board.Fields, "columnCalculations")
// we check another board is inside the blocks table as
// a block of board type and has several different properties in boards
err = th.f.DB().Get(&board, "SELECT id, title, type, fields FROM focalboard_blocks WHERE id = 'board-id2'")
require.NoError(t, err)
require.Equal(t, "My Board Two", board.Title)
require.Equal(t, "board", board.Type)
require.Contains(t, board.Fields, "description")
require.Contains(t, board.Fields, "showDescription")
require.Contains(t, board.Fields, "isTemplate")
require.Contains(t, board.Fields, "templateVer")
// then we run the migration
th.f.MigrateToStep(18)
// we assert that the board is now a block
// we assert that the board is now in the boards table
bErr := th.f.DB().Get(&board, "SELECT id, title, type FROM focalboard_boards WHERE id = 'board-id'")
require.NoError(t, bErr)
require.Equal(t, "My Board", board.Title)
@ -43,12 +61,46 @@ func Test18AddTeamsAndBoardsSQLMigration(t *testing.T) {
Board_ID string
}{}
// we fetch the card to ensure that the
// we fetch the card to ensure that the card is still in the blocks table
cErr := th.f.DB().Get(&card, "SELECT title, type, parent_id, board_id FROM focalboard_blocks WHERE id = 'card-id'")
require.NoError(t, cErr)
require.Equal(t, "A card", card.Title)
require.Equal(t, "card", card.Type)
require.Equal(t, board.ID, card.Parent_ID)
require.Equal(t, board.ID, card.Board_ID)
// we assert that the board is now a board and properties from JSON Fields
dErr := th.f.DB().Get(&board, "SELECT id, title, type, description, show_description, is_template, template_version FROM focalboard_boards WHERE id = 'board-id2'")
require.NoError(t, dErr)
require.Equal(t, "My Board Two", board.Title)
require.Equal(t, "O", board.Type)
require.Equal(t, "My Description", board.Description)
require.Equal(t, true, board.Show_Description)
require.Equal(t, true, board.Is_Template)
require.Equal(t, 1, board.Template_Version)
view := struct {
Title string
Type string
Parent_ID string
Board_ID string
Fields string
}{}
// we fetch the views to ensure that the calculation columns exist on views
eErr := th.f.DB().Get(&view, "SELECT title, type, parent_id, board_id, fields FROM focalboard_blocks WHERE id = 'view-id'")
require.NoError(t, eErr)
require.Contains(t, view.Fields, "columnCalculations")
var fields map[string]interface{}
// Make sure a valid JSON object
json.Unmarshal([]byte(view.Fields), &fields)
require.NotNil(t, fields["columnCalculations"])
require.NotEmpty(t, fields["columnCalculations"])
// Board View should not have columnCalculations
fErr := th.f.DB().Get(&view, "SELECT title, type, parent_id, board_id, fields FROM focalboard_blocks WHERE id = 'view-id2'")
require.NoError(t, fErr)
require.NotContains(t, view.Fields, "columnCalculations")
})
}

View File

@ -2,7 +2,6 @@ package sqlstore
import (
"database/sql"
"errors"
"fmt"
"net/url"
"strings"
@ -17,9 +16,6 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
//nolint:lll
var ErrInvalidMariaDB = errors.New("MariaDB database is not supported, you can find more information at https://docs.mattermost.com/install/software-hardware-requirements.html#database-software")
// SQLStore is a SQL database.
type SQLStore struct {
db *sql.DB
@ -58,10 +54,6 @@ func New(params Params) (*SQLStore, error) {
servicesAPI: params.ServicesAPI,
}
if store.IsMariaDB() {
return nil, ErrInvalidMariaDB
}
var err error
store.isBinaryParam, err = store.computeBinaryParam()
if err != nil {

View File

@ -42,6 +42,9 @@ func PrepareNewTestDatabase() (dbType string, connectionString string, err error
if dbType == "" {
dbType = model.SqliteDBType
}
if dbType == "mariadb" {
dbType = model.MysqlDBType
}
var dbName string
var rootUser string