mirror of
https://github.com/dnote/dnote
synced 2026-03-15 15:05:51 +01:00
* Add healthcheck for Docker * Prevent nil pointer if endpoint is wrong * Converge if using same book names while syncing
4554 lines
220 KiB
Go
4554 lines
220 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 main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dnote/dnote/pkg/assert"
|
|
"github.com/dnote/dnote/pkg/cli/consts"
|
|
"github.com/dnote/dnote/pkg/cli/context"
|
|
cliDatabase "github.com/dnote/dnote/pkg/cli/database"
|
|
"github.com/dnote/dnote/pkg/cli/testutils"
|
|
clitest "github.com/dnote/dnote/pkg/cli/testutils"
|
|
"github.com/dnote/dnote/pkg/clock"
|
|
"github.com/dnote/dnote/pkg/server/app"
|
|
"github.com/dnote/dnote/pkg/server/controllers"
|
|
"github.com/dnote/dnote/pkg/server/database"
|
|
"github.com/dnote/dnote/pkg/server/mailer"
|
|
apitest "github.com/dnote/dnote/pkg/server/testutils"
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var cliBinaryName string
|
|
var server *httptest.Server
|
|
var serverDb *gorm.DB
|
|
var serverTime = time.Date(2017, time.March, 14, 21, 15, 0, 0, time.UTC)
|
|
|
|
var tmpDirPath string
|
|
var dnoteCmdOpts clitest.RunDnoteCmdOptions
|
|
var paths context.Paths
|
|
|
|
var testDir = "./tmp/.dnote"
|
|
|
|
func init() {
|
|
tmpDirPath = fmt.Sprintf("%s/tmp", testDir)
|
|
cliBinaryName = fmt.Sprintf("%s/test/cli/test-cli", testDir)
|
|
dnoteCmdOpts = clitest.RunDnoteCmdOptions{
|
|
Env: []string{
|
|
fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpDirPath),
|
|
fmt.Sprintf("XDG_DATA_HOME=%s", tmpDirPath),
|
|
fmt.Sprintf("XDG_CACHE_HOME=%s", tmpDirPath),
|
|
},
|
|
}
|
|
|
|
paths = context.Paths{
|
|
Data: tmpDirPath,
|
|
Cache: tmpDirPath,
|
|
Config: tmpDirPath,
|
|
}
|
|
}
|
|
|
|
func clearTmp(t *testing.T) {
|
|
if err := os.RemoveAll(tmpDirPath); err != nil {
|
|
t.Fatal("cleaning tmp dir")
|
|
}
|
|
}
|
|
|
|
// setupTestServer creates a test server with its own database
|
|
func setupTestServer(dbPath string, serverTime time.Time) (*httptest.Server, *gorm.DB, error) {
|
|
db := apitest.InitDB(dbPath)
|
|
|
|
mockClock := clock.NewMock()
|
|
mockClock.SetNow(serverTime)
|
|
|
|
a := app.NewTest()
|
|
a.Clock = mockClock
|
|
a.EmailTemplates = mailer.Templates{}
|
|
a.EmailBackend = &apitest.MockEmailbackendImplementation{}
|
|
a.DB = db
|
|
|
|
server, err := controllers.NewServer(&a)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrap(err, "initializing server")
|
|
}
|
|
|
|
return server, db, nil
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Set up server database - use file-based DB for e2e tests
|
|
dbPath := fmt.Sprintf("%s/server.db", testDir)
|
|
|
|
var err error
|
|
server, serverDb, err = setupTestServer(dbPath, serverTime)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer server.Close()
|
|
|
|
// Build binaries
|
|
apiEndpoint := fmt.Sprintf("%s/api", server.URL)
|
|
ldflags := fmt.Sprintf("-X main.apiEndpoint=%s", apiEndpoint)
|
|
|
|
cmd := exec.Command("go", "build", "--tags", "fts5", "-o", cliBinaryName, "-ldflags", ldflags, "github.com/dnote/dnote/pkg/cli")
|
|
|
|
var stderr bytes.Buffer
|
|
cmd.Stderr = &stderr
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
log.Print(errors.Wrap(err, "building a CLI binary").Error())
|
|
log.Print(stderr.String())
|
|
os.Exit(1)
|
|
}
|
|
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
// helpers
|
|
func setupUser(t *testing.T, db *cliDatabase.DB) database.User {
|
|
user := apitest.SetupUserData(serverDb)
|
|
apitest.SetupAccountData(serverDb, user, "alice@example.com", "pass1234")
|
|
|
|
return user
|
|
}
|
|
|
|
func setupUserAndLogin(t *testing.T, db *cliDatabase.DB) database.User {
|
|
user := setupUser(t, db)
|
|
login(t, db, user)
|
|
|
|
return user
|
|
}
|
|
|
|
// log in the user in CLI
|
|
func login(t *testing.T, db *cliDatabase.DB, user database.User) {
|
|
session := apitest.SetupSession(serverDb, user)
|
|
|
|
cliDatabase.MustExec(t, "inserting session_key", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKey, session.Key)
|
|
cliDatabase.MustExec(t, "inserting session_key_expiry", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKeyExpiry, session.ExpiresAt.Unix())
|
|
}
|
|
|
|
func apiCreateBook(t *testing.T, user database.User, name, message string) string {
|
|
res := doHTTPReq(t, "POST", "/v3/books", fmt.Sprintf(`{"name": "%s"}`, name), message, user)
|
|
|
|
var resp controllers.CreateBookResp
|
|
if err := json.NewDecoder(res.Body).Decode(&resp); err != nil {
|
|
t.Fatal(errors.Wrap(err, "decoding payload for adding book"))
|
|
return ""
|
|
}
|
|
|
|
return resp.Book.UUID
|
|
}
|
|
|
|
func apiPatchBook(t *testing.T, user database.User, uuid, payload, message string) {
|
|
doHTTPReq(t, "PATCH", fmt.Sprintf("/v3/books/%s", uuid), payload, message, user)
|
|
}
|
|
|
|
func apiDeleteBook(t *testing.T, user database.User, uuid, message string) {
|
|
doHTTPReq(t, "DELETE", fmt.Sprintf("/v3/books/%s", uuid), "", message, user)
|
|
}
|
|
|
|
func apiCreateNote(t *testing.T, user database.User, bookUUID, body, message string) string {
|
|
res := doHTTPReq(t, "POST", "/v3/notes", fmt.Sprintf(`{"book_uuid": "%s", "content": "%s"}`, bookUUID, body), message, user)
|
|
|
|
var resp controllers.CreateNoteResp
|
|
if err := json.NewDecoder(res.Body).Decode(&resp); err != nil {
|
|
t.Fatal(errors.Wrap(err, "decoding payload for adding note"))
|
|
return ""
|
|
}
|
|
|
|
return resp.Result.UUID
|
|
}
|
|
|
|
func apiPatchNote(t *testing.T, user database.User, noteUUID, payload, message string) {
|
|
doHTTPReq(t, "PATCH", fmt.Sprintf("/v3/notes/%s", noteUUID), payload, message, user)
|
|
}
|
|
|
|
func apiDeleteNote(t *testing.T, user database.User, noteUUID, message string) {
|
|
doHTTPReq(t, "DELETE", fmt.Sprintf("/v3/notes/%s", noteUUID), "", message, user)
|
|
}
|
|
|
|
func doHTTPReq(t *testing.T, method, path, payload, message string, user database.User) *http.Response {
|
|
apiEndpoint := fmt.Sprintf("%s/api", server.URL)
|
|
endpoint := fmt.Sprintf("%s%s", apiEndpoint, path)
|
|
|
|
req, err := http.NewRequest(method, endpoint, strings.NewReader(payload))
|
|
if err != nil {
|
|
panic(errors.Wrap(err, "constructing http request"))
|
|
}
|
|
|
|
res := apitest.HTTPAuthDo(t, serverDb, req, user)
|
|
if res.StatusCode >= 400 {
|
|
bs, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
panic(errors.Wrap(err, "parsing response body for error"))
|
|
}
|
|
|
|
t.Errorf("%s. HTTP status %d. Message: %s", message, res.StatusCode, string(bs))
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
type setupFunc func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string
|
|
type assertFunc func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string)
|
|
|
|
func testSyncCmd(t *testing.T, fullSync bool, setup setupFunc, assert assertFunc) {
|
|
// clean up
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
ids := setup(t, ctx, user)
|
|
|
|
if fullSync {
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "-f")
|
|
} else {
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
}
|
|
|
|
assert(t, ctx, user, ids)
|
|
}
|
|
|
|
type systemState struct {
|
|
clientNoteCount int
|
|
clientBookCount int
|
|
clientLastMaxUSN int
|
|
clientLastSyncAt int64
|
|
serverNoteCount int64
|
|
serverBookCount int64
|
|
serverUserMaxUSN int
|
|
}
|
|
|
|
// checkState compares the state of the client and the server with the given system state
|
|
func checkState(t *testing.T, ctx context.DnoteCtx, user database.User, expected systemState) {
|
|
checkStateWithDB(t, ctx.DB, user, serverDb, expected)
|
|
}
|
|
|
|
func checkStateWithDB(t *testing.T, clientDB *cliDatabase.DB, user database.User, serverDB *gorm.DB, expected systemState) {
|
|
var clientBookCount, clientNoteCount int
|
|
cliDatabase.MustScan(t, "counting client notes", clientDB.QueryRow("SELECT count(*) FROM notes"), &clientNoteCount)
|
|
cliDatabase.MustScan(t, "counting client books", clientDB.QueryRow("SELECT count(*) FROM books"), &clientBookCount)
|
|
assert.Equal(t, clientNoteCount, expected.clientNoteCount, "client note count mismatch")
|
|
assert.Equal(t, clientBookCount, expected.clientBookCount, "client book count mismatch")
|
|
|
|
var clientLastMaxUSN int
|
|
var clientLastSyncAt int64
|
|
cliDatabase.MustScan(t, "finding system last_max_usn", clientDB.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastMaxUSN), &clientLastMaxUSN)
|
|
cliDatabase.MustScan(t, "finding system last_sync_at", clientDB.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastSyncAt), &clientLastSyncAt)
|
|
assert.Equal(t, clientLastMaxUSN, expected.clientLastMaxUSN, "client last_max_usn mismatch")
|
|
assert.Equal(t, clientLastSyncAt, expected.clientLastSyncAt, "client last_sync_at mismatch")
|
|
|
|
var serverBookCount, serverNoteCount int64
|
|
apitest.MustExec(t, serverDB.Model(&database.Note{}).Count(&serverNoteCount), "counting server notes")
|
|
apitest.MustExec(t, serverDB.Model(&database.Book{}).Count(&serverBookCount), "counting api notes")
|
|
assert.Equal(t, serverNoteCount, expected.serverNoteCount, "server note count mismatch")
|
|
assert.Equal(t, serverBookCount, expected.serverBookCount, "server book count mismatch")
|
|
var serverUser database.User
|
|
apitest.MustExec(t, serverDB.Where("id = ?", user.ID).First(&serverUser), "finding user")
|
|
assert.Equal(t, serverUser.MaxUSN, expected.serverUserMaxUSN, "user max_usn mismatch")
|
|
}
|
|
|
|
// tests
|
|
func TestSync_Empty(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
return map[string]string{}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
// Test
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 0,
|
|
clientLastMaxUSN: 0,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 0,
|
|
serverBookCount: 0,
|
|
serverUserMaxUSN: 0,
|
|
})
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
}
|
|
|
|
func TestSync_oneway(t *testing.T) {
|
|
t.Run("cli to api only", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) {
|
|
apitest.MustExec(t, serverDb.Model(&user).Update("max_usn", 0), "updating user max_usn")
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js2")
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User) {
|
|
cliDB := ctx.DB
|
|
|
|
// test client
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 3,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 5,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 3,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 5,
|
|
})
|
|
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
var cliNote1JS, cliNote2JS, cliNote1CSS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cli book js", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cli book css", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js2"), &cliNote2JS.UUID, &cliNote2JS.Body, &cliNote2JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliNote2JS.USN, 0, "cliNote2JS USN mismatch")
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS USN mismatch")
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote2JS.Body, "js2", "cliNote2JS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote2JS.Deleted, false, "cliNote2JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
|
|
// test server
|
|
var apiBookJS, apiBookCSS database.Book
|
|
var apiNote1JS, apiNote2JS, apiNote1CSS database.Note
|
|
apitest.MustExec(t, serverDb.Model(&database.Note{}).Where("uuid = ?", cliNote1JS.UUID).First(&apiNote1JS), "getting js1 note")
|
|
apitest.MustExec(t, serverDb.Model(&database.Note{}).Where("uuid = ?", cliNote2JS.UUID).First(&apiNote2JS), "getting js2 note")
|
|
apitest.MustExec(t, serverDb.Model(&database.Note{}).Where("uuid = ?", cliNote1CSS.UUID).First(&apiNote1CSS), "getting css1 note")
|
|
apitest.MustExec(t, serverDb.Model(&database.Book{}).Where("uuid = ?", cliBookJS.UUID).First(&apiBookJS), "getting js book")
|
|
apitest.MustExec(t, serverDb.Model(&database.Book{}).Where("uuid = ?", cliBookCSS.UUID).First(&apiBookCSS), "getting css book")
|
|
|
|
// assert usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote2JS.USN, 0, "apiNote2JS usn mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS usn mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS usn mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS usn mismatch")
|
|
// client must have generated uuids
|
|
assert.NotEqual(t, apiNote1JS.UUID, "", "apiNote1JS UUID mismatch")
|
|
assert.NotEqual(t, apiNote2JS.UUID, "", "apiNote2JS UUID mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.UUID, "", "apiNote1CSS UUID mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote2JS.Deleted, false, "apiNote2JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS.Deleted, false, "apiNote1CSS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
// assert on body and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote2JS.Body, "js2", "apiNote2JS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
}
|
|
|
|
t.Run("stepSync", func(t *testing.T) {
|
|
clearTmp(t)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
setup(t, ctx, user)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
assert(t, ctx, user)
|
|
})
|
|
|
|
t.Run("fullSync", func(t *testing.T) {
|
|
clearTmp(t)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
setup(t, ctx, user)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "-f")
|
|
|
|
assert(t, ctx, user)
|
|
})
|
|
})
|
|
|
|
t.Run("cli to api with edit and delete", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) {
|
|
apiDB := serverDb
|
|
apitest.MustExec(t, apiDB.Model(&user).Update("max_usn", 0), "updating user max_usn")
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js2")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js3")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css2")
|
|
|
|
var nid, nid2 string
|
|
cliDB := ctx.DB
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js3"), &nid)
|
|
cliDatabase.MustScan(t, "getting id of note to delete", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "css2"), &nid2)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", nid, "-c", "js3-edited")
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveNote, cliBinaryName, "remove", "css", nid2)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css3")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css4")
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 6,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 6,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// test cli
|
|
var cliN1, cliN2, cliN3, cliN4, cliN5, cliN6 cliDatabase.Note
|
|
var cliB1, cliB2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliN1", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliN1.UUID, &cliN1.Body, &cliN1.USN)
|
|
cliDatabase.MustScan(t, "finding cliN2", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js2"), &cliN2.UUID, &cliN2.Body, &cliN2.USN)
|
|
cliDatabase.MustScan(t, "finding cliN3", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js3-edited"), &cliN3.UUID, &cliN3.Body, &cliN3.USN)
|
|
cliDatabase.MustScan(t, "finding cliN4", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliN4.UUID, &cliN4.Body, &cliN4.USN)
|
|
cliDatabase.MustScan(t, "finding cliN5", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css3"), &cliN5.UUID, &cliN5.Body, &cliN5.USN)
|
|
cliDatabase.MustScan(t, "finding cliN6", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css4"), &cliN6.UUID, &cliN6.Body, &cliN6.USN)
|
|
cliDatabase.MustScan(t, "finding cliB1", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliB1.UUID, &cliB1.Label, &cliB1.USN)
|
|
cliDatabase.MustScan(t, "finding cliB2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliB2.UUID, &cliB2.Label, &cliB2.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliN1.USN, 0, "cliN1 USN mismatch")
|
|
assert.NotEqual(t, cliN2.USN, 0, "cliN2 USN mismatch")
|
|
assert.NotEqual(t, cliN3.USN, 0, "cliN3 USN mismatch")
|
|
assert.NotEqual(t, cliN4.USN, 0, "cliN4 USN mismatch")
|
|
assert.NotEqual(t, cliN5.USN, 0, "cliN5 USN mismatch")
|
|
assert.NotEqual(t, cliN6.USN, 0, "cliN6 USN mismatch")
|
|
assert.NotEqual(t, cliB1.USN, 0, "cliB1 USN mismatch")
|
|
assert.NotEqual(t, cliB2.USN, 0, "cliB2 USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliN1.Body, "js1", "cliN1 Body mismatch")
|
|
assert.Equal(t, cliN2.Body, "js2", "cliN2 Body mismatch")
|
|
assert.Equal(t, cliN3.Body, "js3-edited", "cliN3 Body mismatch")
|
|
assert.Equal(t, cliN4.Body, "css1", "cliN4 Body mismatch")
|
|
assert.Equal(t, cliN5.Body, "css3", "cliN5 Body mismatch")
|
|
assert.Equal(t, cliN6.Body, "css4", "cliN6 Body mismatch")
|
|
assert.Equal(t, cliB1.Label, "js", "cliB1 Label mismatch")
|
|
assert.Equal(t, cliB2.Label, "css", "cliB2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliN1.Deleted, false, "cliN1 Deleted mismatch")
|
|
assert.Equal(t, cliN2.Deleted, false, "cliN2 Deleted mismatch")
|
|
assert.Equal(t, cliN3.Deleted, false, "cliN3 Deleted mismatch")
|
|
assert.Equal(t, cliN4.Deleted, false, "cliN4 Deleted mismatch")
|
|
assert.Equal(t, cliN5.Deleted, false, "cliN5 Deleted mismatch")
|
|
assert.Equal(t, cliN6.Deleted, false, "cliN6 Deleted mismatch")
|
|
assert.Equal(t, cliB1.Deleted, false, "cliB1 Deleted mismatch")
|
|
assert.Equal(t, cliB2.Deleted, false, "cliB2 Deleted mismatch")
|
|
|
|
// test api
|
|
var apiN1, apiN2, apiN3, apiN4, apiN5, apiN6 database.Note
|
|
var apiB1, apiB2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliN1.UUID).First(&apiN1), "finding apiN1")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliN2.UUID).First(&apiN2), "finding apiN2")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliN3.UUID).First(&apiN3), "finding apiN3")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliN4.UUID).First(&apiN4), "finding apiN4")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliN5.UUID).First(&apiN5), "finding apiN5")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliN6.UUID).First(&apiN6), "finding apiN6")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliB1.UUID).First(&apiB1), "finding apiB1")
|
|
apitest.MustExec(t, apiDB.Where("uuid = ?", cliB2.UUID).First(&apiB2), "finding apiB2")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiN1.USN, 0, "apiN1 usn mismatch")
|
|
assert.NotEqual(t, apiN2.USN, 0, "apiN2 usn mismatch")
|
|
assert.NotEqual(t, apiN3.USN, 0, "apiN3 usn mismatch")
|
|
assert.NotEqual(t, apiN4.USN, 0, "apiN4 usn mismatch")
|
|
assert.NotEqual(t, apiN5.USN, 0, "apiN5 usn mismatch")
|
|
assert.NotEqual(t, apiN6.USN, 0, "apiN6 usn mismatch")
|
|
assert.NotEqual(t, apiB1.USN, 0, "apiB1 usn mismatch")
|
|
assert.NotEqual(t, apiB2.USN, 0, "apiB2 usn mismatch")
|
|
// client must have generated uuids
|
|
assert.NotEqual(t, apiN1.UUID, "", "apiN1 UUID mismatch")
|
|
assert.NotEqual(t, apiN2.UUID, "", "apiN2 UUID mismatch")
|
|
assert.NotEqual(t, apiN3.UUID, "", "apiN3 UUID mismatch")
|
|
assert.NotEqual(t, apiN4.UUID, "", "apiN4 UUID mismatch")
|
|
assert.NotEqual(t, apiN5.UUID, "", "apiN5 UUID mismatch")
|
|
assert.NotEqual(t, apiN6.UUID, "", "apiN6 UUID mismatch")
|
|
assert.NotEqual(t, apiB1.UUID, "", "apiB1 UUID mismatch")
|
|
assert.NotEqual(t, apiB2.UUID, "", "apiB2 UUID mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiN1.Deleted, false, "apiN1 Deleted mismatch")
|
|
assert.Equal(t, apiN2.Deleted, false, "apiN2 Deleted mismatch")
|
|
assert.Equal(t, apiN3.Deleted, false, "apiN3 Deleted mismatch")
|
|
assert.Equal(t, apiN4.Deleted, false, "apiN4 Deleted mismatch")
|
|
assert.Equal(t, apiN5.Deleted, false, "apiN5 Deleted mismatch")
|
|
assert.Equal(t, apiN6.Deleted, false, "apiN6 Deleted mismatch")
|
|
assert.Equal(t, apiB1.Deleted, false, "apiB1 Deleted mismatch")
|
|
assert.Equal(t, apiB2.Deleted, false, "apiB2 Deleted mismatch")
|
|
// assert on body and labels
|
|
assert.Equal(t, apiN1.Body, "js1", "apiN1 Body mismatch")
|
|
assert.Equal(t, apiN2.Body, "js2", "apiN2 Body mismatch")
|
|
assert.Equal(t, apiN3.Body, "js3-edited", "apiN3 Body mismatch")
|
|
assert.Equal(t, apiN4.Body, "css1", "apiN4 Body mismatch")
|
|
assert.Equal(t, apiN5.Body, "css3", "apiN5 Body mismatch")
|
|
assert.Equal(t, apiN6.Body, "css4", "apiN6 Body mismatch")
|
|
assert.Equal(t, apiB1.Label, "js", "apiB1 Label mismatch")
|
|
assert.Equal(t, apiB2.Label, "css", "apiB2 Label mismatch")
|
|
}
|
|
|
|
t.Run("stepSync", func(t *testing.T) {
|
|
clearTmp(t)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
setup(t, ctx, user)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
assert(t, ctx, user)
|
|
})
|
|
|
|
t.Run("fullSync", func(t *testing.T) {
|
|
clearTmp(t)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
setup(t, ctx, user)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "-f")
|
|
|
|
assert(t, ctx, user)
|
|
})
|
|
})
|
|
|
|
t.Run("api to cli", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
apiDB := serverDb
|
|
|
|
apitest.MustExec(t, apiDB.Model(&user).Update("max_usn", 0), "updating user max_usn")
|
|
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding css book")
|
|
cssNote1UUID := apiCreateNote(t, user, cssBookUUID, "css1", "adding css note 1")
|
|
jsNote2UUID := apiCreateNote(t, user, jsBookUUID, "js2", "adding js note 2")
|
|
cssNote2UUID := apiCreateNote(t, user, cssBookUUID, "css2", "adding css note 2")
|
|
linuxBookUUID := apiCreateBook(t, user, "linux", "adding linux book")
|
|
linuxNote1UUID := apiCreateNote(t, user, linuxBookUUID, "linux1", "adding linux note 1")
|
|
apiPatchNote(t, user, jsNote2UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, linuxBookUUID), "moving js note 2 to linux")
|
|
apiDeleteNote(t, user, jsNote1UUID, "deleting js note 1")
|
|
cssNote3UUID := apiCreateNote(t, user, cssBookUUID, "css3", "adding css note 3")
|
|
bashBookUUID := apiCreateBook(t, user, "bash", "adding bash book")
|
|
bashNote1UUID := apiCreateNote(t, user, bashBookUUID, "bash1", "adding bash note 1")
|
|
|
|
// delete the linux book and its two notes
|
|
apiDeleteBook(t, user, linuxBookUUID, "deleting linux book")
|
|
|
|
apiPatchNote(t, user, cssNote2UUID, fmt.Sprintf(`{"content": "%s"}`, "css2-edited"), "editing css 2 body")
|
|
bashNote2UUID := apiCreateNote(t, user, bashBookUUID, "bash2", "adding bash note 2")
|
|
linuxBook2UUID := apiCreateBook(t, user, "linux", "adding new linux book")
|
|
linux2Note1UUID := apiCreateNote(t, user, linuxBookUUID, "linux-new-1", "adding linux note 1")
|
|
apiDeleteBook(t, user, jsBookUUID, "deleting js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"jsNote2UUID": jsNote2UUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"cssNote1UUID": cssNote1UUID,
|
|
"cssNote2UUID": cssNote2UUID,
|
|
"cssNote3UUID": cssNote3UUID,
|
|
"linuxBookUUID": linuxBookUUID,
|
|
"linuxNote1UUID": linuxNote1UUID,
|
|
"bashBookUUID": bashBookUUID,
|
|
"bashNote1UUID": bashNote1UUID,
|
|
"bashNote2UUID": bashNote2UUID,
|
|
"linuxBook2UUID": linuxBook2UUID,
|
|
"linux2Note1UUID": linux2Note1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 6,
|
|
clientBookCount: 3,
|
|
clientLastMaxUSN: 21,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 9,
|
|
serverBookCount: 5,
|
|
serverUserMaxUSN: 21,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote2JS, apiNote1CSS, apiNote2CSS, apiNote3CSS, apiNote1Bash, apiNote2Bash, apiNote1Linux, apiNote2Linux, apiNote1LinuxDup database.Note
|
|
var apiBookJS, apiBookCSS, apiBookBash, apiBookLinux, apiBookLinuxDup database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding api js note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote2UUID"]).First(&apiNote2JS), "finding api js note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote1UUID"]).First(&apiNote1CSS), "finding api css note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote2UUID"]).First(&apiNote2CSS), "finding api css note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote3UUID"]).First(&apiNote3CSS), "finding api css note 3")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxNote1UUID"]).First(&apiNote1Linux), "finding api linux note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote2UUID"]).First(&apiNote2Linux), "finding api linux note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashNote1UUID"]).First(&apiNote1Bash), "finding api bash note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashNote2UUID"]).First(&apiNote2Bash), "finding api bash note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linux2Note1UUID"]).First(&apiNote1LinuxDup), "finding api linux 2 note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding api js book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding api css book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashBookUUID"]).First(&apiBookBash), "finding api bash book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxBookUUID"]).First(&apiBookLinux), "finding api linux book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxBook2UUID"]).First(&apiBookLinuxDup), "finding api linux book 2")
|
|
|
|
// assert on server Label
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote2JS.USN, 0, "apiNote2JS USN mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS USN mismatch")
|
|
assert.NotEqual(t, apiNote2CSS.USN, 0, "apiNote2CSS USN mismatch")
|
|
assert.NotEqual(t, apiNote3CSS.USN, 0, "apiNote3CSS USN mismatch")
|
|
assert.NotEqual(t, apiNote1Linux.USN, 0, "apiNote1Linux USN mismatch")
|
|
assert.NotEqual(t, apiNote2Linux.USN, 0, "apiNote2Linux USN mismatch")
|
|
assert.NotEqual(t, apiNote1Bash.USN, 0, "apiNote1Bash USN mismatch")
|
|
assert.NotEqual(t, apiNote2Bash.USN, 0, "apiNote2Bash USN mismatch")
|
|
assert.NotEqual(t, apiNote1LinuxDup.USN, 0, "apiNote1LinuxDup USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apibookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apibookCSS USN mismatch")
|
|
assert.NotEqual(t, apiBookBash.USN, 0, "apibookBash USN mismatch")
|
|
assert.NotEqual(t, apiBookLinux.USN, 0, "apibookLinux USN mismatch")
|
|
assert.NotEqual(t, apiBookLinuxDup.USN, 0, "apiBookLinuxDup USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote2JS.Body, "", "apiNote2JS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiNote2CSS.Body, "css2-edited", "apiNote2CSS Body mismatch")
|
|
assert.Equal(t, apiNote3CSS.Body, "css3", "apiNote3CSS Body mismatch")
|
|
assert.Equal(t, apiNote1Linux.Body, "", "apiNote1Linux Body mismatch")
|
|
assert.Equal(t, apiNote2Linux.Body, "", "apiNote2Linux Body mismatch")
|
|
assert.Equal(t, apiNote1Bash.Body, "bash1", "apiNote1Bash Body mismatch")
|
|
assert.Equal(t, apiNote2Bash.Body, "bash2", "apiNote2Bash Body mismatch")
|
|
assert.Equal(t, apiNote1LinuxDup.Body, "linux-new-1", "apiNote1LinuxDup Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "", "apibookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apibookCSS Label mismatch")
|
|
assert.Equal(t, apiBookBash.Label, "bash", "apibookBash Label mismatch")
|
|
assert.Equal(t, apiBookLinux.Label, "", "apibookLinux Label mismatch")
|
|
assert.Equal(t, apiBookLinuxDup.Label, "linux", "apiBookLinuxDup Label mismatch")
|
|
// assert on uuids
|
|
assert.NotEqual(t, apiNote1JS.UUID, "", "apiNote1JS UUID mismatch")
|
|
assert.NotEqual(t, apiNote2JS.UUID, "", "apiNote2JS UUID mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.UUID, "", "apiNote1CSS UUID mismatch")
|
|
assert.NotEqual(t, apiNote2CSS.UUID, "", "apiNote2CSS UUID mismatch")
|
|
assert.NotEqual(t, apiNote3CSS.UUID, "", "apiNote3CSS UUID mismatch")
|
|
assert.NotEqual(t, apiNote1Linux.UUID, "", "apiNote1Linux UUID mismatch")
|
|
assert.NotEqual(t, apiNote2Linux.UUID, "", "apiNote2Linux UUID mismatch")
|
|
assert.NotEqual(t, apiNote1Bash.UUID, "", "apiNote1Bash UUID mismatch")
|
|
assert.NotEqual(t, apiNote2Bash.UUID, "", "apiNote2Bash UUID mismatch")
|
|
assert.NotEqual(t, apiNote2Bash.UUID, "", "apiNote2Bash UUID mismatch")
|
|
assert.NotEqual(t, apiBookJS.UUID, "", "apibookJS UUID mismatch")
|
|
assert.NotEqual(t, apiBookCSS.UUID, "", "apibookCSS UUID mismatch")
|
|
assert.NotEqual(t, apiBookBash.UUID, "", "apibookBash UUID mismatch")
|
|
assert.NotEqual(t, apiBookLinux.UUID, "", "apibookLinux UUID mismatch")
|
|
assert.NotEqual(t, apiBookLinuxDup.UUID, "", "apiBookLinuxDup UUID mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote2JS.Deleted, true, "apiNote2JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS.Deleted, false, "apiNote1CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote2CSS.Deleted, false, "apiNote2CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote3CSS.Deleted, false, "apiNote3CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote1Linux.Deleted, true, "apiNote1Linux Deleted mismatch")
|
|
assert.Equal(t, apiNote2Linux.Deleted, true, "apiNote2Linux Deleted mismatch")
|
|
assert.Equal(t, apiNote1Bash.Deleted, false, "apiNote1Bash Deleted mismatch")
|
|
assert.Equal(t, apiNote2Bash.Deleted, false, "apiNote2Bash Deleted mismatch")
|
|
assert.Equal(t, apiNote2Bash.Deleted, false, "apiNote2Bash Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, true, "apibookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apibookCSS Deleted mismatch")
|
|
assert.Equal(t, apiBookBash.Deleted, false, "apibookBash Deleted mismatch")
|
|
assert.Equal(t, apiBookLinux.Deleted, true, "apibookLinux Deleted mismatch")
|
|
assert.Equal(t, apiBookLinuxDup.Deleted, false, "apiBookLinuxDup Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookCSS, cliBookBash, cliBookLinux cliDatabase.Book
|
|
var cliNote1CSS, cliNote2CSS, cliNote3CSS, cliNote1Bash, cliNote2Bash, cliNote1Linux cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cli book css", cliDB.QueryRow("SELECT label FROM books WHERE uuid = ?", ids["cssBookUUID"]), &cliBookCSS.Label)
|
|
cliDatabase.MustScan(t, "finding cli book bash", cliDB.QueryRow("SELECT label FROM books WHERE uuid = ?", ids["bashBookUUID"]), &cliBookBash.Label)
|
|
cliDatabase.MustScan(t, "finding cli book linux2", cliDB.QueryRow("SELECT label FROM books WHERE uuid = ?", ids["linuxBook2UUID"]), &cliBookLinux.Label)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", apiNote1CSS.UUID), &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2CSS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", apiNote2CSS.UUID), &cliNote2CSS.Body, &cliNote2CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote3CSS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", apiNote3CSS.UUID), &cliNote3CSS.Body, &cliNote3CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Bash", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", apiNote1Bash.UUID), &cliNote1Bash.Body, &cliNote1Bash.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2Bash", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", apiNote2Bash.UUID), &cliNote2Bash.Body, &cliNote2Bash.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2Bash", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", apiNote1LinuxDup.UUID), &cliNote1Linux.Body, &cliNote1Linux.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS usn mismatch")
|
|
assert.NotEqual(t, cliNote2CSS.USN, 0, "cliNote2CSS usn mismatch")
|
|
assert.NotEqual(t, cliNote3CSS.USN, 0, "cliNote3CSS usn mismatch")
|
|
assert.NotEqual(t, cliNote1Bash.USN, 0, "cliNote1Bash usn mismatch")
|
|
assert.NotEqual(t, cliNote2Bash.USN, 0, "cliNote2Bash usn mismatch")
|
|
assert.NotEqual(t, cliNote1Linux.USN, 0, "cliNote1Linux usn mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliNote2CSS.Body, "css2-edited", "cliNote2CSS Body mismatch")
|
|
assert.Equal(t, cliNote3CSS.Body, "css3", "cliNote3CSS Body mismatch")
|
|
assert.Equal(t, cliNote1Bash.Body, "bash1", "cliNote1Bash Body mismatch")
|
|
assert.Equal(t, cliNote2Bash.Body, "bash2", "cliNote2Bash Body mismatch")
|
|
assert.Equal(t, cliNote1Linux.Body, "linux-new-1", "cliNote1Linux Body mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
assert.Equal(t, cliBookBash.Label, "bash", "cliBookBash Label mismatch")
|
|
assert.Equal(t, cliBookLinux.Label, "linux", "cliBookLinux Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote2CSS.Deleted, false, "cliNote2CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote3CSS.Deleted, false, "cliNote3CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote1Bash.Deleted, false, "cliNote1Bash Deleted mismatch")
|
|
assert.Equal(t, cliNote2Bash.Deleted, false, "cliNote2Bash Deleted mismatch")
|
|
assert.Equal(t, cliNote1Linux.Deleted, false, "cliNote1Linux Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookBash.Deleted, false, "cliBookBash Deleted mismatch")
|
|
assert.Equal(t, cliBookLinux.Deleted, false, "cliBookLinux Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
}
|
|
|
|
func TestSync_twoway(t *testing.T) {
|
|
t.Run("once", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding css book")
|
|
cssNote1UUID := apiCreateNote(t, user, cssBookUUID, "css1", "adding css note 1")
|
|
jsNote2UUID := apiCreateNote(t, user, jsBookUUID, "js2", "adding js note 2")
|
|
cssNote2UUID := apiCreateNote(t, user, cssBookUUID, "css2", "adding css note 2")
|
|
linuxBookUUID := apiCreateBook(t, user, "linux", "adding linux book")
|
|
linuxNote1UUID := apiCreateNote(t, user, linuxBookUUID, "linux1", "adding linux note 1")
|
|
apiPatchNote(t, user, jsNote2UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, linuxBookUUID), "moving js note 2 to linux")
|
|
apiDeleteNote(t, user, jsNote1UUID, "deleting js note 1")
|
|
cssNote3UUID := apiCreateNote(t, user, cssBookUUID, "css3", "adding css note 3")
|
|
bashBookUUID := apiCreateBook(t, user, "bash", "adding bash book")
|
|
bashNote1UUID := apiCreateNote(t, user, bashBookUUID, "bash1", "adding bash note 1")
|
|
apiDeleteBook(t, user, linuxBookUUID, "deleting linux book")
|
|
apiPatchNote(t, user, cssNote2UUID, fmt.Sprintf(`{"content": "%s"}`, "css2-edited"), "editing css 2 body")
|
|
bashNote2UUID := apiCreateNote(t, user, bashBookUUID, "bash2", "adding bash note 2")
|
|
linuxBook2UUID := apiCreateBook(t, user, "linux", "adding new linux book")
|
|
linux2Note1UUID := apiCreateNote(t, user, linuxBookUUID, "linux-new-1", "adding linux note 1")
|
|
apiDeleteBook(t, user, jsBookUUID, "deleting js book")
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js3")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "algorithms", "-c", "algorithms1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js4")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "algorithms", "-c", "algorithms2")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "math", "-c", "math1")
|
|
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to remove", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js3"), &nid)
|
|
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveBook, cliBinaryName, "remove", "algorithms")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css4")
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveNote, cliBinaryName, "remove", "js", nid)
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"jsNote2UUID": jsNote2UUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"cssNote1UUID": cssNote1UUID,
|
|
"cssNote2UUID": cssNote2UUID,
|
|
"cssNote3UUID": cssNote3UUID,
|
|
"linuxBookUUID": linuxBookUUID,
|
|
"linuxNote1UUID": linuxNote1UUID,
|
|
"bashBookUUID": bashBookUUID,
|
|
"bashNote1UUID": bashNote1UUID,
|
|
"bashNote2UUID": bashNote2UUID,
|
|
"linuxBook2UUID": linuxBook2UUID,
|
|
"linux2Note1UUID": linux2Note1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 9,
|
|
clientBookCount: 6,
|
|
clientLastMaxUSN: 27,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 12,
|
|
serverBookCount: 8,
|
|
serverUserMaxUSN: 27,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1CSS, cliNote2CSS, cliNote3CSS, cliNote1CSS2, cliNote1Bash, cliNote2Bash, cliNote1Linux, cliNote1Math, cliNote1JS cliDatabase.Note
|
|
var cliBookCSS, cliBookCSS2, cliBookBash, cliBookLinux, cliBookMath, cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css2-edited"), &cliNote2CSS.UUID, &cliNote2CSS.Body, &cliNote2CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote3CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css3"), &cliNote3CSS.UUID, &cliNote3CSS.Body, &cliNote3CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS2", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css4"), &cliNote1CSS2.UUID, &cliNote1CSS2.Body, &cliNote1CSS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Bash", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "bash1"), &cliNote1Bash.UUID, &cliNote1Bash.Body, &cliNote1Bash.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2Bash", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "bash2"), &cliNote2Bash.UUID, &cliNote2Bash.Body, &cliNote2Bash.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Linux", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "linux-new-1"), &cliNote1Linux.UUID, &cliNote1Linux.Body, &cliNote1Linux.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Math", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "math1"), &cliNote1Math.UUID, &cliNote1Math.Body, &cliNote1Math.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js4"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css_2"), &cliBookCSS2.UUID, &cliBookCSS2.Label, &cliBookCSS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookBash", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "bash"), &cliBookBash.UUID, &cliBookBash.Label, &cliBookBash.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookLinux", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "linux"), &cliBookLinux.UUID, &cliBookLinux.Label, &cliBookLinux.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookMath", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "math"), &cliBookMath.UUID, &cliBookMath.Label, &cliBookMath.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS USN mismatch")
|
|
assert.NotEqual(t, cliNote2CSS.USN, 0, "cliNote2CSS USN mismatch")
|
|
assert.NotEqual(t, cliNote3CSS.USN, 0, "cliNote3CSS USN mismatch")
|
|
assert.NotEqual(t, cliNote1CSS2.USN, 0, "cliNote1CSS2 USN mismatch")
|
|
assert.NotEqual(t, cliNote1Bash.USN, 0, "cliNote1Bash USN mismatch")
|
|
assert.NotEqual(t, cliNote2Bash.USN, 0, "cliNote2Bash USN mismatch")
|
|
assert.NotEqual(t, cliNote1Linux.USN, 0, "cliNote1Linux USN mismatch")
|
|
assert.NotEqual(t, cliNote1Math.USN, 0, "cliNote1Math USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS2.USN, 0, "cliBookCSS2 USN mismatch")
|
|
assert.NotEqual(t, cliBookBash.USN, 0, "cliBookBash USN mismatch")
|
|
assert.NotEqual(t, cliBookMath.USN, 0, "cliBookMath USN mismatch")
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliNote2CSS.Body, "css2-edited", "cliNote2CSS Body mismatch")
|
|
assert.Equal(t, cliNote3CSS.Body, "css3", "cliNote3CSS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS2.Body, "css4", "cliNote1CSS2 Body mismatch")
|
|
assert.Equal(t, cliNote1Bash.Body, "bash1", "cliNote1Bash Body mismatch")
|
|
assert.Equal(t, cliNote2Bash.Body, "bash2", "cliNote2Bash Body mismatch")
|
|
assert.Equal(t, cliNote1Linux.Body, "linux-new-1", "cliNote1Linux Body mismatch")
|
|
assert.Equal(t, cliNote1Math.Body, "math1", "cliNote1Math Body mismatch")
|
|
assert.Equal(t, cliNote1JS.Body, "js4", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
assert.Equal(t, cliBookCSS2.Label, "css_2", "cliBookCSS2 Label mismatch")
|
|
assert.Equal(t, cliBookBash.Label, "bash", "cliBookBash Label mismatch")
|
|
assert.Equal(t, cliBookMath.Label, "math", "cliBookMath Label mismatch")
|
|
assert.Equal(t, cliBookLinux.Label, "linux", "cliBookLinux Label mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote2CSS.Deleted, false, "cliNote2CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote3CSS.Deleted, false, "cliNote3CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS2.Deleted, false, "cliNote1CSS2 Deleted mismatch")
|
|
assert.Equal(t, cliNote1Bash.Deleted, false, "cliNote1Bash Deleted mismatch")
|
|
assert.Equal(t, cliNote2Bash.Deleted, false, "cliNote2Bash Deleted mismatch")
|
|
assert.Equal(t, cliNote1Linux.Deleted, false, "cliNote1Linux Deleted mismatch")
|
|
assert.Equal(t, cliNote1Math.Deleted, false, "cliNote1Math Deleted mismatch")
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS2.Deleted, false, "cliBookCSS2 Deleted mismatch")
|
|
assert.Equal(t, cliBookBash.Deleted, false, "cliBookBash Deleted mismatch")
|
|
assert.Equal(t, cliBookMath.Deleted, false, "cliBookMath Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1CSS, apiNote2CSS, apiNote3CSS, apiNote1Linux, apiNote2Linux, apiNote1Bash, apiNote2Bash, apiNote1LinuxDup, apiNote1CSS2, apiNote1Math, apiNote1JS2 database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding api js note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote1UUID"]).First(&apiNote1CSS), "finding api css note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote2UUID"]).First(&apiNote2CSS), "finding api css note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote3UUID"]).First(&apiNote3CSS), "finding api css note 3")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxNote1UUID"]).First(&apiNote1Linux), "finding api linux note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote2UUID"]).First(&apiNote2Linux), "finding api linux note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashNote1UUID"]).First(&apiNote1Bash), "finding api bash note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashNote2UUID"]).First(&apiNote2Bash), "finding api bash note 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linux2Note1UUID"]).First(&apiNote1LinuxDup), "finding api linux 2 note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1CSS2.UUID).First(&apiNote1CSS2), "finding apiNote1CSS2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1Math.UUID).First(&apiNote1Math), "finding apiNote1Math")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1JS.UUID).First(&apiNote1JS2), "finding apiNote1JS2")
|
|
var apiBookJS, apiBookCSS, apiBookLinux, apiBookBash, apiBookLinuxDup, apiBookCSS2, apiBookMath, apiBookJS2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding api js book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding api css book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashBookUUID"]).First(&apiBookBash), "finding api bash book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxBookUUID"]).First(&apiBookLinux), "finding api linux book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxBook2UUID"]).First(&apiBookLinuxDup), "finding api linux book 2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookCSS2.UUID).First(&apiBookCSS2), "finding apiBookCSS2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookMath.UUID).First(&apiBookMath), "finding apiBookMath")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookJS.UUID).First(&apiBookJS2), "finding apiBookJS2")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS usn mismatch")
|
|
assert.NotEqual(t, apiNote2CSS.USN, 0, "apiNote2CSS usn mismatch")
|
|
assert.NotEqual(t, apiNote3CSS.USN, 0, "apiNote3CSS usn mismatch")
|
|
assert.NotEqual(t, apiNote1Linux.USN, 0, "apiNote1Linux usn mismatch")
|
|
assert.NotEqual(t, apiNote2Linux.USN, 0, "apiNote2Linux usn mismatch")
|
|
assert.NotEqual(t, apiNote1Bash.USN, 0, "apiNote1Bash usn mismatch")
|
|
assert.NotEqual(t, apiNote2Bash.USN, 0, "apiNote2Bash usn mismatch")
|
|
assert.NotEqual(t, apiNote1LinuxDup.USN, 0, "apiNote1LinuxDup usn mismatch")
|
|
assert.NotEqual(t, apiNote1CSS2.USN, 0, "apiNoteCSS2 usn mismatch")
|
|
assert.NotEqual(t, apiNote1Math.USN, 0, "apiNote1Math usn mismatch")
|
|
assert.NotEqual(t, apiNote1JS2.USN, 0, "apiNote1JS2 usn mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS usn mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS usn mismatch")
|
|
assert.NotEqual(t, apiBookLinux.USN, 0, "apiBookLinux usn mismatch")
|
|
assert.NotEqual(t, apiBookBash.USN, 0, "apiBookBash usn mismatch")
|
|
assert.NotEqual(t, apiBookLinuxDup.USN, 0, "apiBookLinuxDup usn mismatch")
|
|
assert.NotEqual(t, apiBookCSS2.USN, 0, "apiBookCSS2 usn mismatch")
|
|
assert.NotEqual(t, apiBookMath.USN, 0, "apiBookMath usn mismatch")
|
|
assert.NotEqual(t, apiBookJS2.USN, 0, "apiBookJS2 usn mismatch")
|
|
// assert on note bodys
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiNote2CSS.Body, "css2-edited", "apiNote2CSS Body mismatch")
|
|
assert.Equal(t, apiNote3CSS.Body, "css3", "apiNote3CSS Body mismatch")
|
|
assert.Equal(t, apiNote1Linux.Body, "", "apiNote1Linux Body mismatch")
|
|
assert.Equal(t, apiNote2Linux.Body, "", "apiNote2Linux Body mismatch")
|
|
assert.Equal(t, apiNote1Bash.Body, "bash1", "apiNote1Bash Body mismatch")
|
|
assert.Equal(t, apiNote2Bash.Body, "bash2", "apiNote2Bash Body mismatch")
|
|
assert.Equal(t, apiNote1LinuxDup.Body, "linux-new-1", "apiNote1LinuxDup Body mismatch")
|
|
assert.Equal(t, apiNote1CSS2.Body, "css4", "apiNote1CSS2 Body mismatch")
|
|
assert.Equal(t, apiNote1Math.Body, "math1", "apiNote1Math Body mismatch")
|
|
assert.Equal(t, apiNote1JS2.Body, "js4", "apiNote1JS2 Body mismatch")
|
|
// client must have generated uuids
|
|
assert.NotEqual(t, apiNote1CSS2.UUID, "", "apiNote1CSS2 uuid mismatch")
|
|
assert.NotEqual(t, apiNote1Math.UUID, "", "apiNote1Math uuid mismatch")
|
|
assert.NotEqual(t, apiNote1JS2.UUID, "", "apiNote1JS2 uuid mismatch")
|
|
// assert on labels
|
|
assert.Equal(t, apiBookJS.Label, "", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
assert.Equal(t, apiBookLinux.Label, "", "apiBookLinux Label mismatch")
|
|
assert.Equal(t, apiBookBash.Label, "bash", "apiBookBash Label mismatch")
|
|
assert.Equal(t, apiBookLinuxDup.Label, "linux", "apiBookLinuxDup Label mismatch")
|
|
assert.Equal(t, apiBookCSS2.Label, "css_2", "apiBookCSS2 Label mismatch")
|
|
assert.Equal(t, apiBookMath.Label, "math", "apiBookMath Label mismatch")
|
|
assert.Equal(t, apiBookJS2.Label, "js", "apiBookJS2 Label mismatch")
|
|
// assert on note deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS.Deleted, false, "apiNote1CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote2CSS.Deleted, false, "apiNote2CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote3CSS.Deleted, false, "apiNote3CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote1Linux.Deleted, true, "apiNote1Linux Deleted mismatch")
|
|
assert.Equal(t, apiNote2Linux.Deleted, true, "apiNote2Linux Deleted mismatch")
|
|
assert.Equal(t, apiNote1Bash.Deleted, false, "apiNote1Bash Deleted mismatch")
|
|
assert.Equal(t, apiNote2Bash.Deleted, false, "apiNote2Bash Deleted mismatch")
|
|
assert.Equal(t, apiNote1LinuxDup.Deleted, false, "apiNote1LinuxDup Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS2.Deleted, false, "apiNote1CSS2 Deleted mismatch")
|
|
assert.Equal(t, apiNote1Math.Deleted, false, "apiNote1Math Deleted mismatch")
|
|
assert.Equal(t, apiNote1JS2.Deleted, false, "apiNote1JS2 Deleted mismatch")
|
|
// assert on book deleted
|
|
assert.Equal(t, apiBookJS.Deleted, true, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
assert.Equal(t, apiBookLinux.Deleted, true, "apiBookLinux Deleted mismatch")
|
|
assert.Equal(t, apiBookBash.Deleted, false, "apiBookBash Deleted mismatch")
|
|
assert.Equal(t, apiBookLinuxDup.Deleted, false, "apiBookLinuxDup Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS2.Deleted, false, "apiBookCSS2 Deleted mismatch")
|
|
assert.Equal(t, apiBookMath.Deleted, false, "apiBookMath Deleted mismatch")
|
|
assert.Equal(t, apiBookJS2.Deleted, false, "apiBookJS2 Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("twice", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding css book")
|
|
cssNote1UUID := apiCreateNote(t, user, cssBookUUID, "css1", "adding css note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js2")
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveBook, cliBinaryName, "remove", "js")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "math", "-c", "math1")
|
|
|
|
var nid string
|
|
cliDB := ctx.DB
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "math1"), &nid)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "math", nid, "-c", "math1-edited")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
scssBookUUID := apiCreateBook(t, user, "scss", "adding a scss book")
|
|
apiPatchNote(t, user, cssNote1UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, scssBookUUID), "moving css note 1 to scss")
|
|
|
|
var n1UUID string
|
|
cliDatabase.MustScan(t, "getting math1-edited note UUID", cliDB.QueryRow("SELECT uuid FROM notes WHERE body = ?", "math1-edited"), &n1UUID)
|
|
apiPatchNote(t, user, n1UUID, fmt.Sprintf(`{"content": "%s", "public": true}`, "math1-edited"), "editing math1 note")
|
|
|
|
cssNote2UUID := apiCreateNote(t, user, cssBookUUID, "css2", "adding css note 2")
|
|
apiDeleteBook(t, user, cssBookUUID, "deleting css book")
|
|
|
|
bashBookUUID := apiCreateBook(t, user, "bash", "adding a bash book")
|
|
algorithmsBookUUID := apiCreateBook(t, user, "algorithms", "adding a algorithms book")
|
|
|
|
// 4. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js3")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "algorithms", "-c", "algorithms1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"scssBookUUID": scssBookUUID,
|
|
"cssNote1UUID": cssNote1UUID,
|
|
"cssNote2UUID": cssNote2UUID,
|
|
"bashBookUUID": bashBookUUID,
|
|
"algorithmsBookUUID": algorithmsBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
apiDB := serverDb
|
|
cliDB := ctx.DB
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 5,
|
|
clientBookCount: 6,
|
|
clientLastMaxUSN: 17,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 6,
|
|
serverBookCount: 7,
|
|
serverUserMaxUSN: 17,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote2JS, cliNote1SCSS, cliNote1Math, cliNote1Alg2 cliDatabase.Note
|
|
var cliBookJS, cliBookSCSS, cliBookMath, cliBookBash, cliBookAlg, cliBookAlg2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js3"), &cliNote2JS.UUID, &cliNote2JS.Body, &cliNote2JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1SCSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNote1SCSS.UUID, &cliNote1SCSS.Body, &cliNote1SCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Math", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "math1-edited"), &cliNote1Math.UUID, &cliNote1Math.Body, &cliNote1Math.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Alg2", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "algorithms1"), &cliNote1Alg2.UUID, &cliNote1Alg2.Body, &cliNote1Alg2.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookSCSS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "scss"), &cliBookSCSS.UUID, &cliBookSCSS.Label, &cliBookSCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookMath", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "math"), &cliBookMath.UUID, &cliBookMath.Label, &cliBookMath.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookBash", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "bash"), &cliBookBash.UUID, &cliBookBash.Label, &cliBookBash.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookAlg", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "algorithms"), &cliBookAlg.UUID, &cliBookAlg.Label, &cliBookAlg.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookAlg2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "algorithms_2"), &cliBookAlg2.UUID, &cliBookAlg2.Label, &cliBookAlg2.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliNote2JS.USN, 0, "cliNote2JS USN mismatch")
|
|
assert.NotEqual(t, cliNote1SCSS.USN, 0, "cliNote1SCSS USN mismatch")
|
|
assert.NotEqual(t, cliNote1Math.USN, 0, "cliNote1Math USN mismatch")
|
|
assert.NotEqual(t, cliNote1Alg2.USN, 0, "cliNote1Alg2 USN mismatch")
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookSCSS.USN, 0, "cliBookSCSS USN mismatch")
|
|
assert.NotEqual(t, cliBookMath.USN, 0, "cliBookMath USN mismatch")
|
|
assert.NotEqual(t, cliBookBash.USN, 0, "cliBookBash USN mismatch")
|
|
assert.NotEqual(t, cliBookAlg.USN, 0, "cliBookAlg USN mismatch")
|
|
assert.NotEqual(t, cliBookAlg2.USN, 0, "cliBookAlg2 USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote2JS.Body, "js3", "cliNote2JS Body mismatch")
|
|
assert.Equal(t, cliNote1SCSS.Body, "css1", "cliNote1SCSS Body mismatch")
|
|
assert.Equal(t, cliNote1Math.Body, "math1-edited", "cliNote1Math Body mismatch")
|
|
assert.Equal(t, cliNote1Alg2.Body, "algorithms1", "cliNote1Alg2 Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookSCSS.Label, "scss", "cliBookSCSS Label mismatch")
|
|
assert.Equal(t, cliBookMath.Label, "math", "cliBookMath Label mismatch")
|
|
assert.Equal(t, cliBookBash.Label, "bash", "cliBookBash Label mismatch")
|
|
assert.Equal(t, cliBookAlg.Label, "algorithms", "cliBookAlg Label mismatch")
|
|
assert.Equal(t, cliBookAlg2.Label, "algorithms_2", "cliBookAlg2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote2JS.Deleted, false, "cliNote2JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1SCSS.Deleted, false, "cliNote1SCSS Deleted mismatch")
|
|
assert.Equal(t, cliNote1Math.Deleted, false, "cliNote1Math Deleted mismatch")
|
|
assert.Equal(t, cliNote1Alg2.Deleted, false, "cliNote1Alg2 Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookSCSS.Deleted, false, "cliBookSCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookMath.Deleted, false, "cliBookMath Deleted mismatch")
|
|
assert.Equal(t, cliBookBash.Deleted, false, "cliBookBash Deleted mismatch")
|
|
assert.Equal(t, cliBookAlg.Deleted, false, "cliBookAlg Deleted mismatch")
|
|
assert.Equal(t, cliBookAlg2.Deleted, false, "cliBookAlg2 Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote2JS, apiNote1SCSS, apiNote2CSS, apiNote1Math, apiNote1Alg database.Note
|
|
var apiBookJS, apiBookCSS, apiBookSCSS, apiBookMath, apiBookBash, apiBookAlg, apiBookAlg2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote2UUID"]).First(&apiNote2CSS), "finding apiNote2CSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote2JS.UUID).First(&apiNote2JS), "finding apiNote2JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote1UUID"]).First(&apiNote1SCSS), "finding apiNote1SCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1Math.UUID).First(&apiNote1Math), "finding apiNote1Math")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1Alg2.UUID).First(&apiNote1Alg), "finding apiNote1Alg")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["bashBookUUID"]).First(&apiBookBash), "finding apiBookBash")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["scssBookUUID"]).First(&apiBookSCSS), "finding apiBookSCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["algorithmsBookUUID"]).First(&apiBookAlg), "finding apiBookAlg")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookAlg2.UUID).First(&apiBookAlg2), "finding apiBookAlg2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookMath.UUID).First(&apiBookMath), "finding apiBookMath")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote2JS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote1SCSS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote2CSS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote1Math.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote1Alg.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBook1Alg usn mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS usn mismatch")
|
|
assert.NotEqual(t, apiBookSCSS.USN, 0, "apibookSCSS usn mismatch")
|
|
assert.NotEqual(t, apiBookMath.USN, 0, "apiBookMath usn mismatch")
|
|
assert.NotEqual(t, apiBookBash.USN, 0, "apiBookBash usn mismatch")
|
|
assert.NotEqual(t, apiBookAlg.USN, 0, "apiBookAlg usn mismatch")
|
|
assert.NotEqual(t, apiBookAlg2.USN, 0, "apiBookAlg2 usn mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote2JS.Body, "js3", "apiNote2JS Body mismatch")
|
|
assert.Equal(t, apiNote1SCSS.Body, "css1", "apiNote1SCSS Body mismatch")
|
|
assert.Equal(t, apiNote2CSS.Body, "", "apiNote2CSS Body mismatch")
|
|
assert.Equal(t, apiNote1Math.Body, "math1-edited", "apiNote1Math Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "", "apiBookCSS Label mismatch")
|
|
assert.Equal(t, apiBookSCSS.Label, "scss", "apiBookSCSS Label mismatch")
|
|
assert.Equal(t, apiBookMath.Label, "math", "apiBookMath Label mismatch")
|
|
assert.Equal(t, apiBookBash.Label, "bash", "apiBookBash Label mismatch")
|
|
assert.Equal(t, apiBookAlg.Label, "algorithms", "apiBookAlg Label mismatch")
|
|
assert.Equal(t, apiBookAlg2.Label, "algorithms_2", "apiBookAlg2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote2JS.Deleted, false, "apiNote2JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1SCSS.Deleted, false, "apiNote1SCSS Deleted mismatch")
|
|
assert.Equal(t, apiNote2CSS.Deleted, true, "apiNote2CSS Deleted mismatch")
|
|
assert.Equal(t, apiNote1Math.Deleted, false, "apiNote1Math Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, true, "apiBookCSS Deleted mismatch")
|
|
assert.Equal(t, apiBookSCSS.Deleted, false, "apiBookSCSS Deleted mismatch")
|
|
assert.Equal(t, apiBookMath.Deleted, false, "apiBookMath Deleted mismatch")
|
|
assert.Equal(t, apiBookBash.Deleted, false, "apiBookBash Deleted mismatch")
|
|
assert.Equal(t, apiBookAlg.Deleted, false, "apiBookAlg Deleted mismatch")
|
|
assert.Equal(t, apiBookAlg2.Deleted, false, "apiBookAlg2 Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("three times", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
goBookUUID := apiCreateBook(t, user, "go", "adding a go book")
|
|
goNote1UUID := apiCreateNote(t, user, goBookUUID, "go1", "adding go note 1")
|
|
|
|
// 4. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "html", "-c", "html1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"goBookUUID": goBookUUID,
|
|
"goNote1UUID": goNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 4,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 4,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote1CSS, cliNote1Go, cliNote1HTML cliDatabase.Note
|
|
var cliBookJS, cliBookCSS, cliBookGo, cliBookHTML cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1Go", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "go1"), &cliNote1Go.UUID, &cliNote1Go.Body, &cliNote1Go.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1HTML", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "html1"), &cliNote1HTML.UUID, &cliNote1HTML.Body, &cliNote1HTML.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookGo", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "go"), &cliBookGo.UUID, &cliBookGo.Label, &cliBookGo.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookHTML", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "html"), &cliBookHTML.UUID, &cliBookHTML.Label, &cliBookHTML.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS USN mismatch")
|
|
assert.NotEqual(t, cliNote1Go.USN, 0, "cliNote1Go USN mismatch")
|
|
assert.NotEqual(t, cliNote1HTML.USN, 0, "cliNote1HTML USN mismatch")
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliBookGo.USN, 0, "cliBookGo USN mismatch")
|
|
assert.NotEqual(t, cliBookHTML.USN, 0, "cliBookHTML USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliNote1Go.Body, "go1", "cliNote1Go Body mismatch")
|
|
assert.Equal(t, cliNote1HTML.Body, "html1", "cliNote1HTML Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
assert.Equal(t, cliBookGo.Label, "go", "cliBookGo Label mismatch")
|
|
assert.Equal(t, cliBookHTML.Label, "html", "cliBookHTML Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote1Go.Deleted, false, "cliNote1Go Deleted mismatch")
|
|
assert.Equal(t, cliNote1HTML.Deleted, false, "cliNote1HTML Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookGo.Deleted, false, "cliBookGo Deleted mismatch")
|
|
assert.Equal(t, cliBookHTML.Deleted, false, "cliBookHTML Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1CSS, apiNote1Go, apiNote1HTML database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["goNote1UUID"]).First(&apiNote1Go), "finding apiNote1Go")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1CSS.UUID).First(&apiNote1CSS), "finding apiNote1CSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1HTML.UUID).First(&apiNote1HTML), "finding apiNote1HTML")
|
|
var apiBookJS, apiBookCSS, apiBookGo, apiBookHTML database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["goBookUUID"]).First(&apiBookGo), "finding apiBookGo")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookCSS.UUID).First(&apiBookCSS), "finding apiBookCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookHTML.UUID).First(&apiBookHTML), "finding apiBookHTML")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS USN mismatch")
|
|
assert.NotEqual(t, apiNote1Go.USN, 0, "apiNote1Go USN mismatch")
|
|
assert.NotEqual(t, apiNote1HTML.USN, 0, "apiNote1HTM USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookGo.USN, 0, "apiBookGo USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
assert.NotEqual(t, apiBookHTML.USN, 0, "apiBookHTML USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiNote1Go.Body, "go1", "apiNote1Go Body mismatch")
|
|
assert.Equal(t, apiNote1HTML.Body, "html1", "apiNote1HTM Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookGo.Label, "go", "apiBookGo Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
assert.Equal(t, apiBookHTML.Label, "html", "apiBookHTML Label mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
}
|
|
|
|
func TestSync(t *testing.T) {
|
|
t.Run("client adds a book and a note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
|
|
return map[string]string{}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 2,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 2,
|
|
})
|
|
|
|
// test client
|
|
// assert on bodys and labels
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1JS.UUID).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookJS.UUID).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client deletes a book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveBook, cliBinaryName, "remove", "js")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 0,
|
|
clientLastMaxUSN: 5,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 5,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, true, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client deletes a note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to remove", cliDB.QueryRow("SELECT rowid FROM notes WHERE uuid = ?", jsNote1UUID), &nid)
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveNote, cliBinaryName, "remove", "js", nid)
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE label = ?", "js"), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client edits a note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE uuid = ?", jsNote1UUID), &nid)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", nid, "-c", "js1-edited")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1-edited", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1-edited", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client edits a book by renaming it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", "-n", "js-edited")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
var cliNote1JS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, book_uuid, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.BookUUID, &cliNote1JS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookJS.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js-edited", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js-edited", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server adds a book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 1,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 0,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 1,
|
|
})
|
|
|
|
// test server
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
// assert on bodys and labels
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE label = ?", "js"), &cliBookJS.Label, &cliBookJS.USN)
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server edits a book by renaming it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiPatchBook(t, user, jsBookUUID, fmt.Sprintf(`{"name": "%s"}`, "js-new-label"), "editing js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 2,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 0,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 2,
|
|
})
|
|
|
|
// test server
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiBookJS.Label, "js-new-label", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
// assert on bodys and labels
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
assert.Equal(t, cliBookJS.Label, "js-new-label", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server deletes a book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteBook(t, user, jsBookUUID, "deleting js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 0,
|
|
clientLastMaxUSN: 2,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 0,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 2,
|
|
})
|
|
|
|
// test server
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiBookJS.Label, "", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiBookJS.Deleted, true, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server adds a note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 2,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 2,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server edits a note body", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"content": "%s"}`, "js1-edited"), "editing js note 1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1-edited", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1-edited", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server moves a note to another book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding css book")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, cssBookUUID), "moving js note 1 to css book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS, apiBookCSS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["cssBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["cssBookUUID"]), &cliBookCSS.Label, &cliBookCSS.USN)
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server deletes a note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteNote(t, user, jsNote1UUID, "deleting js note 1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server deletes the same book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteBook(t, user, jsBookUUID, "deleting js book")
|
|
|
|
// 4. on cli
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveBook, cliBinaryName, "remove", "js")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 0,
|
|
clientLastMaxUSN: 6,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 6,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, true, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server deletes the same note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteNote(t, user, jsNote1UUID, "deleting js note 1")
|
|
|
|
// 4. on cli
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to remove", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js1"), &nid)
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveNote, cliBinaryName, "remove", "js", nid)
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
apiDB := serverDb
|
|
cliDB := ctx.DB
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server and client adds a note with same body", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 4. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote2JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ? and uuid != ?", "js1", ids["jsNote1UUID"]), &cliNote2JS.UUID, &cliNote2JS.Body, &cliNote2JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote2JS.Body, "js1", "cliNote2JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote2JS.Deleted, false, "cliNote2JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote2JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote2JS.UUID).First(&apiNote2JS), "finding apiNote2JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote2JS.USN, 0, "apiNote2JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote2JS.Body, "js1", "apiNote2JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server and client adds a book with same label", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote1JS2 cliDatabase.Note
|
|
var cliBookJS, cliBookJS2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS2",
|
|
cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ? AND uuid !=?", "js1", ids["jsNote1UUID"]), &cliNote1JS2.UUID, &cliNote1JS2.Body, &cliNote1JS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js_2"), &cliBookJS2.UUID, &cliBookJS2.Label, &cliBookJS2.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS2.Body, "js1", "cliNote1JS2 Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookJS2.Label, "js_2", "cliBookJS2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1JS2.Deleted, false, "cliNote1JS2 Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS2.Deleted, false, "cliBookJS2 Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1JS2 database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1JS2.UUID).First(&apiNote1JS2), "finding apiNote1JS2")
|
|
var apiBookJS, apiBookJS2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookJS2.UUID).First(&apiBookJS2), "finding apiBookJS2")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote1JS2.USN, 0, "apiNote1JS2 USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS2.USN, 0, "apiBookJS2 USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS2.Body, "js1", "apiNote1JS2 Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookJS2.Label, "js_2", "apiBookJS2 Label mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server and client adds two sets of books with same labels", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding css book")
|
|
cssNote1UUID := apiCreateNote(t, user, cssBookUUID, "css1", "adding css note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"cssNote1UUID": cssNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 4,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 4,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote1JS2, cliNote1CSS, cliNote1CSS2 cliDatabase.Note
|
|
var cliBookJS, cliBookJS2, cliBookCSS, cliBookCSS2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS2",
|
|
cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ? AND uuid != ?", "js1", ids["jsNote1UUID"]), &cliNote1JS2.UUID, &cliNote1JS2.Body, &cliNote1JS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE uuid = ?", ids["cssNote1UUID"]), &cliNote1CSS.UUID, &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS2",
|
|
cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ? AND uuid != ?", "css1", ids["cssNote1UUID"]), &cliNote1CSS2.UUID, &cliNote1CSS2.Body, &cliNote1CSS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js_2"), &cliBookJS2.UUID, &cliBookJS2.Label, &cliBookJS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css_2"), &cliBookCSS2.UUID, &cliBookCSS2.Label, &cliBookCSS2.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS2.Body, "js1", "cliNote1JS2 Body mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS2.Body, "css1", "cliNote1CSS2 Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookJS2.Label, "js_2", "cliBookJS2 Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
assert.Equal(t, cliBookCSS2.Label, "css_2", "cliBookCSS2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1JS2.Deleted, false, "cliNote1JS2 Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS2.Deleted, false, "cliNote1CSS2 Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS2.Deleted, false, "cliBookJS2 Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS2.Deleted, false, "cliBookCSS2 Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1JS2, apiNote1CSS, apiNote1CSS2 database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1JS2.UUID).First(&apiNote1JS2), "finding apiNote1JS2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssNote1UUID"]).First(&apiNote1CSS), "finding apiNote1CSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1CSS2.UUID).First(&apiNote1CSS2), "finding apiNote1CSS2")
|
|
var apiBookJS, apiBookJS2, apiBookCSS, apiBookCSS2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookJS2.UUID).First(&apiBookJS2), "finding apiBookJS2")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookCSS2.UUID).First(&apiBookCSS2), "finding apiBookCSS2")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote1JS2.USN, 0, "apiNote1JS2 USN mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS USN mismatch")
|
|
assert.NotEqual(t, apiNote1CSS2.USN, 0, "apiNote1CSS2 USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS2.USN, 0, "apiBookJS2 USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS2.USN, 0, "apiBookCSS2 USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS2.Body, "js1", "apiNote1JS2 Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS2 Body mismatch")
|
|
assert.Equal(t, apiNote1CSS2.Body, "css1", "apiNote1CSS2 Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookJS2.Label, "js_2", "apiBookJS2 Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
assert.Equal(t, apiBookCSS2.Label, "css_2", "apiBookCSS2 Label mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server and client adds notes to the same book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 4. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js2")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote2JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js2"), &cliNote2JS.UUID, &cliNote2JS.Body, &cliNote2JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote2JS.Body, "js2", "cliNote2JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote2JS.Deleted, false, "cliNote2JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote2JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote2JS.UUID).First(&apiNote2JS), "finding apiNote2JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote2JS.USN, 0, "apiNote2JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote2JS.Body, "js2", "apiNote2JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server and client adds a book with the same label and notes in it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js2")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote2JS cliDatabase.Note
|
|
var cliBookJS, cliBookJS2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote2JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js2"), &cliNote2JS.UUID, &cliNote2JS.Body, &cliNote2JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js_2"), &cliBookJS2.UUID, &cliBookJS2.Label, &cliBookJS2.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote2JS.Body, "js2", "cliNote2JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookJS2.Label, "js_2", "cliBookJS2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote2JS.Deleted, false, "cliNote2JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS2.Deleted, false, "cliBookJS2 Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote2JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote2JS.UUID).First(&apiNote2JS), "finding apiNote2JS")
|
|
var apiBookJS, apiBookJS2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookJS2.UUID).First(&apiBookJS2), "finding apiBookJS2")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote2JS.USN, 0, "apiNote2JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS2.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote2JS.Body, "js2", "apiNote2JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookJS2.Label, "js_2", "apiBookJS2 USN mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server edits bodys of the same note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE uuid = ?", jsNote1UUID), &nid)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", nid, "-c", "js1-edited-from-client")
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"content": "%s"}`, "js1-edited-from-server"), "editing js note 1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
resolvedBody := "<<<<<<< Local\njs1-edited-from-client\n=======\njs1-edited-from-server\n>>>>>>> Server\n"
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, resolvedBody, "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, resolvedBody, "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("clients deletes a note and server edits its body", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to remove", cliDB.QueryRow("SELECT rowid FROM notes WHERE uuid = ?", jsNote1UUID), &nid)
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveNote, cliBinaryName, "remove", "js", nid)
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"content": "%s"}`, "js1-edited"), "editing js note 1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 3,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 3,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1-edited", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1-edited", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("clients deletes a note and server moves it to another book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding css book")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to remove", cliDB.QueryRow("SELECT rowid FROM notes WHERE uuid = ?", jsNote1UUID), &nid)
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveNote, cliBinaryName, "remove", "js", nid)
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, cssBookUUID), "moving js note 1 to css book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS, apiBookCSS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["cssBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["cssBookUUID"]), &cliBookCSS.Label, &cliBookCSS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, ids["cssNote1UUID"], "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server deletes a note and client edits it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteNote(t, user, jsNote1UUID, "deleting js note 1")
|
|
|
|
// 4. on cli
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js1"), &nid)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", nid, "-c", "js1-edited")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1-edited", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, book_uuid, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.BookUUID, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1-edited", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, ids["jsBookUUID"], "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server deletes a book and client edits it by renaming it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteNote(t, user, jsNote1UUID, "deleting js note 1")
|
|
|
|
// 4. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", "-n", "js-edited")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js-edited", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliBookJS.Label, "js-edited", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("server deletes a book and client edits a note in it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
cliDB := ctx.DB
|
|
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiDeleteBook(t, user, jsBookUUID, "deleting js book")
|
|
|
|
// 4. on cli
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js1"), &nid)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", nid, "-c", "js1-edited")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 6,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 6,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1-edited", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliNote1JS cliDatabase.Note
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT body, book_uuid, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.Body, &cliNote1JS.BookUUID, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1-edited", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, ids["jsBookUUID"], "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client deletes a book and server edits it by renaming it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveBook, cliBinaryName, "remove", "js")
|
|
|
|
// 3. on server
|
|
apiPatchBook(t, user, jsBookUUID, fmt.Sprintf(`{"name": "%s"}`, "js-edited"), "editing js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 5,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 5,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js-edited", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.Label, &cliBookJS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliBookJS.Label, "js-edited", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client deletes a book and server edits a note in it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"content": "%s"}`, "js1-edited"), "editing js1 note")
|
|
|
|
// 4. on cli
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.ConfirmRemoveBook, cliBinaryName, "remove", "js")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 0,
|
|
clientBookCount: 0,
|
|
clientLastMaxUSN: 6,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 6,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["jsBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, true, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, true, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server edit a book by renaming it to a same name", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", "-n", "js-edited")
|
|
|
|
// 3. on server
|
|
apiPatchBook(t, user, jsBookUUID, fmt.Sprintf(`{"name": "%s"}`, "js-edited"), "editing js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
var cliNote1JS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, book_uuid, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.BookUUID, &cliNote1JS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookJS.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js-edited", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js-edited", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server edit a book by renaming it to different names", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", "-n", "js-edited-client")
|
|
|
|
// 3. on server
|
|
apiPatchBook(t, user, jsBookUUID, fmt.Sprintf(`{"name": "%s"}`, "js-edited-server"), "editing js book")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
// In this case, server's change wins and overwrites that of client's
|
|
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 1,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 1,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test client
|
|
var cliBookJS cliDatabase.Book
|
|
var cliNote1JS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE uuid = ?", ids["jsBookUUID"]), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, book_uuid, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.BookUUID, &cliNote1JS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookJS.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js-edited-server", "cliBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js-edited-server", "apiBookJS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client moves a note", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding a css book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "1", "-b", "css")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS, apiBookCSS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["cssBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
var cliNote1JS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cli book js", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cli book css", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, book_uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.BookUUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookCSS.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
assert.Equal(t, cliBookCSS.Dirty, false, "cliBookCSS Dirty mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server each moves a note to a same book", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding a css book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, cssBookUUID), "moving js note 1 to css book")
|
|
|
|
// 3. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "1", "-b", "css")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 5,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 5,
|
|
})
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS, apiBookCSS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, ids["cssBookUUID"], "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
|
|
// test client
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
var cliNote1JS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cli book js", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cli book css", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, book_uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.BookUUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookCSS.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
assert.Equal(t, cliBookCSS.Dirty, false, "cliBookCSS Dirty mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client and server each moves a note to different books", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding a css book")
|
|
linuxBookUUID := apiCreateBook(t, user, "linux", "adding a linux book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
apiPatchNote(t, user, jsNote1UUID, fmt.Sprintf(`{"book_uuid": "%s"}`, cssBookUUID), "moving js note 1 to css book")
|
|
|
|
// 3. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "1", "-b", "linux")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
"linuxBookUUID": linuxBookUUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
expectedNote1JSBody := `<<<<<<< Local
|
|
Moved to the book linux
|
|
=======
|
|
Moved to the book css
|
|
>>>>>>> Server
|
|
|
|
js1`
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 1,
|
|
clientBookCount: 4,
|
|
clientLastMaxUSN: 7,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 1,
|
|
serverBookCount: 4,
|
|
serverUserMaxUSN: 7,
|
|
})
|
|
|
|
// test client
|
|
var cliBookJS, cliBookCSS, cliBookLinux, cliBookConflicts cliDatabase.Book
|
|
var cliNote1JS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cli book js", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cli book css", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookLinux", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "linux"), &cliBookLinux.UUID, &cliBookLinux.Label, &cliBookLinux.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookConflicts", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "conflicts"), &cliBookConflicts.UUID, &cliBookConflicts.Label, &cliBookConflicts.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, book_uuid, body, usn FROM notes WHERE uuid = ?", ids["jsNote1UUID"]), &cliNote1JS.UUID, &cliNote1JS.BookUUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliBookLinux.USN, 0, "cliBookLinux USN mismatch")
|
|
assert.NotEqual(t, cliBookConflicts.USN, 0, "cliBookConflicts USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, expectedNote1JSBody, "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookConflicts.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
assert.Equal(t, cliBookLinux.Label, "linux", "cliBookLinux Label mismatch")
|
|
assert.Equal(t, cliBookConflicts.Label, "conflicts", "cliBookConflicts Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookLinux.Deleted, false, "cliBookLinux Deleted mismatch")
|
|
assert.Equal(t, cliBookConflicts.Deleted, false, "cliBookConflicts Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
assert.Equal(t, cliBookCSS.Dirty, false, "cliBookCSS Dirty mismatch")
|
|
assert.Equal(t, cliBookLinux.Dirty, false, "cliBookLinux Dirty mismatch")
|
|
assert.Equal(t, cliBookConflicts.Dirty, false, "cliBookConflicts Dirty mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
var apiBookJS, apiBookCSS, apiBookLinux, apiBookConflicts database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["linuxBookUUID"]).First(&apiBookLinux), "finding apiBookLinux")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookConflicts.UUID).First(&apiBookConflicts), "finding apiBookConflicts")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
assert.NotEqual(t, apiBookConflicts.USN, 0, "apiBookConflicts USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, expectedNote1JSBody, "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, apiBookConflicts.UUID, "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
assert.Equal(t, apiBookLinux.Label, "linux", "apiBookLinux Label mismatch")
|
|
assert.Equal(t, apiBookConflicts.Label, "conflicts", "apiBookConflicts Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
assert.Equal(t, apiBookLinux.Deleted, false, "apiBookLinux Deleted mismatch")
|
|
assert.Equal(t, apiBookConflicts.Deleted, false, "apiBookConflicts Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client adds a new book and moves a note into it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
|
|
cliDB := ctx.DB
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to edit", cliDB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js1"), &nid)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", "js", nid, "-b", "css")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 5,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 5,
|
|
})
|
|
|
|
// test client
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
var cliNote1JS, cliNote1CSS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cli book js", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cli book css", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, book_uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.BookUUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, book_uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.BookUUID, &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookCSS.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS.BookUUID, cliBookCSS.UUID, "cliNote1CSS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliNote1CSS.Dirty, false, "cliNote1CSS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
assert.Equal(t, cliBookCSS.Dirty, false, "cliBookCSS Dirty mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1CSS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1CSS.UUID).First(&apiNote1CSS), "finding apiNote1CSS")
|
|
var apiBookJS, apiBookCSS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookCSS.UUID).First(&apiBookCSS), "finding apiBookCSS")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, apiBookCSS.UUID, "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.BookUUID, apiBookCSS.UUID, "apiNote1CSS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS.Deleted, false, "apiNote1CSS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
|
|
t.Run("client adds a duplicate book and moves a note into it", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
// 1. on server
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
// 2. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// 3. on server
|
|
cssBookUUID := apiCreateBook(t, user, "css", "adding a css book")
|
|
|
|
// 3. on cli
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
|
|
var nid string
|
|
cliDatabase.MustScan(t, "getting id of note to edit", ctx.DB.QueryRow("SELECT rowid FROM notes WHERE body = ?", "js1"), &nid)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "edit", nid, "-b", "css")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"cssBookUUID": cssBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 3,
|
|
clientLastMaxUSN: 6,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 3,
|
|
serverUserMaxUSN: 6,
|
|
})
|
|
|
|
// test client
|
|
var cliBookJS, cliBookCSS, cliBookCSS2 cliDatabase.Book
|
|
var cliNote1JS, cliNote1CSS cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS2", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css_2"), &cliBookCSS2.UUID, &cliBookCSS2.Label, &cliBookCSS2.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, book_uuid, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.BookUUID, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, body, book_uuid, usn FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.Body, &cliNote1CSS.BookUUID, &cliNote1CSS.USN)
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS2.USN, 0, "cliBookCSS2 USN mismatch")
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1JS.BookUUID, cliBookCSS2.UUID, "cliNote1JS BookUUID mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS.BookUUID, cliBookCSS2.UUID, "cliNote1CSS BookUUID mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
assert.Equal(t, cliBookCSS2.Label, "css_2", "cliBookCSS2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS2.Deleted, false, "cliBookCSS2 Deleted mismatch")
|
|
// assert on dirty
|
|
assert.Equal(t, cliNote1JS.Dirty, false, "cliNote1JS Dirty mismatch")
|
|
assert.Equal(t, cliNote1CSS.Dirty, false, "cliNote1CSS Dirty mismatch")
|
|
assert.Equal(t, cliBookJS.Dirty, false, "cliBookJS Dirty mismatch")
|
|
assert.Equal(t, cliBookCSS.Dirty, false, "cliBookCSS Dirty mismatch")
|
|
assert.Equal(t, cliBookCSS2.Dirty, false, "cliBookCSS2 Dirty mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1CSS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding apiNote1JS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1CSS.UUID).First(&apiNote1CSS), "finding apiNote1CSS")
|
|
var apiBookJS, apiBookCSS, apiBookCSS2 database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding apiBookJS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["cssBookUUID"]).First(&apiBookCSS), "finding apiBookCSS")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookCSS2.UUID).First(&apiBookCSS2), "finding apiBookCSS2")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS USN mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS USN mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS USN mismatch")
|
|
assert.NotEqual(t, apiBookCSS2.USN, 0, "apiBookCSS2 USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1JS.BookUUID, apiBookCSS2.UUID, "apiNote1JS BookUUID mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.BookUUID, apiBookCSS2.UUID, "apiNote1CSS BookUUID mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
assert.Equal(t, apiBookCSS2.Label, "css_2", "apiBookCSS2 Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS.Deleted, false, "apiNote1CSS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS2.Deleted, false, "apiBookCSS2 Deleted mismatch")
|
|
}
|
|
|
|
testSyncCmd(t, false, setup, assert)
|
|
testSyncCmd(t, true, setup, assert)
|
|
})
|
|
}
|
|
|
|
func TestFullSync(t *testing.T) {
|
|
t.Run("consecutively with stepSync", func(t *testing.T) {
|
|
setup := func(t *testing.T, ctx context.DnoteCtx, user database.User) map[string]string {
|
|
jsBookUUID := apiCreateBook(t, user, "js", "adding a js book")
|
|
jsNote1UUID := apiCreateNote(t, user, jsBookUUID, "js1", "adding js note 1")
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
|
|
return map[string]string{
|
|
"jsBookUUID": jsBookUUID,
|
|
"jsNote1UUID": jsNote1UUID,
|
|
}
|
|
}
|
|
|
|
assert := func(t *testing.T, ctx context.DnoteCtx, user database.User, ids map[string]string) {
|
|
cliDB := ctx.DB
|
|
apiDB := serverDb
|
|
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// test client
|
|
var cliNote1JS, cliNote1CSS cliDatabase.Note
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body, &cliNote1JS.USN)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", cliDB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.Body, &cliNote1CSS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", cliDB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
|
|
// test usn
|
|
assert.NotEqual(t, cliNote1JS.USN, 0, "cliNote1JS USN mismatch")
|
|
assert.NotEqual(t, cliNote1CSS.USN, 0, "cliNote1CSS USN mismatch")
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "cliBookJS USN mismatch")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "cliBookCSS USN mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "cliNote1JS Body mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "cliNote1CSS Body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "cliBookJS Label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "cliBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, cliNote1JS.Deleted, false, "cliNote1JS Deleted mismatch")
|
|
assert.Equal(t, cliNote1CSS.Deleted, false, "cliNote1CSS Deleted mismatch")
|
|
assert.Equal(t, cliBookJS.Deleted, false, "cliBookJS Deleted mismatch")
|
|
assert.Equal(t, cliBookCSS.Deleted, false, "cliBookCSS Deleted mismatch")
|
|
|
|
// test server
|
|
var apiNote1JS, apiNote1CSS database.Note
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsNote1UUID"]).First(&apiNote1JS), "finding api js note 1")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliNote1CSS.UUID).First(&apiNote1CSS), "finding api css note 1")
|
|
var apiBookJS, apiBookCSS database.Book
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, ids["jsBookUUID"]).First(&apiBookJS), "finding api js book")
|
|
apitest.MustExec(t, apiDB.Where("user_id = ? AND uuid = ?", user.ID, cliBookCSS.UUID).First(&apiBookCSS), "finding api css book")
|
|
|
|
// assert on usn
|
|
assert.NotEqual(t, apiNote1JS.USN, 0, "apiNote1JS usn mismatch")
|
|
assert.NotEqual(t, apiNote1CSS.USN, 0, "apiNote1CSS usn mismatch")
|
|
assert.NotEqual(t, apiBookJS.USN, 0, "apiBookJS usn mismatch")
|
|
assert.NotEqual(t, apiBookCSS.USN, 0, "apiBookCSS usn mismatch")
|
|
// assert on bodys and labels
|
|
assert.Equal(t, apiNote1JS.Body, "js1", "apiNote1JS Body mismatch")
|
|
assert.Equal(t, apiNote1CSS.Body, "css1", "apiNote1CSS Body mismatch")
|
|
assert.Equal(t, apiBookJS.Label, "js", "apiBookJS Label mismatch")
|
|
assert.Equal(t, apiBookCSS.Label, "css", "apiBookCSS Label mismatch")
|
|
// assert on deleted
|
|
assert.Equal(t, apiNote1JS.Deleted, false, "apiNote1JS Deleted mismatch")
|
|
assert.Equal(t, apiNote1CSS.Deleted, false, "apiNote1CSS Deleted mismatch")
|
|
assert.Equal(t, apiBookJS.Deleted, false, "apiBookJS Deleted mismatch")
|
|
assert.Equal(t, apiBookCSS.Deleted, false, "apiBookCSS Deleted mismatch")
|
|
}
|
|
|
|
t.Run("stepSync then fullSync", func(t *testing.T) {
|
|
// clean up
|
|
os.RemoveAll(tmpDirPath)
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
ids := setup(t, ctx, user)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
assert(t, ctx, user, ids)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "-f")
|
|
assert(t, ctx, user, ids)
|
|
})
|
|
|
|
t.Run("fullSync then stepSync", func(t *testing.T) {
|
|
// clean up
|
|
os.RemoveAll(tmpDirPath)
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
ids := setup(t, ctx, user)
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "-f")
|
|
assert(t, ctx, user, ids)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
assert(t, ctx, user, ids)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestSync_EmptyServer(t *testing.T) {
|
|
t.Run("sync to empty server after syncing to non-empty server", func(t *testing.T) {
|
|
// Test server data loss/wipe scenario (disaster recovery):
|
|
// Verify empty server detection works when the server loses all its data
|
|
|
|
// clean up
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 1: Create local data and sync to server
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// Verify sync succeeded
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 2: Clear all server data to simulate switching to a completely new empty server
|
|
apitest.ClearData(serverDb)
|
|
// Recreate user and session (simulating a new server)
|
|
user = setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 3: Sync again - should detect empty server and prompt user
|
|
// User confirms with "y"
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.UserConfirmEmptyServerSync, cliBinaryName, "sync")
|
|
|
|
// Step 4: Verify data was uploaded to the empty server
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Verify the content is correct on both client and server
|
|
var cliNote1JS, cliNote1CSS cliDatabase.Note
|
|
var cliBookJS, cliBookCSS cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding cliNote1JS", ctx.DB.QueryRow("SELECT uuid, body FROM notes WHERE body = ?", "js1"), &cliNote1JS.UUID, &cliNote1JS.Body)
|
|
cliDatabase.MustScan(t, "finding cliNote1CSS", ctx.DB.QueryRow("SELECT uuid, body FROM notes WHERE body = ?", "css1"), &cliNote1CSS.UUID, &cliNote1CSS.Body)
|
|
cliDatabase.MustScan(t, "finding cliBookJS", ctx.DB.QueryRow("SELECT uuid, label FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label)
|
|
cliDatabase.MustScan(t, "finding cliBookCSS", ctx.DB.QueryRow("SELECT uuid, label FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label)
|
|
|
|
assert.Equal(t, cliNote1JS.Body, "js1", "js note body mismatch")
|
|
assert.Equal(t, cliNote1CSS.Body, "css1", "css note body mismatch")
|
|
assert.Equal(t, cliBookJS.Label, "js", "js book label mismatch")
|
|
assert.Equal(t, cliBookCSS.Label, "css", "css book label mismatch")
|
|
|
|
// Verify on server side
|
|
var serverNoteJS, serverNoteCSS database.Note
|
|
var serverBookJS, serverBookCSS database.Book
|
|
apitest.MustExec(t, serverDb.Where("body = ?", "js1").First(&serverNoteJS), "finding server note js1")
|
|
apitest.MustExec(t, serverDb.Where("body = ?", "css1").First(&serverNoteCSS), "finding server note css1")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "js").First(&serverBookJS), "finding server book js")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "css").First(&serverBookCSS), "finding server book css")
|
|
|
|
assert.Equal(t, serverNoteJS.Body, "js1", "server js note body mismatch")
|
|
assert.Equal(t, serverNoteCSS.Body, "css1", "server css note body mismatch")
|
|
assert.Equal(t, serverBookJS.Label, "js", "server js book label mismatch")
|
|
assert.Equal(t, serverBookCSS.Label, "css", "server css book label mismatch")
|
|
})
|
|
|
|
t.Run("user cancels empty server prompt", func(t *testing.T) {
|
|
// clean up
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 1: Create local data and sync to server
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// Verify initial sync succeeded
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 2: Clear all server data
|
|
apitest.ClearData(serverDb)
|
|
user = setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 3: Sync again but user cancels with "n"
|
|
output, err := clitest.WaitDnoteCmd(t, dnoteCmdOpts, clitest.UserCancelEmptyServerSync, cliBinaryName, "sync")
|
|
if err == nil {
|
|
t.Fatal("Expected sync to fail when user cancels, but it succeeded")
|
|
}
|
|
|
|
// Verify the prompt appeared
|
|
if !strings.Contains(output, clitest.PromptEmptyServer) {
|
|
t.Fatalf("Expected empty server warning in output, got: %s", output)
|
|
}
|
|
|
|
// Step 4: Verify local state unchanged (transaction rolled back)
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 0,
|
|
serverBookCount: 0,
|
|
serverUserMaxUSN: 0,
|
|
})
|
|
|
|
// Verify items still have original USN and dirty=false
|
|
var book cliDatabase.Book
|
|
var note cliDatabase.Note
|
|
cliDatabase.MustScan(t, "checking book state", ctx.DB.QueryRow("SELECT usn, dirty FROM books WHERE label = ?", "js"), &book.USN, &book.Dirty)
|
|
cliDatabase.MustScan(t, "checking note state", ctx.DB.QueryRow("SELECT usn, dirty FROM notes WHERE body = ?", "js1"), ¬e.USN, ¬e.Dirty)
|
|
|
|
assert.NotEqual(t, book.USN, 0, "book USN should not be reset")
|
|
assert.NotEqual(t, note.USN, 0, "note USN should not be reset")
|
|
assert.Equal(t, book.Dirty, false, "book should not be marked dirty")
|
|
assert.Equal(t, note.Dirty, false, "note should not be marked dirty")
|
|
})
|
|
|
|
t.Run("all local data is marked deleted - should not upload", func(t *testing.T) {
|
|
// Test edge case: Server MaxUSN=0, local MaxUSN>0, but all items are deleted=true
|
|
// Should NOT prompt because there's nothing to upload
|
|
|
|
// clean up
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 1: Create local data and sync to server
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// Verify initial sync succeeded
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 2: Delete all local notes and books (mark as deleted)
|
|
cliDatabase.MustExec(t, "marking all books deleted", ctx.DB, "UPDATE books SET deleted = 1")
|
|
cliDatabase.MustExec(t, "marking all notes deleted", ctx.DB, "UPDATE notes SET deleted = 1")
|
|
|
|
// Step 3: Clear server data to simulate switching to empty server
|
|
apitest.ClearData(serverDb)
|
|
user = setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 4: Sync - should NOT prompt because bookCount=0 and noteCount=0 (counting only deleted=0)
|
|
// This should complete without user interaction
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// Verify no data was uploaded (server still empty, but client still has deleted items)
|
|
// Check server is empty
|
|
var serverNoteCount, serverBookCount int64
|
|
apitest.MustExec(t, serverDb.Model(&database.Note{}).Count(&serverNoteCount), "counting server notes")
|
|
apitest.MustExec(t, serverDb.Model(&database.Book{}).Count(&serverBookCount), "counting server books")
|
|
assert.Equal(t, serverNoteCount, int64(0), "server should have no notes")
|
|
assert.Equal(t, serverBookCount, int64(0), "server should have no books")
|
|
|
|
// Check client still has the deleted items locally
|
|
var clientNoteCount, clientBookCount int
|
|
cliDatabase.MustScan(t, "counting client notes", ctx.DB.QueryRow("SELECT count(*) FROM notes WHERE deleted = 1"), &clientNoteCount)
|
|
cliDatabase.MustScan(t, "counting client books", ctx.DB.QueryRow("SELECT count(*) FROM books WHERE deleted = 1"), &clientBookCount)
|
|
assert.Equal(t, clientNoteCount, 2, "client should still have 2 deleted notes")
|
|
assert.Equal(t, clientBookCount, 2, "client should still have 2 deleted books")
|
|
|
|
// Verify lastMaxUSN was reset to 0
|
|
var lastMaxUSN int
|
|
cliDatabase.MustScan(t, "getting lastMaxUSN", ctx.DB.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastMaxUSN), &lastMaxUSN)
|
|
assert.Equal(t, lastMaxUSN, 0, "lastMaxUSN should be reset to 0")
|
|
})
|
|
|
|
t.Run("race condition - other client uploads first", func(t *testing.T) {
|
|
// This test exercises a race condition that can occur during sync:
|
|
// While Client A is waiting for user input, Client B uploads data to the server.
|
|
//
|
|
// The empty server scenario is the natural place to test this because
|
|
// an empty server detection triggers a prompt, at which point the test
|
|
// can make client B upload data. We trigger the race condition deterministically.
|
|
//
|
|
// Test flow:
|
|
// - Client A detects empty server and prompts user
|
|
// - While waiting for confirmation, Client B uploads the same data via API
|
|
// - Client A continues and handles the 409 conflict gracefully by:
|
|
// 1. Detecting the 409 error when trying to CREATE books that already exist
|
|
// 2. Running stepSync to pull the server's books (js, css)
|
|
// 3. mergeBook renames local conflicts (js→js_2, css→css_2)
|
|
// 4. Retrying sendChanges to upload the renamed books
|
|
// - Result: Both clients' data is preserved (4 books total)
|
|
|
|
// Clean up
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 1: Create local data and sync to establish lastMaxUSN > 0
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// Verify initial sync succeeded
|
|
checkState(t, ctx, user, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 2: Clear server to simulate switching to empty server
|
|
apitest.ClearData(serverDb)
|
|
user = setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Step 3: Trigger sync which will detect empty server and prompt user
|
|
// Inside the callback (before confirming), we simulate Client B uploading via API.
|
|
// We wait for the empty server prompt to ensure Client B uploads AFTER
|
|
// GetSyncState but BEFORE the sync decision, creating the race condition deterministically
|
|
raceCallback := func(stdout io.Reader, stdin io.WriteCloser) error {
|
|
// First, wait for the prompt to ensure Client A has obtained the sync state from the server.
|
|
clitest.MustWaitForPrompt(t, stdout, clitest.PromptEmptyServer)
|
|
|
|
// Now Client B uploads the same data via API (after Client A got the sync state from the server
|
|
// but before its sync decision)
|
|
// This creates the race condition: Client A thinks server is empty, but Client B uploads data
|
|
jsBookUUID := apiCreateBook(t, user, "js", "client B creating js book")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "client B creating css book")
|
|
apiCreateNote(t, user, jsBookUUID, "js1", "client B creating js note")
|
|
apiCreateNote(t, user, cssBookUUID, "css1", "client B creating css note")
|
|
|
|
// Now user confirms
|
|
if _, err := io.WriteString(stdin, "y\n"); err != nil {
|
|
return errors.Wrap(err, "confirming sync")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Step 4: Client A runs sync with race condition
|
|
// The 409 conflict is automatically handled:
|
|
// - When 409 is detected, isBehind flag is set
|
|
// - stepSync pulls Client B's data
|
|
// - mergeBook renames Client A's books to js_2, css_2
|
|
// - Renamed books are uploaded
|
|
// - Both clients' data is preserved.
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, raceCallback, cliBinaryName, "sync")
|
|
|
|
// Verify final state - both clients' data preserved
|
|
checkStateWithDB(t, ctx.DB, user, serverDb, systemState{
|
|
clientNoteCount: 4, // Both clients' notes
|
|
clientBookCount: 4, // js, css, js_2, css_2
|
|
clientLastMaxUSN: 8, // 4 from Client B + 4 from Client A's renamed books/notes
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 4,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// Verify server has both clients' books
|
|
var svrBookJS, svrBookCSS, svrBookJS2, svrBookCSS2 database.Book
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "js").First(&svrBookJS), "finding server book 'js'")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "css").First(&svrBookCSS), "finding server book 'css'")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "js_2").First(&svrBookJS2), "finding server book 'js_2'")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "css_2").First(&svrBookCSS2), "finding server book 'css_2'")
|
|
|
|
assert.Equal(t, svrBookJS.Label, "js", "server should have book 'js' (Client B)")
|
|
assert.Equal(t, svrBookCSS.Label, "css", "server should have book 'css' (Client B)")
|
|
assert.Equal(t, svrBookJS2.Label, "js_2", "server should have book 'js_2' (Client A renamed)")
|
|
assert.Equal(t, svrBookCSS2.Label, "css_2", "server should have book 'css_2' (Client A renamed)")
|
|
|
|
// Verify client has all books
|
|
var cliBookJS, cliBookCSS, cliBookJS2, cliBookCSS2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding client book 'js'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding client book 'css'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding client book 'js_2'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js_2"), &cliBookJS2.UUID, &cliBookJS2.Label, &cliBookJS2.USN)
|
|
cliDatabase.MustScan(t, "finding client book 'css_2'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css_2"), &cliBookCSS2.UUID, &cliBookCSS2.Label, &cliBookCSS2.USN)
|
|
|
|
// Verify client UUIDs match server
|
|
assert.Equal(t, cliBookJS.UUID, svrBookJS.UUID, "client 'js' UUID should match server")
|
|
assert.Equal(t, cliBookCSS.UUID, svrBookCSS.UUID, "client 'css' UUID should match server")
|
|
assert.Equal(t, cliBookJS2.UUID, svrBookJS2.UUID, "client 'js_2' UUID should match server")
|
|
assert.Equal(t, cliBookCSS2.UUID, svrBookCSS2.UUID, "client 'css_2' UUID should match server")
|
|
|
|
// Verify all items have non-zero USN (synced successfully)
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "client 'js' should have non-zero USN")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "client 'css' should have non-zero USN")
|
|
assert.NotEqual(t, cliBookJS2.USN, 0, "client 'js_2' should have non-zero USN")
|
|
assert.NotEqual(t, cliBookCSS2.USN, 0, "client 'css_2' should have non-zero USN")
|
|
})
|
|
|
|
t.Run("sync to server A, then B, then back to A, then back to B", func(t *testing.T) {
|
|
// Test switching between two actual servers to verify:
|
|
// 1. Empty server detection works when switching to empty server
|
|
// 2. No false detection when switching back to non-empty servers
|
|
// 3. Both servers maintain independent state across multiple switches
|
|
|
|
// Clean up
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
// Create Server A with its own database
|
|
dbPathA := fmt.Sprintf("%s/serverA.db", testDir)
|
|
defer os.Remove(dbPathA)
|
|
|
|
serverA, serverDbA, err := setupTestServer(dbPathA, serverTime)
|
|
if err != nil {
|
|
t.Fatal(errors.Wrap(err, "setting up server A"))
|
|
}
|
|
defer serverA.Close()
|
|
|
|
// Create Server B with its own database
|
|
dbPathB := fmt.Sprintf("%s/serverB.db", testDir)
|
|
defer os.Remove(dbPathB)
|
|
|
|
serverB, serverDbB, err := setupTestServer(dbPathB, serverTime)
|
|
if err != nil {
|
|
t.Fatal(errors.Wrap(err, "setting up server B"))
|
|
}
|
|
defer serverB.Close()
|
|
|
|
// Step 1: Set up user on Server A and sync
|
|
apiEndpointA := fmt.Sprintf("%s/api", serverA.URL)
|
|
|
|
userA := apitest.SetupUserData(serverDbA)
|
|
apitest.SetupAccountData(serverDbA, userA, "alice@example.com", "pass1234")
|
|
sessionA := apitest.SetupSession(serverDbA, userA)
|
|
cliDatabase.MustExec(t, "inserting session_key", ctx.DB, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKey, sessionA.Key)
|
|
cliDatabase.MustExec(t, "inserting session_key_expiry", ctx.DB, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKeyExpiry, sessionA.ExpiresAt.Unix())
|
|
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "--apiEndpoint", apiEndpointA)
|
|
|
|
// Verify sync to Server A succeeded
|
|
checkStateWithDB(t, ctx.DB, userA, serverDbA, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 2: Switch to Server B (empty) and sync
|
|
apiEndpointB := fmt.Sprintf("%s/api", serverB.URL)
|
|
|
|
// Set up user on Server B
|
|
userB := apitest.SetupUserData(serverDbB)
|
|
apitest.SetupAccountData(serverDbB, userB, "alice@example.com", "pass1234")
|
|
sessionB := apitest.SetupSession(serverDbB, userB)
|
|
cliDatabase.MustExec(t, "updating session_key for B", ctx.DB, "UPDATE system SET value = ? WHERE key = ?", sessionB.Key, consts.SystemSessionKey)
|
|
cliDatabase.MustExec(t, "updating session_key_expiry for B", ctx.DB, "UPDATE system SET value = ? WHERE key = ?", sessionB.ExpiresAt.Unix(), consts.SystemSessionKeyExpiry)
|
|
|
|
// Should detect empty server and prompt
|
|
clitest.MustWaitDnoteCmd(t, dnoteCmdOpts, clitest.UserConfirmEmptyServerSync, cliBinaryName, "sync", "--apiEndpoint", apiEndpointB)
|
|
|
|
// Verify Server B now has data
|
|
checkStateWithDB(t, ctx.DB, userB, serverDbB, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 3: Switch back to Server A and sync
|
|
cliDatabase.MustExec(t, "updating session_key back to A", ctx.DB, "UPDATE system SET value = ? WHERE key = ?", sessionA.Key, consts.SystemSessionKey)
|
|
cliDatabase.MustExec(t, "updating session_key_expiry back to A", ctx.DB, "UPDATE system SET value = ? WHERE key = ?", sessionA.ExpiresAt.Unix(), consts.SystemSessionKeyExpiry)
|
|
|
|
// Should NOT trigger empty server detection (Server A has MaxUSN > 0)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "--apiEndpoint", apiEndpointA)
|
|
|
|
// Verify Server A still has its data
|
|
checkStateWithDB(t, ctx.DB, userA, serverDbA, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Step 4: Switch back to Server B and sync again
|
|
cliDatabase.MustExec(t, "updating session_key back to B", ctx.DB, "UPDATE system SET value = ? WHERE key = ?", sessionB.Key, consts.SystemSessionKey)
|
|
cliDatabase.MustExec(t, "updating session_key_expiry back to B", ctx.DB, "UPDATE system SET value = ? WHERE key = ?", sessionB.ExpiresAt.Unix(), consts.SystemSessionKeyExpiry)
|
|
|
|
// Should NOT trigger empty server detection (Server B now has MaxUSN > 0 from Step 2)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync", "--apiEndpoint", apiEndpointB)
|
|
|
|
// Verify both servers maintain independent state
|
|
checkStateWithDB(t, ctx.DB, userB, serverDbB, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestSync_FreshClientConcurrent(t *testing.T) {
|
|
// Test the core issue: Fresh client (never synced, lastMaxUSN=0) syncing to a server
|
|
// that already has data uploaded by another client.
|
|
//
|
|
// Scenario:
|
|
// 1. Client A creates local notes (never synced, lastMaxUSN=0, lastSyncAt=0)
|
|
// 2. Client B uploads same book names to server first
|
|
// 3. Client A syncs
|
|
//
|
|
// Expected: Client A should pull server data first, detect duplicate book names,
|
|
// rename local books to avoid conflicts (js→js_2), then upload successfully.
|
|
|
|
// Clean up
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
user := setupUserAndLogin(t, ctx.DB)
|
|
|
|
// Client A: Create local data (never sync)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "js", "-c", "js1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "add", "css", "-c", "css1")
|
|
|
|
// Client B: Upload same book names to server via API
|
|
jsBookUUID := apiCreateBook(t, user, "js", "client B creating js book")
|
|
cssBookUUID := apiCreateBook(t, user, "css", "client B creating css book")
|
|
apiCreateNote(t, user, jsBookUUID, "js2", "client B note")
|
|
apiCreateNote(t, user, cssBookUUID, "css2", "client B note")
|
|
|
|
// Client A syncs - should handle the conflict gracefully
|
|
// Expected: pulls server data, renames local books to js_2/css_2, uploads successfully
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "sync")
|
|
|
|
// Verify: Should have 4 books and 4 notes on both client and server
|
|
// USN breakdown: 2 books + 2 notes from Client B (USN 1-4), then 2 books + 2 notes from Client A (USN 5-8)
|
|
checkStateWithDB(t, ctx.DB, user, serverDb, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 4,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 4,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// Verify server has all 4 books with correct names
|
|
var svrBookJS, svrBookCSS, svrBookJS2, svrBookCSS2 database.Book
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "js").First(&svrBookJS), "finding server book 'js'")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "css").First(&svrBookCSS), "finding server book 'css'")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "js_2").First(&svrBookJS2), "finding server book 'js_2'")
|
|
apitest.MustExec(t, serverDb.Where("label = ?", "css_2").First(&svrBookCSS2), "finding server book 'css_2'")
|
|
|
|
assert.Equal(t, svrBookJS.Label, "js", "server should have book 'js' (Client B)")
|
|
assert.Equal(t, svrBookCSS.Label, "css", "server should have book 'css' (Client B)")
|
|
assert.Equal(t, svrBookJS2.Label, "js_2", "server should have book 'js_2' (Client A renamed)")
|
|
assert.Equal(t, svrBookCSS2.Label, "css_2", "server should have book 'css_2' (Client A renamed)")
|
|
|
|
// Verify server has all 4 notes with correct content
|
|
var svrNoteJS1, svrNoteJS2, svrNoteCSS1, svrNoteCSS2 database.Note
|
|
apitest.MustExec(t, serverDb.Where("body = ?", "js1").First(&svrNoteJS1), "finding server note 'js1'")
|
|
apitest.MustExec(t, serverDb.Where("body = ?", "js2").First(&svrNoteJS2), "finding server note 'js2'")
|
|
apitest.MustExec(t, serverDb.Where("body = ?", "css1").First(&svrNoteCSS1), "finding server note 'css1'")
|
|
apitest.MustExec(t, serverDb.Where("body = ?", "css2").First(&svrNoteCSS2), "finding server note 'css2'")
|
|
|
|
assert.Equal(t, svrNoteJS1.BookUUID, svrBookJS2.UUID, "note 'js1' should belong to book 'js_2' (Client A)")
|
|
assert.Equal(t, svrNoteJS2.BookUUID, svrBookJS.UUID, "note 'js2' should belong to book 'js' (Client B)")
|
|
assert.Equal(t, svrNoteCSS1.BookUUID, svrBookCSS2.UUID, "note 'css1' should belong to book 'css_2' (Client A)")
|
|
assert.Equal(t, svrNoteCSS2.BookUUID, svrBookCSS.UUID, "note 'css2' should belong to book 'css' (Client B)")
|
|
|
|
// Verify client has all 4 books
|
|
var cliBookJS, cliBookCSS, cliBookJS2, cliBookCSS2 cliDatabase.Book
|
|
cliDatabase.MustScan(t, "finding client book 'js'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js"), &cliBookJS.UUID, &cliBookJS.Label, &cliBookJS.USN)
|
|
cliDatabase.MustScan(t, "finding client book 'css'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css"), &cliBookCSS.UUID, &cliBookCSS.Label, &cliBookCSS.USN)
|
|
cliDatabase.MustScan(t, "finding client book 'js_2'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "js_2"), &cliBookJS2.UUID, &cliBookJS2.Label, &cliBookJS2.USN)
|
|
cliDatabase.MustScan(t, "finding client book 'css_2'", ctx.DB.QueryRow("SELECT uuid, label, usn FROM books WHERE label = ?", "css_2"), &cliBookCSS2.UUID, &cliBookCSS2.Label, &cliBookCSS2.USN)
|
|
|
|
// Verify client UUIDs match server
|
|
assert.Equal(t, cliBookJS.UUID, svrBookJS.UUID, "client 'js' UUID should match server")
|
|
assert.Equal(t, cliBookCSS.UUID, svrBookCSS.UUID, "client 'css' UUID should match server")
|
|
assert.Equal(t, cliBookJS2.UUID, svrBookJS2.UUID, "client 'js_2' UUID should match server")
|
|
assert.Equal(t, cliBookCSS2.UUID, svrBookCSS2.UUID, "client 'css_2' UUID should match server")
|
|
|
|
// Verify all books have non-zero USN (synced successfully)
|
|
assert.NotEqual(t, cliBookJS.USN, 0, "client 'js' should have non-zero USN")
|
|
assert.NotEqual(t, cliBookCSS.USN, 0, "client 'css' should have non-zero USN")
|
|
assert.NotEqual(t, cliBookJS2.USN, 0, "client 'js_2' should have non-zero USN")
|
|
assert.NotEqual(t, cliBookCSS2.USN, 0, "client 'css_2' should have non-zero USN")
|
|
|
|
// Verify client has all 4 notes
|
|
var cliNoteJS1, cliNoteJS2, cliNoteCSS1, cliNoteCSS2 cliDatabase.Note
|
|
cliDatabase.MustScan(t, "finding client note 'js1'", ctx.DB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js1"), &cliNoteJS1.UUID, &cliNoteJS1.Body, &cliNoteJS1.USN)
|
|
cliDatabase.MustScan(t, "finding client note 'js2'", ctx.DB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "js2"), &cliNoteJS2.UUID, &cliNoteJS2.Body, &cliNoteJS2.USN)
|
|
cliDatabase.MustScan(t, "finding client note 'css1'", ctx.DB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css1"), &cliNoteCSS1.UUID, &cliNoteCSS1.Body, &cliNoteCSS1.USN)
|
|
cliDatabase.MustScan(t, "finding client note 'css2'", ctx.DB.QueryRow("SELECT uuid, body, usn FROM notes WHERE body = ?", "css2"), &cliNoteCSS2.UUID, &cliNoteCSS2.Body, &cliNoteCSS2.USN)
|
|
|
|
// Verify client note UUIDs match server
|
|
assert.Equal(t, cliNoteJS1.UUID, svrNoteJS1.UUID, "client note 'js1' UUID should match server")
|
|
assert.Equal(t, cliNoteJS2.UUID, svrNoteJS2.UUID, "client note 'js2' UUID should match server")
|
|
assert.Equal(t, cliNoteCSS1.UUID, svrNoteCSS1.UUID, "client note 'css1' UUID should match server")
|
|
assert.Equal(t, cliNoteCSS2.UUID, svrNoteCSS2.UUID, "client note 'css2' UUID should match server")
|
|
|
|
// Verify all notes have non-zero USN (synced successfully)
|
|
assert.NotEqual(t, cliNoteJS1.USN, 0, "client note 'js1' should have non-zero USN")
|
|
assert.NotEqual(t, cliNoteJS2.USN, 0, "client note 'js2' should have non-zero USN")
|
|
assert.NotEqual(t, cliNoteCSS1.USN, 0, "client note 'css1' should have non-zero USN")
|
|
assert.NotEqual(t, cliNoteCSS2.USN, 0, "client note 'css2' should have non-zero USN")
|
|
}
|
|
|
|
// TestSync_ConvergeSameBookNames tests that two clients don't enter an infinite sync loop if they
|
|
// try to sync books with the same names. Books shouldn't get marked dirty when re-downloaded from server.
|
|
func TestSync_ConvergeSameBookNames(t *testing.T) {
|
|
// Clean up and prepare server
|
|
apitest.ClearData(serverDb)
|
|
defer apitest.ClearData(serverDb)
|
|
|
|
clearTmp(t)
|
|
|
|
ctx := context.InitTestCtx(t, paths, nil)
|
|
defer context.TeardownTestCtx(t, ctx)
|
|
|
|
// Setup two separate client databases
|
|
client1DB := fmt.Sprintf("%s/client1.db", tmpDirPath)
|
|
client2DB := fmt.Sprintf("%s/client2.db", tmpDirPath)
|
|
defer os.Remove(client1DB)
|
|
defer os.Remove(client2DB)
|
|
|
|
// Set up sessions
|
|
user := setupUser(t, ctx.DB)
|
|
db1 := testutils.MustOpenDatabase(t, client1DB)
|
|
db2 := testutils.MustOpenDatabase(t, client2DB)
|
|
defer db1.Close()
|
|
defer db2.Close()
|
|
|
|
// Client 1: First sync to empty server
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client1DB, "add", "testbook", "-c", "client1 note1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client1DB, "add", "anotherbook", "-c", "client1 note2")
|
|
login(t, db1, user)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client1DB, "sync")
|
|
checkStateWithDB(t, db1, user, serverDb, systemState{
|
|
clientNoteCount: 2,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 4,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 2,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 4,
|
|
})
|
|
|
|
// Client 2: Sync (downloads client 1's data, adds own notes) =====
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client2DB, "add", "testbook", "-c", "client2 note1")
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client2DB, "add", "anotherbook", "-c", "client2 note2")
|
|
login(t, db2, user)
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client2DB, "sync")
|
|
// Verify state after client2 sync
|
|
checkStateWithDB(t, db2, user, serverDb, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// Client 1: Sync again. It downloads client2's changes (2 extra notes).
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client1DB, "sync")
|
|
|
|
// Verify MaxUSN did not increase (client1 should only download, not upload)
|
|
// Client1 still has: 2 original books + 4 notes (2 own + 2 from client2)
|
|
checkStateWithDB(t, db1, user, serverDb, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// Verify no infinite loop: alternate syncing
|
|
// Both clients should be able to sync without any changes (MaxUSN stays at 8)
|
|
for range 3 {
|
|
// Client 2 syncs
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client2DB, "sync")
|
|
|
|
// Verify client2 state unchanged
|
|
checkStateWithDB(t, db2, user, serverDb, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
|
|
// Client 1 syncs
|
|
clitest.RunDnoteCmd(t, dnoteCmdOpts, cliBinaryName, "--dbPath", client1DB, "sync")
|
|
|
|
// Verify client1 state unchanged
|
|
checkStateWithDB(t, db1, user, serverDb, systemState{
|
|
clientNoteCount: 4,
|
|
clientBookCount: 2,
|
|
clientLastMaxUSN: 8,
|
|
clientLastSyncAt: serverTime.Unix(),
|
|
serverNoteCount: 4,
|
|
serverBookCount: 2,
|
|
serverUserMaxUSN: 8,
|
|
})
|
|
}
|
|
}
|