dnote/pkg/cli/migrate/migrate_test.go

1220 lines
47 KiB
Go

/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
*
* This file is part of Dnote.
*
* Dnote is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dnote is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/
package migrate
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"gopkg.in/yaml.v2"
"github.com/dnote/actions"
"github.com/dnote/dnote/pkg/assert"
"github.com/dnote/dnote/pkg/cli/consts"
"github.com/dnote/dnote/pkg/cli/context"
"github.com/dnote/dnote/pkg/cli/database"
"github.com/dnote/dnote/pkg/cli/testutils"
"github.com/pkg/errors"
)
// initTestDBNoMigration initializes a test database with schema.sql but removes
// migration version data so tests can control the migration state themselves.
func initTestDBNoMigration(t *testing.T) *database.DB {
db := database.InitTestMemoryDBRaw(t, "")
// Remove migration versions from schema.sql so tests can set their own
database.MustExec(t, "clearing schema versions", db, "DELETE FROM system WHERE key IN (?, ?)", consts.SystemSchema, consts.SystemRemoteSchema)
return db
}
func TestExecute_bump_schema(t *testing.T) {
testCases := []struct {
schemaKey string
}{
{
schemaKey: consts.SystemSchema,
},
{
schemaKey: consts.SystemRemoteSchema,
},
}
for _, tc := range testCases {
func() {
// set up
db := initTestDBNoMigration(t)
ctx := context.InitTestCtxWithDB(t, db)
database.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 8)
m1 := migration{
name: "noop",
run: func(ctx context.DnoteCtx, db *database.DB) error {
return nil
},
}
m2 := migration{
name: "noop",
run: func(ctx context.DnoteCtx, db *database.DB) error {
return nil
},
}
// execute
err := execute(ctx, m1, tc.schemaKey)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to execute"))
}
err = execute(ctx, m2, tc.schemaKey)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to execute"))
}
// test
var schema int
database.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema)
assert.Equal(t, schema, 10, "schema was not incremented properly")
}()
}
}
func TestRun_nonfresh(t *testing.T) {
testCases := []struct {
mode int
schemaKey string
}{
{
mode: LocalMode,
schemaKey: consts.SystemSchema,
},
{
mode: RemoteMode,
schemaKey: consts.SystemRemoteSchema,
},
}
for _, tc := range testCases {
func() {
// set up
db := initTestDBNoMigration(t)
ctx := context.InitTestCtxWithDB(t, db)
database.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 2)
database.MustExec(t, "creating a temporary table for testing", db,
"CREATE TABLE migrate_run_test ( name string )")
sequence := []migration{
{
name: "v1",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1")
return nil
},
},
{
name: "v2",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2")
return nil
},
},
{
name: "v3",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3")
return nil
},
},
{
name: "v4",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v4 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v4")
return nil
},
},
}
// execute
err := Run(ctx, sequence, tc.mode)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to run"))
}
// test
var schema int
database.MustScan(t, fmt.Sprintf("getting schema for %s", tc.schemaKey), db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema)
assert.Equal(t, schema, 4, fmt.Sprintf("schema was not updated for %s", tc.schemaKey))
var testRunCount int
database.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount)
assert.Equal(t, testRunCount, 2, "test run count mismatch")
var testRun1, testRun2 string
database.MustScan(t, "finding test run 1", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v3"), &testRun1)
database.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v4"), &testRun2)
}()
}
}
func TestRun_fresh(t *testing.T) {
testCases := []struct {
mode int
schemaKey string
}{
{
mode: LocalMode,
schemaKey: consts.SystemSchema,
},
{
mode: RemoteMode,
schemaKey: consts.SystemRemoteSchema,
},
}
for _, tc := range testCases {
func() {
// set up
db := initTestDBNoMigration(t)
ctx := context.InitTestCtxWithDB(t, db)
database.MustExec(t, "creating a temporary table for testing", db,
"CREATE TABLE migrate_run_test ( name string )")
sequence := []migration{
{
name: "v1",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1")
return nil
},
},
{
name: "v2",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2")
return nil
},
},
{
name: "v3",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3")
return nil
},
},
}
// execute
err := Run(ctx, sequence, tc.mode)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to run"))
}
// test
var schema int
database.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema)
assert.Equal(t, schema, 3, "schema was not updated")
var testRunCount int
database.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount)
assert.Equal(t, testRunCount, 3, "test run count mismatch")
var testRun1, testRun2, testRun3 string
database.MustScan(t, "finding test run 1", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v1"), &testRun1)
database.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v2"), &testRun2)
database.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v3"), &testRun3)
}()
}
}
func TestRun_up_to_date(t *testing.T) {
testCases := []struct {
mode int
schemaKey string
}{
{
mode: LocalMode,
schemaKey: consts.SystemSchema,
},
{
mode: RemoteMode,
schemaKey: consts.SystemRemoteSchema,
},
}
for _, tc := range testCases {
func() {
// set up
db := initTestDBNoMigration(t)
ctx := context.InitTestCtxWithDB(t, db)
database.MustExec(t, "creating a temporary table for testing", db,
"CREATE TABLE migrate_run_test ( name string )")
database.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 3)
sequence := []migration{
{
name: "v1",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1")
return nil
},
},
{
name: "v2",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2")
return nil
},
},
{
name: "v3",
run: func(ctx context.DnoteCtx, db *database.DB) error {
database.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3")
return nil
},
},
}
// execute
err := Run(ctx, sequence, tc.mode)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to run"))
}
// test
var schema int
database.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema)
assert.Equal(t, schema, 3, "schema was not updated")
var testRunCount int
database.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount)
assert.Equal(t, testRunCount, 0, "test run count mismatch")
}()
}
}
func TestLocalMigration1(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-1-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"})
a1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 1, "add_book", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.EditNoteDataV1{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: "", Content: "note 1"})
a2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 1, "edit_note", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.EditNoteDataV1{NoteUUID: "note-2-uuid", FromBook: "js", ToBook: "", Content: "note 2"})
a3UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 1, "edit_note", string(data), 1537829463)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm1.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var actionCount int
database.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount)
assert.Equal(t, actionCount, 3, "action count mismatch")
var a1, a2, a3 actions.Action
var a1DataRaw, a2DataRaw, a3DataRaw string
database.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID),
&a1.Schema, &a1.Type, &a1DataRaw, &a1.Timestamp)
database.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID),
&a2.Schema, &a2.Type, &a2DataRaw, &a2.Timestamp)
database.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID),
&a3.Schema, &a3.Type, &a3DataRaw, &a3.Timestamp)
var a1Data actions.AddBookDataV1
var a2Data, a3Data actions.EditNoteDataV3
testutils.MustUnmarshalJSON(t, []byte(a1DataRaw), &a1Data)
testutils.MustUnmarshalJSON(t, []byte(a2DataRaw), &a2Data)
testutils.MustUnmarshalJSON(t, []byte(a3DataRaw), &a3Data)
assert.Equal(t, a1.Schema, 1, "a1 schema mismatch")
assert.Equal(t, a1.Type, "add_book", "a1 type mismatch")
assert.Equal(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch")
assert.Equal(t, a1Data.BookName, "js", "a1 data book_name mismatch")
assert.Equal(t, a2.Schema, 3, "a2 schema mismatch")
assert.Equal(t, a2.Type, "edit_note", "a2 type mismatch")
assert.Equal(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch")
assert.Equal(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch")
assert.Equal(t, a2Data.BookName, (*string)(nil), "a2 data book_name mismatch")
assert.Equal(t, *a2Data.Content, "note 1", "a2 data content mismatch")
assert.Equal(t, *a2Data.Public, false, "a2 data public mismatch")
assert.Equal(t, a3.Schema, 3, "a3 schema mismatch")
assert.Equal(t, a3.Type, "edit_note", "a3 type mismatch")
assert.Equal(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch")
assert.Equal(t, a3Data.NoteUUID, "note-2-uuid", "a3 data note_uuid mismatch")
assert.Equal(t, a3Data.BookName, (*string)(nil), "a3 data book_name mismatch")
assert.Equal(t, *a3Data.Content, "note 2", "a3 data content mismatch")
assert.Equal(t, *a3Data.Public, false, "a3 data public mismatch")
}
func TestLocalMigration2(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-1-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
c1 := "note 1 - v1"
c2 := "note 1 - v2"
css := "css"
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css")
data := testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: "note-1-uuid", BookName: "js", Content: "note 1", Public: false})
a1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 2, "add_note", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.EditNoteDataV2{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: nil, Content: &c1, Public: nil})
a2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 2, "edit_note", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.EditNoteDataV2{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: &css, Content: &c2, Public: nil})
a3UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 2, "edit_note", string(data), 1537829463)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm2.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var actionCount int
database.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount)
assert.Equal(t, actionCount, 3, "action count mismatch")
var a1, a2, a3 actions.Action
var a1DataRaw, a2DataRaw, a3DataRaw string
database.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID),
&a1.Schema, &a1.Type, &a1DataRaw, &a1.Timestamp)
database.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID),
&a2.Schema, &a2.Type, &a2DataRaw, &a2.Timestamp)
database.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID),
&a3.Schema, &a3.Type, &a3DataRaw, &a3.Timestamp)
var a1Data actions.AddNoteDataV2
var a2Data, a3Data actions.EditNoteDataV3
testutils.MustUnmarshalJSON(t, []byte(a1DataRaw), &a1Data)
testutils.MustUnmarshalJSON(t, []byte(a2DataRaw), &a2Data)
testutils.MustUnmarshalJSON(t, []byte(a3DataRaw), &a3Data)
assert.Equal(t, a1.Schema, 2, "a1 schema mismatch")
assert.Equal(t, a1.Type, "add_note", "a1 type mismatch")
assert.Equal(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch")
assert.Equal(t, a1Data.NoteUUID, "note-1-uuid", "a1 data note_uuid mismatch")
assert.Equal(t, a1Data.BookName, "js", "a1 data book_name mismatch")
assert.Equal(t, a1Data.Public, false, "a1 data public mismatch")
assert.Equal(t, a2.Schema, 3, "a2 schema mismatch")
assert.Equal(t, a2.Type, "edit_note", "a2 type mismatch")
assert.Equal(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch")
assert.Equal(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch")
assert.Equal(t, a2Data.BookName, (*string)(nil), "a2 data book_name mismatch")
assert.Equal(t, *a2Data.Content, c1, "a2 data content mismatch")
assert.Equal(t, a2Data.Public, (*bool)(nil), "a2 data public mismatch")
assert.Equal(t, a3.Schema, 3, "a3 schema mismatch")
assert.Equal(t, a3.Type, "edit_note", "a3 type mismatch")
assert.Equal(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch")
assert.Equal(t, a3Data.NoteUUID, "note-1-uuid", "a3 data note_uuid mismatch")
assert.Equal(t, *a3Data.BookName, "css", "a3 data book_name mismatch")
assert.Equal(t, *a3Data.Content, c2, "a3 data content mismatch")
assert.Equal(t, a3Data.Public, (*bool)(nil), "a3 data public mismatch")
}
func TestLocalMigration3(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-1-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
data := testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: "note-1-uuid", BookName: "js", Content: "note 1", Public: false})
a1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 2, "add_note", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.RemoveNoteDataV1{NoteUUID: "note-1-uuid", BookName: "js"})
a2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 1, "remove_note", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.RemoveNoteDataV1{NoteUUID: "note-2-uuid", BookName: "js"})
a3UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 1, "remove_note", string(data), 1537829463)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm3.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var actionCount int
database.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount)
assert.Equal(t, actionCount, 3, "action count mismatch")
var a1, a2, a3 actions.Action
var a1DataRaw, a2DataRaw, a3DataRaw string
database.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID),
&a1.Schema, &a1.Type, &a1DataRaw, &a1.Timestamp)
database.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID),
&a2.Schema, &a2.Type, &a2DataRaw, &a2.Timestamp)
database.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID),
&a3.Schema, &a3.Type, &a3DataRaw, &a3.Timestamp)
var a1Data actions.AddNoteDataV2
var a2Data, a3Data actions.RemoveNoteDataV2
testutils.MustUnmarshalJSON(t, []byte(a1DataRaw), &a1Data)
testutils.MustUnmarshalJSON(t, []byte(a2DataRaw), &a2Data)
testutils.MustUnmarshalJSON(t, []byte(a3DataRaw), &a3Data)
assert.Equal(t, a1.Schema, 2, "a1 schema mismatch")
assert.Equal(t, a1.Type, "add_note", "a1 type mismatch")
assert.Equal(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch")
assert.Equal(t, a1Data.NoteUUID, "note-1-uuid", "a1 data note_uuid mismatch")
assert.Equal(t, a1Data.BookName, "js", "a1 data book_name mismatch")
assert.Equal(t, a1Data.Content, "note 1", "a1 data content mismatch")
assert.Equal(t, a1Data.Public, false, "a1 data public mismatch")
assert.Equal(t, a2.Schema, 2, "a2 schema mismatch")
assert.Equal(t, a2.Type, "remove_note", "a2 type mismatch")
assert.Equal(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch")
assert.Equal(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch")
assert.Equal(t, a3.Schema, 2, "a3 schema mismatch")
assert.Equal(t, a3.Type, "remove_note", "a3 type mismatch")
assert.Equal(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch")
assert.Equal(t, a3Data.NoteUUID, "note-2-uuid", "a3 data note_uuid mismatch")
}
func TestLocalMigration4(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-1-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css")
n1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n1UUID, b1UUID, "n1 content", time.Now().UnixNano())
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm4.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var n1Dirty, b1Dirty bool
var n1Deleted, b1Deleted bool
var n1USN, b1USN int
database.MustScan(t, "scanning the newly added dirty flag of n1", db.QueryRow("SELECT dirty, deleted, usn FROM notes WHERE uuid = ?", n1UUID), &n1Dirty, &n1Deleted, &n1USN)
database.MustScan(t, "scanning the newly added dirty flag of b1", db.QueryRow("SELECT dirty, deleted, usn FROM books WHERE uuid = ?", b1UUID), &b1Dirty, &b1Deleted, &b1USN)
assert.Equal(t, n1Dirty, false, "n1 dirty flag should be false by default")
assert.Equal(t, b1Dirty, false, "b1 dirty flag should be false by default")
assert.Equal(t, n1Deleted, false, "n1 deleted flag should be false by default")
assert.Equal(t, b1Deleted, false, "b1 deleted flag should be false by default")
assert.Equal(t, n1USN, 0, "n1 usn flag should be 0 by default")
assert.Equal(t, b1USN, 0, "b1 usn flag should be 0 by default")
}
func TestLocalMigration5(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-5-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css")
b2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting js book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "js")
n1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n1UUID, b1UUID, "n1 content", time.Now().UnixNano())
n2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n2UUID, b1UUID, "n2 content", time.Now().UnixNano())
n3UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n3UUID, b1UUID, "n3 content", time.Now().UnixNano())
data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"})
database.MustExec(t, "inserting a1", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a1-uuid", 1, "add_book", string(data), 1537829463)
data = testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: n1UUID, BookName: "css", Content: "n1 content", Public: false})
database.MustExec(t, "inserting a2", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a2-uuid", 1, "add_note", string(data), 1537829463)
updatedContent := "updated content"
data = testutils.MustMarshalJSON(t, actions.EditNoteDataV3{NoteUUID: n2UUID, BookName: (*string)(nil), Content: &updatedContent, Public: (*bool)(nil)})
database.MustExec(t, "inserting a3", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a3-uuid", 1, "edit_note", string(data), 1537829463)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm5.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var b1Dirty, b2Dirty, n1Dirty, n2Dirty, n3Dirty bool
database.MustScan(t, "scanning the newly added dirty flag of b1", db.QueryRow("SELECT dirty FROM books WHERE uuid = ?", b1UUID), &b1Dirty)
database.MustScan(t, "scanning the newly added dirty flag of b2", db.QueryRow("SELECT dirty FROM books WHERE uuid = ?", b2UUID), &b2Dirty)
database.MustScan(t, "scanning the newly added dirty flag of n1", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n1UUID), &n1Dirty)
database.MustScan(t, "scanning the newly added dirty flag of n2", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n2UUID), &n2Dirty)
database.MustScan(t, "scanning the newly added dirty flag of n3", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n3UUID), &n3Dirty)
assert.Equal(t, b1Dirty, false, "b1 dirty flag should be false by default")
assert.Equal(t, b2Dirty, true, "b2 dirty flag should be false by default")
assert.Equal(t, n1Dirty, true, "n1 dirty flag should be false by default")
assert.Equal(t, n2Dirty, true, "n2 dirty flag should be false by default")
assert.Equal(t, n3Dirty, false, "n3 dirty flag should be false by default")
}
func TestLocalMigration6(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-5-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"})
a1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting action", db,
"INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 1, "add_book", string(data), 1537829463)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm5.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var count int
err = db.QueryRow("SELECT name FROM sqlite_master WHERE type='table' AND name = ?;", "actions").Scan(&count)
assert.Equal(t, count, 0, "actions table should have been deleted")
}
func TestLocalMigration7_trash(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-7-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting trash book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "trash")
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm7.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var b1Label string
var b1Dirty bool
database.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty)
assert.Equal(t, b1Label, "trash (2)", "b1 label was not migrated")
assert.Equal(t, b1Dirty, true, "b1 was not marked dirty")
}
func TestLocalMigration7_conflicts(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-7-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "conflicts")
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm7.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var b1Label string
var b1Dirty bool
database.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty)
assert.Equal(t, b1Label, "conflicts (2)", "b1 label was not migrated")
assert.Equal(t, b1Dirty, true, "b1 was not marked dirty")
}
func TestLocalMigration7_conflicts_dup(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-7-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "conflicts")
b2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "conflicts (2)")
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm7.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var b1Label, b2Label string
var b1Dirty, b2Dirty bool
database.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty)
database.MustScan(t, "scanning b2 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty)
assert.Equal(t, b1Label, "conflicts (3)", "b1 label was not migrated")
assert.Equal(t, b2Label, "conflicts (2)", "b1 label was not migrated")
assert.Equal(t, b1Dirty, true, "b1 was not marked dirty")
assert.Equal(t, b2Dirty, false, "b2 should not have been marked dirty")
}
func TestLocalMigration8(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-8-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1")
n1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting n1", db, `INSERT INTO notes
(id, uuid, book_uuid, content, added_on, edited_on, public, dirty, usn, deleted) VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 1, n1UUID, b1UUID, "n1 Body", 1, 2, true, true, 20, false)
n2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting n2", db, `INSERT INTO notes
(id, uuid, book_uuid, content, added_on, edited_on, public, dirty, usn, deleted) VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 2, n2UUID, b1UUID, "", 3, 4, false, true, 21, true)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm8.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var n1BookUUID, n1Body string
var n1AddedOn, n1EditedOn int64
var n1USN int
var n1Public, n1Dirty, n1Deleted bool
database.MustScan(t, "scanning n1", db.QueryRow("SELECT book_uuid, body, added_on, edited_on, usn, public, dirty, deleted FROM notes WHERE uuid = ?", n1UUID), &n1BookUUID, &n1Body, &n1AddedOn, &n1EditedOn, &n1USN, &n1Public, &n1Dirty, &n1Deleted)
var n2BookUUID, n2Body string
var n2AddedOn, n2EditedOn int64
var n2USN int
var n2Public, n2Dirty, n2Deleted bool
database.MustScan(t, "scanning n2", db.QueryRow("SELECT book_uuid, body, added_on, edited_on, usn, public, dirty, deleted FROM notes WHERE uuid = ?", n2UUID), &n2BookUUID, &n2Body, &n2AddedOn, &n2EditedOn, &n2USN, &n2Public, &n2Dirty, &n2Deleted)
assert.Equal(t, n1BookUUID, b1UUID, "n1 BookUUID mismatch")
assert.Equal(t, n1Body, "n1 Body", "n1 Body mismatch")
assert.Equal(t, n1AddedOn, int64(1), "n1 AddedOn mismatch")
assert.Equal(t, n1EditedOn, int64(2), "n1 EditedOn mismatch")
assert.Equal(t, n1USN, 20, "n1 USN mismatch")
assert.Equal(t, n1Public, true, "n1 Public mismatch")
assert.Equal(t, n1Dirty, true, "n1 Dirty mismatch")
assert.Equal(t, n1Deleted, false, "n1 Deleted mismatch")
assert.Equal(t, n2BookUUID, b1UUID, "n2 BookUUID mismatch")
assert.Equal(t, n2Body, "", "n2 Body mismatch")
assert.Equal(t, n2AddedOn, int64(3), "n2 AddedOn mismatch")
assert.Equal(t, n2EditedOn, int64(4), "n2 EditedOn mismatch")
assert.Equal(t, n2USN, 21, "n2 USN mismatch")
assert.Equal(t, n2Public, false, "n2 Public mismatch")
assert.Equal(t, n2Dirty, true, "n2 Dirty mismatch")
assert.Equal(t, n2Deleted, true, "n2 Deleted mismatch")
}
func TestLocalMigration9(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-9-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1")
n1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting n1", db, `INSERT INTO notes
(uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)`, n1UUID, b1UUID, "n1 Body", 1, 2, true, true, 20, false)
n2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting n2", db, `INSERT INTO notes
(uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)`, n2UUID, b1UUID, "n2 Body", 3, 4, false, true, 21, false)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm9.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
// assert that note_fts was populated with correct values
var noteFtsCount int
database.MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts;"), &noteFtsCount)
assert.Equal(t, noteFtsCount, 2, "noteFtsCount mismatch")
var resCount int
database.MustScan(t, "counting result", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "n1"), &resCount)
assert.Equal(t, resCount, 1, "noteFtsCount mismatch")
}
func TestLocalMigration10(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-10-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book ", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "123")
b2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "123 javascript")
b3UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 3", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b3UUID, "foo")
b4UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 4", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b4UUID, "+123")
b5UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 5", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b5UUID, "0123")
b6UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 6", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b6UUID, "javascript 123")
b7UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 7", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b7UUID, "123 (1)")
b8UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 8", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b8UUID, "5")
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm10.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
// assert that note_fts was populated with correct values
var b1Label, b2Label, b3Label, b4Label, b5Label, b6Label, b7Label, b8Label string
var b1Dirty, b2Dirty, b3Dirty, b4Dirty, b5Dirty, b6Dirty, b7Dirty, b8Dirty bool
database.MustScan(t, "getting b1", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty)
database.MustScan(t, "getting b2", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty)
database.MustScan(t, "getting b3", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b3UUID), &b3Label, &b3Dirty)
database.MustScan(t, "getting b4", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b4UUID), &b4Label, &b4Dirty)
database.MustScan(t, "getting b5", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b5UUID), &b5Label, &b5Dirty)
database.MustScan(t, "getting b6", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b6UUID), &b6Label, &b6Dirty)
database.MustScan(t, "getting b7", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b7UUID), &b7Label, &b7Dirty)
database.MustScan(t, "getting b8", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b8UUID), &b8Label, &b8Dirty)
assert.Equal(t, b1Label, "123 (2)", "b1Label mismatch")
assert.Equal(t, b1Dirty, true, "b1Dirty mismatch")
assert.Equal(t, b2Label, "123 javascript", "b2Label mismatch")
assert.Equal(t, b2Dirty, false, "b2Dirty mismatch")
assert.Equal(t, b3Label, "foo", "b3Label mismatch")
assert.Equal(t, b3Dirty, false, "b3Dirty mismatch")
assert.Equal(t, b4Label, "+123", "b4Label mismatch")
assert.Equal(t, b4Dirty, false, "b4Dirty mismatch")
assert.Equal(t, b5Label, "0123 (1)", "b5Label mismatch")
assert.Equal(t, b5Dirty, true, "b5Dirty mismatch")
assert.Equal(t, b6Label, "javascript 123", "b6Label mismatch")
assert.Equal(t, b6Dirty, false, "b6Dirty mismatch")
assert.Equal(t, b7Label, "123 (1)", "b7Label mismatch")
assert.Equal(t, b7Dirty, false, "b7Dirty mismatch")
assert.Equal(t, b8Label, "5 (1)", "b8Label mismatch")
assert.Equal(t, b8Dirty, true, "b8Dirty mismatch")
}
func TestLocalMigration11(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-11-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "foo")
b2UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "bar baz")
b3UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 3", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b3UUID, "quz qux")
b4UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 4", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b4UUID, "quz_qux")
b5UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 5", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b5UUID, "foo bar baz quz 123")
b6UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 6", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b6UUID, "foo_bar baz")
b7UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 7", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b7UUID, "cool ideas")
b8UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 8", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b8UUID, "cool_ideas")
b9UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book 9", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b9UUID, "cool_ideas_2")
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm11.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test
var bookCount int
database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount)
assert.Equal(t, bookCount, 9, "bookCount mismatch")
// assert that note_fts was populated with correct values
var b1Label, b2Label, b3Label, b4Label, b5Label, b6Label, b7Label, b8Label, b9Label string
var b1Dirty, b2Dirty, b3Dirty, b4Dirty, b5Dirty, b6Dirty, b7Dirty, b8Dirty, b9Dirty bool
database.MustScan(t, "getting b1", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty)
database.MustScan(t, "getting b2", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty)
database.MustScan(t, "getting b3", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b3UUID), &b3Label, &b3Dirty)
database.MustScan(t, "getting b4", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b4UUID), &b4Label, &b4Dirty)
database.MustScan(t, "getting b5", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b5UUID), &b5Label, &b5Dirty)
database.MustScan(t, "getting b6", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b6UUID), &b6Label, &b6Dirty)
database.MustScan(t, "getting b7", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b7UUID), &b7Label, &b7Dirty)
database.MustScan(t, "getting b8", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b8UUID), &b8Label, &b8Dirty)
database.MustScan(t, "getting b9", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b9UUID), &b9Label, &b9Dirty)
assert.Equal(t, b1Label, "foo", "b1Label mismatch")
assert.Equal(t, b1Dirty, false, "b1Dirty mismatch")
assert.Equal(t, b2Label, "bar_baz", "b2Label mismatch")
assert.Equal(t, b2Dirty, true, "b2Dirty mismatch")
assert.Equal(t, b3Label, "quz_qux_2", "b3Label mismatch")
assert.Equal(t, b3Dirty, true, "b3Dirty mismatch")
assert.Equal(t, b4Label, "quz_qux", "b4Label mismatch")
assert.Equal(t, b4Dirty, false, "b4Dirty mismatch")
assert.Equal(t, b5Label, "foo_bar_baz_quz_123", "b5Label mismatch")
assert.Equal(t, b5Dirty, true, "b5Dirty mismatch")
assert.Equal(t, b6Label, "foo_bar_baz", "b6Label mismatch")
assert.Equal(t, b6Dirty, true, "b6Dirty mismatch")
assert.Equal(t, b7Label, "cool_ideas_3", "b7Label mismatch")
assert.Equal(t, b7Dirty, true, "b7Dirty mismatch")
assert.Equal(t, b8Label, "cool_ideas", "b8Label mismatch")
assert.Equal(t, b8Dirty, false, "b8Dirty mismatch")
assert.Equal(t, b9Label, "cool_ideas_2", "b9Label mismatch")
assert.Equal(t, b9Dirty, false, "b9Dirty mismatch")
}
func TestLocalMigration12(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-12-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
data := []byte("editor: vim")
path := fmt.Sprintf("%s/%s/dnoterc", ctx.Paths.Config, consts.DnoteDirName)
if err := os.WriteFile(path, data, 0644); err != nil {
t.Fatal(errors.Wrap(err, "Failed to write schema file"))
}
// execute
err := lm12.run(ctx, nil)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to run"))
}
// test
b, err := os.ReadFile(path)
if err != nil {
t.Fatal(errors.Wrap(err, "reading config"))
}
type config struct {
APIEndpoint string `yaml:"apiEndpoint"`
}
var cf config
err = yaml.Unmarshal(b, &cf)
if err != nil {
t.Fatal(errors.Wrap(err, "unmarshalling config"))
}
assert.NotEqual(t, cf.APIEndpoint, "", "apiEndpoint was not populated")
}
func TestLocalMigration13(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-12-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
data := []byte("editor: vim\napiEndpoint: https://test.com/api")
path := fmt.Sprintf("%s/%s/dnoterc", ctx.Paths.Config, consts.DnoteDirName)
if err := os.WriteFile(path, data, 0644); err != nil {
t.Fatal(errors.Wrap(err, "Failed to write schema file"))
}
// execute
err := lm13.run(ctx, nil)
if err != nil {
t.Fatal(errors.Wrap(err, "failed to run"))
}
// test
b, err := os.ReadFile(path)
if err != nil {
t.Fatal(errors.Wrap(err, "reading config"))
}
type config struct {
Editor string `yaml:"editor"`
ApiEndpoint string `yaml:"apiEndpoint"`
EnableUpgradeCheck bool `yaml:"enableUpgradeCheck"`
}
var cf config
err = yaml.Unmarshal(b, &cf)
if err != nil {
t.Fatal(errors.Wrap(err, "unmarshalling config"))
}
assert.Equal(t, cf.Editor, "vim", "editor mismatch")
assert.Equal(t, cf.ApiEndpoint, "https://test.com/api", "apiEndpoint mismatch")
assert.Equal(t, cf.EnableUpgradeCheck, true, "enableUpgradeCheck mismatch")
}
func TestLocalMigration14(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/local-14-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
b1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1")
n1UUID := testutils.MustGenerateUUID(t)
database.MustExec(t, "inserting note", db, `INSERT INTO notes
(uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?)`, n1UUID, b1UUID, "test note", 1, 2, true, false, 0, false)
// Execute
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = lm14.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// Test - verify public column was dropped by checking column names
rows, err := db.Query("SELECT name FROM pragma_table_info('notes')")
if err != nil {
t.Fatal(errors.Wrap(err, "getting table info"))
}
defer rows.Close()
for rows.Next() {
var name string
err := rows.Scan(&name)
if err != nil {
t.Fatal(errors.Wrap(err, "scanning column name"))
}
if name == "public" {
t.Fatal("public column still exists after migration")
}
}
}
func TestRemoteMigration1(t *testing.T) {
// set up
db := database.InitTestMemoryDBRaw(t, "./fixtures/remote-1-pre-schema.sql")
ctx := context.InitTestCtxWithDB(t, db)
testutils.Login(t, &ctx)
JSBookUUID := "existing-js-book-uuid"
CSSBookUUID := "existing-css-book-uuid"
linuxBookUUID := "existing-linux-book-uuid"
newJSBookUUID := "new-js-book-uuid"
newCSSBookUUID := "new-css-book-uuid"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/v3/books" {
res := []struct {
UUID string `json:"uuid"`
Label string `json:"label"`
}{
{
UUID: newJSBookUUID,
Label: "js",
},
{
UUID: newCSSBookUUID,
Label: "css",
},
// book that only exists on the server. client must ignore.
{
UUID: "golang-book-uuid",
Label: "golang",
},
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(res); err != nil {
t.Fatal(errors.Wrap(err, "encoding response"))
}
}
}))
defer server.Close()
ctx.APIEndpoint = server.URL
database.MustExec(t, "inserting js book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", JSBookUUID, "js")
database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", CSSBookUUID, "css")
database.MustExec(t, "inserting linux book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", linuxBookUUID, "linux")
database.MustExec(t, "inserting sessionKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKey, "someSessionKey")
database.MustExec(t, "inserting sessionKeyExpiry", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKeyExpiry, time.Now().Add(24*time.Hour).Unix())
tx, err := db.Begin()
if err != nil {
t.Fatal(errors.Wrap(err, "beginning a transaction"))
}
err = rm1.run(ctx, tx)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "failed to run"))
}
tx.Commit()
// test
var postJSBookUUID, postCSSBookUUID, postLinuxBookUUID string
database.MustScan(t, "getting js book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "js"), &postJSBookUUID)
database.MustScan(t, "getting css book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "css"), &postCSSBookUUID)
database.MustScan(t, "getting linux book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "linux"), &postLinuxBookUUID)
assert.Equal(t, postJSBookUUID, newJSBookUUID, "js book uuid was not updated correctly")
assert.Equal(t, postCSSBookUUID, newCSSBookUUID, "css book uuid was not updated correctly")
assert.Equal(t, postLinuxBookUUID, linuxBookUUID, "linux book uuid changed")
}